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 }