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 }