1 /* 2 * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.bench.vm.compiler; 24 25 import org.openjdk.jmh.annotations.Benchmark; 26 import org.openjdk.jmh.annotations.BenchmarkMode; 27 import org.openjdk.jmh.annotations.CompilerControl; 28 import org.openjdk.jmh.annotations.Fork; 29 import org.openjdk.jmh.annotations.Level; 30 import org.openjdk.jmh.annotations.Measurement; 31 import org.openjdk.jmh.annotations.Mode; 32 import org.openjdk.jmh.annotations.OutputTimeUnit; 33 import org.openjdk.jmh.annotations.Scope; 34 import org.openjdk.jmh.annotations.Setup; 35 import org.openjdk.jmh.annotations.State; 36 import org.openjdk.jmh.annotations.TearDown; 37 import org.openjdk.jmh.annotations.Warmup; 38 39 import java.util.Random; 40 import java.util.concurrent.TimeUnit; 41 42 @BenchmarkMode(Mode.AverageTime) 43 @OutputTimeUnit(TimeUnit.NANOSECONDS) 44 @State(Scope.Thread) 45 @Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) 46 @Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) 47 @Fork(value = 3) 48 public abstract class WriteBarrier { 49 50 // For array references 51 public static final int NUM_REFERENCES_SMALL = 32; 52 public static final int NUM_REFERENCES_LARGE = 2048; 53 54 // For array update tests 55 private Object[] theArraySmall; 56 private int[] indicesSmall; 57 58 private Object[] theArrayLarge; 59 private int[] indicesLarge; 60 61 private Object[] youngArraySmall; 62 private Object[] youngArrayLarge; 63 64 private Object nullRef; 65 private Object realRef; 66 private Object youngRef; 67 68 // For field update tests 69 public Referencer head = null; 70 public Referencer tail = null; 71 public Referencer youngHead = null; 72 public Referencer youngTail = null; 73 74 // For random number generation 75 private int m_w; 76 private int m_z; 77 78 // For field references 79 public class Referencer { 80 Referencer next = null; 81 Referencer() { 82 this.next = null; 83 } 84 void append(Referencer r) { 85 this.next = r; 86 } 87 void clear() { 88 this.next = null; 89 } 90 } 91 92 @Setup(Level.Trial) 93 public void setup() { 94 theArraySmall = new Object[NUM_REFERENCES_SMALL]; 95 indicesSmall = new int[NUM_REFERENCES_SMALL]; 96 97 theArrayLarge = new Object[NUM_REFERENCES_LARGE]; 98 indicesLarge = new int[NUM_REFERENCES_LARGE]; 99 100 m_w = (int) System.currentTimeMillis(); 101 Random random = new Random(); 102 m_z = random.nextInt(10000) + 1; 103 104 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 105 indicesSmall[i] = get_random() % (NUM_REFERENCES_SMALL - 1); 106 } 107 108 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 109 indicesLarge[i] = get_random() % (NUM_REFERENCES_LARGE - 1); 110 } 111 112 realRef = new Object(); 113 114 // Build a small linked structure 115 this.head = new Referencer(); 116 this.tail = new Referencer(); 117 this.head.append(this.tail); 118 119 // This will (hopefully) promote objects to old space 120 // Run with -XX:+DisableExplicitGC to keep 121 // objects in young space 122 System.gc(); 123 } 124 125 @Setup(Level.Iteration) 126 public void setupIteration() { 127 // Reallocate target objects each iteration to ensure they are in young gen. 128 youngArraySmall = new Object[NUM_REFERENCES_SMALL]; 129 youngArrayLarge = new Object[NUM_REFERENCES_LARGE]; 130 youngRef = new Object(); 131 this.youngHead = new Referencer(); 132 this.youngTail = new Referencer(); 133 } 134 135 private int get_random() { 136 m_z = 36969 * (m_z & 65535) + (m_z >> 16); 137 m_w = 18000 * (m_w & 65535) + (m_w >> 16); 138 return Math.abs((m_z << 16) + m_w); /* 32-bit result */ 139 } 140 141 // This and the other testArrayWriteBarrierFast benchmarks below should not 142 // be inlined into the JMH-generated harness method. If the methods were 143 // inlined, we might spill in the main loop (on x64) depending on very 144 // subtle conditions (such as whether LinuxPerfAsmProfiler is enabled!), 145 // which could distort the results. 146 @Benchmark 147 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 148 public void testArrayWriteBarrierFastPathRealSmall() { 149 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 150 theArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = realRef; 151 } 152 } 153 154 @Benchmark 155 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 156 public void testArrayWriteBarrierFastPathOldToYoungSmall() { 157 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 158 youngArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = realRef; 159 } 160 } 161 162 @Benchmark 163 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 164 public void testArrayWriteBarrierFastPathYoungToOldSmall() { 165 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 166 theArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = youngRef; 167 } 168 } 169 170 @Benchmark 171 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 172 public void testArrayWriteBarrierFastPathYoungToYoungSmall() { 173 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 174 youngArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = youngRef; 175 } 176 } 177 178 @Benchmark 179 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 180 public void testArrayWriteBarrierFastPathNullYoungSmall() { 181 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 182 youngArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = nullRef; 183 } 184 } 185 186 @Benchmark 187 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 188 public void testArrayWriteBarrierFastPathOldToYoungLarge() { 189 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 190 youngArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = realRef; 191 } 192 } 193 194 @Benchmark 195 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 196 public void testArrayWriteBarrierFastPathYoungToYoungLarge() { 197 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 198 youngArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = youngRef; 199 } 200 } 201 202 @Benchmark 203 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 204 public void testArrayWriteBarrierFastPathNullYoungLarge() { 205 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 206 youngArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = nullRef; 207 } 208 } 209 210 @Benchmark 211 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 212 public void testArrayWriteBarrierFastPathYoungToOldLarge() { 213 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 214 theArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = youngRef; 215 } 216 } 217 218 @Benchmark 219 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 220 public void testArrayWriteBarrierFastPathNullSmall() { 221 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 222 theArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = nullRef; 223 } 224 } 225 226 @Benchmark 227 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 228 public void testArrayWriteBarrierFastPathRealLarge() { 229 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 230 theArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = realRef; 231 } 232 } 233 234 @Benchmark 235 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 236 public void testArrayWriteBarrierFastPathNullLarge() { 237 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 238 theArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = nullRef; 239 } 240 } 241 242 @Benchmark() 243 public void testFieldWriteBarrierFastPath() { 244 // Shuffle everything around 245 this.tail.append(this.head); 246 this.head.clear(); 247 this.head.append(this.tail); 248 this.tail.clear(); 249 } 250 251 @Benchmark() 252 public void testFieldWriteBarrierFastPathYoungRef() { 253 // Shuffle everything around 254 this.tail.append(this.youngHead); 255 this.head.clear(); 256 this.head.append(this.youngTail); 257 this.tail.clear(); 258 } 259 260 // This run is useful to compare different GC barrier models without being 261 // affected by C2 unrolling the main loop differently for each model. 262 @Fork(value = 3, jvmArgs = {"-XX:LoopUnrollLimit=1"}) 263 public static class WithoutUnrolling extends WriteBarrier {} 264 265 // This run is useful to study the interaction of GC barriers and loop 266 // unrolling. Check that the main loop in the testArray benchmarks is 267 // unrolled (or not) as expected for the studied GC barrier model. 268 @Fork(value = 3) 269 public static class WithDefaultUnrolling extends WriteBarrier {} 270 }