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 @SuppressWarnings("initialization") 82 Referencer() { 83 this.next = null; 84 } 85 void append(Referencer r) { 86 this.next = r; 87 } 88 void clear() { 89 this.next = null; 90 } 91 } 92 93 @Setup(Level.Trial) 94 public void setup() { 95 theArraySmall = new Object[NUM_REFERENCES_SMALL]; 96 indicesSmall = new int[NUM_REFERENCES_SMALL]; 97 98 theArrayLarge = new Object[NUM_REFERENCES_LARGE]; 99 indicesLarge = new int[NUM_REFERENCES_LARGE]; 100 101 m_w = (int) System.currentTimeMillis(); 102 Random random = new Random(); 103 m_z = random.nextInt(10000) + 1; 104 105 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 106 indicesSmall[i] = get_random() % (NUM_REFERENCES_SMALL - 1); 107 } 108 109 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 110 indicesLarge[i] = get_random() % (NUM_REFERENCES_LARGE - 1); 111 } 112 113 realRef = new Object(); 114 115 // Build a small linked structure 116 this.head = new Referencer(); 117 this.tail = new Referencer(); 118 this.head.append(this.tail); 119 120 // This will (hopefully) promote objects to old space 121 // Run with -XX:+DisableExplicitGC to keep 122 // objects in young space 123 System.gc(); 124 } 125 126 @Setup(Level.Iteration) 127 public void setupIteration() { 128 // Reallocate target objects each iteration to ensure they are in young gen. 129 youngArraySmall = new Object[NUM_REFERENCES_SMALL]; 130 youngArrayLarge = new Object[NUM_REFERENCES_LARGE]; 131 youngRef = new Object(); 132 this.youngHead = new Referencer(); 133 this.youngTail = new Referencer(); 134 } 135 136 private int get_random() { 137 m_z = 36969 * (m_z & 65535) + (m_z >> 16); 138 m_w = 18000 * (m_w & 65535) + (m_w >> 16); 139 return Math.abs((m_z << 16) + m_w); /* 32-bit result */ 140 } 141 142 // This and the other testArrayWriteBarrierFast benchmarks below should not 143 // be inlined into the JMH-generated harness method. If the methods were 144 // inlined, we might spill in the main loop (on x64) depending on very 145 // subtle conditions (such as whether LinuxPerfAsmProfiler is enabled!), 146 // which could distort the results. 147 @Benchmark 148 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 149 public void testArrayWriteBarrierFastPathRealSmall() { 150 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 151 theArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = realRef; 152 } 153 } 154 155 @Benchmark 156 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 157 public void testArrayWriteBarrierFastPathOldToYoungSmall() { 158 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 159 youngArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = realRef; 160 } 161 } 162 163 @Benchmark 164 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 165 public void testArrayWriteBarrierFastPathYoungToOldSmall() { 166 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 167 theArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = youngRef; 168 } 169 } 170 171 @Benchmark 172 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 173 public void testArrayWriteBarrierFastPathYoungToYoungSmall() { 174 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 175 youngArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = youngRef; 176 } 177 } 178 179 @Benchmark 180 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 181 public void testArrayWriteBarrierFastPathNullYoungSmall() { 182 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 183 youngArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = nullRef; 184 } 185 } 186 187 @Benchmark 188 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 189 public void testArrayWriteBarrierFastPathOldToYoungLarge() { 190 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 191 youngArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = realRef; 192 } 193 } 194 195 @Benchmark 196 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 197 public void testArrayWriteBarrierFastPathYoungToYoungLarge() { 198 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 199 youngArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = youngRef; 200 } 201 } 202 203 @Benchmark 204 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 205 public void testArrayWriteBarrierFastPathNullYoungLarge() { 206 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 207 youngArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = nullRef; 208 } 209 } 210 211 @Benchmark 212 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 213 public void testArrayWriteBarrierFastPathYoungToOldLarge() { 214 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 215 theArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = youngRef; 216 } 217 } 218 219 @Benchmark 220 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 221 public void testArrayWriteBarrierFastPathNullSmall() { 222 for (int i = 0; i < NUM_REFERENCES_SMALL; i++) { 223 theArraySmall[indicesSmall[NUM_REFERENCES_SMALL - i - 1]] = nullRef; 224 } 225 } 226 227 @Benchmark 228 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 229 public void testArrayWriteBarrierFastPathRealLarge() { 230 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 231 theArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = realRef; 232 } 233 } 234 235 @Benchmark 236 @CompilerControl(CompilerControl.Mode.DONT_INLINE) 237 public void testArrayWriteBarrierFastPathNullLarge() { 238 for (int i = 0; i < NUM_REFERENCES_LARGE; i++) { 239 theArrayLarge[indicesLarge[NUM_REFERENCES_LARGE - i - 1]] = nullRef; 240 } 241 } 242 243 @Benchmark() 244 public void testFieldWriteBarrierFastPath() { 245 // Shuffle everything around 246 this.tail.append(this.head); 247 this.head.clear(); 248 this.head.append(this.tail); 249 this.tail.clear(); 250 } 251 252 @Benchmark() 253 public void testFieldWriteBarrierFastPathYoungRef() { 254 // Shuffle everything around 255 this.tail.append(this.youngHead); 256 this.head.clear(); 257 this.head.append(this.youngTail); 258 this.tail.clear(); 259 } 260 261 // This run is useful to compare different GC barrier models without being 262 // affected by C2 unrolling the main loop differently for each model. 263 @Fork(value = 3, jvmArgs = {"-XX:LoopUnrollLimit=1"}) 264 public static class WithoutUnrolling extends WriteBarrier {} 265 266 // This run is useful to study the interaction of GC barriers and loop 267 // unrolling. Check that the main loop in the testArray benchmarks is 268 // unrolled (or not) as expected for the studied GC barrier model. 269 @Fork(value = 3) 270 public static class WithDefaultUnrolling extends WriteBarrier {} 271 }