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 }
--- EOF ---