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 }