1 /*
2 * Copyright (c) 2023, 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 java.lang.reflect.Constructor;
26 import java.lang.reflect.Method;
27 import java.util.*;
28 import java.util.concurrent.ThreadLocalRandom;
29
30 import java.util.concurrent.TimeUnit;
31 import java.util.stream.Collectors;
32 import java.util.stream.IntStream;
33
34 import org.openjdk.jmh.annotations.Benchmark;
35 import org.openjdk.jmh.annotations.BenchmarkMode;
36 import org.openjdk.jmh.annotations.CompilerControl;
37 import org.openjdk.jmh.annotations.Fork;
38 import org.openjdk.jmh.annotations.Level;
39 import org.openjdk.jmh.annotations.Measurement;
40 import org.openjdk.jmh.annotations.Mode;
41 import org.openjdk.jmh.annotations.OutputTimeUnit;
42 import org.openjdk.jmh.annotations.Param;
43 import org.openjdk.jmh.annotations.Scope;
44 import org.openjdk.jmh.annotations.Setup;
45 import org.openjdk.jmh.annotations.State;
46 import org.openjdk.jmh.annotations.Threads;
47 import org.openjdk.jmh.annotations.Warmup;
48 import org.openjdk.jmh.infra.Blackhole;
49
50 import org.openjdk.bench.util.InMemoryJavaCompiler;
51
52 @State(Scope.Benchmark)
53 @Warmup(iterations = 20, time = 2)
54 @Measurement(iterations = 15, time = 2)
55 @BenchmarkMode(Mode.AverageTime)
56 @OutputTimeUnit(TimeUnit.MICROSECONDS)
57 @Threads(1)
58 @Fork(value = 2)
59 public class CodeCacheStress {
60
61 // The number of distinct classes generated from the source string below
62 // All the classes are "warmed up" by invoking their methods to get compiled by the jit
63 @Param({"100"})
64 public int numberOfClasses;
65
66 // The range of these classes to use in the measured phase after the warm up
67 @Param({"100"})
68 public int rangeOfClasses;
69
70 // How deep is the recursion when calling into the generated classes
71 @Param({"20"})
72 public int recurse;
73
74 // How many instances of each generated class to create and call in the measurement phase
75 @Param({"100"})
76 public int instanceCount;
77
78 byte[][] compiledClasses;
79 Class[] loadedClasses;
80 String[] classNames;
81
82 int index = 0;
83 Map<Object, Method[]> classToMethodsMap = new HashMap<>();
84 ArrayList<Map<String, Integer>> argumentMaps = new ArrayList<>();
85 Map<Class, Object[]> instancesOfClassMap = new HashMap<>();
86
87 static final String k = "key";
88 static final Integer v = 1000;
89
90 static final String methodNames[] = {
91 "get"
92 };
93
94 static String B(int count) {
95 return "import java.util.*; "
96 + " "
97 + "public class B" + count + " {"
98 + " "
99 + " static int staticA = 0;"
100 + " static int staticB = 0;"
101 + " static int staticC = 0;"
102 + " static int staticD = 0;"
103 + " "
104 + " static synchronized void setA(int a) {"
105 + " staticB = a;"
106 + " }"
107 + " "
108 + " static synchronized void setB(int b) {"
109 + " staticB = b;"
110 + " }"
111 + " "
112 + " static synchronized void setC(int c) {"
113 + " staticC = c;"
114 + " }"
115 + " "
116 + " static synchronized void setD(int d) {"
117 + " staticD = d;"
118 + " }"
119 + " "
120 + " int instA = 0;"
121 + " "
122 + " int padAA = 0;"
123 + " int padAB = 0;"
124 + " int padAC = 0;"
125 + " int padAD = 0;"
126 + " int padAE = 0;"
127 + " int padAF = 0;"
128 + " int padAG = 0;"
129 + " int padAH = 0;"
130 + " int padAI = 0;"
131 + " int padAJ = 0;"
132 + " int padAK = 0;"
133 + " int padAL = 0;"
134 + " int padAM = 0;"
135 + " int padAN = 0;"
136 + " int padAO = 0;"
137 + " int padAP = 0;"
138 + " int padAQ = 0;"
139 + " int padAR = 0;"
140 + " int padAS = 0;"
141 + " int padAT = 0;"
142 + " "
143 + " int instB = 0;"
144 + " "
145 + " int padBA = 0;"
146 + " int padBB = 0;"
147 + " int padBC = 0;"
148 + " int padBD = 0;"
149 + " int padBE = 0;"
150 + " int padBF = 0;"
151 + " int padBG = 0;"
152 + " int padBH = 0;"
153 + " int padBI = 0;"
154 + " int padBJ = 0;"
155 + " int padBK = 0;"
156 + " int padBL = 0;"
157 + " int padBM = 0;"
158 + " int padBN = 0;"
159 + " int padBO = 0;"
160 + " int padBP = 0;"
161 + " int padBQ = 0;"
162 + " int padBR = 0;"
163 + " int padBS = 0;"
164 + " int padBT = 0;"
165 + " "
166 + " int instC = 0;"
167 + " "
168 + " int padCA = 0;"
169 + " int padCB = 0;"
170 + " int padCC = 0;"
171 + " int padCD = 0;"
172 + " int padCE = 0;"
173 + " int padCF = 0;"
174 + " int padCG = 0;"
175 + " int padCH = 0;"
176 + " int padCI = 0;"
177 + " int padCJ = 0;"
178 + " int padCK = 0;"
179 + " int padCL = 0;"
180 + " int padCM = 0;"
181 + " int padCN = 0;"
182 + " int padCO = 0;"
183 + " int padCP = 0;"
184 + " int padCQ = 0;"
185 + " int padCR = 0;"
186 + " int padCS = 0;"
187 + " int padCT = 0;"
188 + " "
189 + " int instD = 0;"
190 + " "
191 + " "
192 + " public Integer get(Map m, String k, Integer depth) {"
193 + " if (depth > 0) {"
194 + " instA += ((depth % 2) + staticA);"
195 + " return (Integer) m.get(k) + get2(m, k, --depth);"
196 + " } else {"
197 + " setA(depth);"
198 + " return (Integer) m.get(k)+ 10;"
199 + " }"
200 + " }"
201 + " "
202 + " public Integer get2(Map m, String k, Integer depth) {"
203 + " if (depth > 0) {"
204 + " instB += ((depth % 2) + staticB);"
205 + " return (Integer) m.get(k) + get3(m, k, --depth);"
206 + " } else {"
207 + " setB(depth);"
208 + " return (Integer) m.get(k)+ 20;"
209 + " }"
210 + " }"
211 + " "
212 + " public Integer get3(Map m, String k, Integer depth) {"
213 + " if (depth > 0) {"
214 + " instC += ((depth % 2) + staticC);"
215 + " return (Integer) m.get(k) + get4(m, k, --depth);"
216 + " } else {"
217 + " setC(depth);"
218 + " return (Integer) m.get(k)+ 30;"
219 + " }"
220 + " }"
221 + " "
222 + " "
223 + " public Integer get4(Map m, String k, Integer depth) {"
224 + " if (depth > 0) {"
225 + " instD += ((depth % 2) + staticD);"
226 + " return (Integer) m.get(k) + get5(m, k, --depth);"
227 + " } else {"
228 + " setD(depth);"
229 + " return (Integer) m.get(k)+ 40;"
230 + " }"
231 + " }"
232 + " "
233 + " "
234 + " public Integer get5(Map m, String k, Integer depth) {"
235 + " if (depth > 0) {"
236 + " return (Integer) m.get(k) + get6(m, k, --depth);"
237 + " } else {"
238 + " return (Integer) m.get(k)+ instA;"
239 + " }"
240 + " }"
241 + " "
242 + " public Integer get6(Map m, String k, Integer depth) {"
243 + " if (depth > 0) {"
244 + " return (Integer) m.get(k) + get7(m, k, --depth);"
245 + " } else {"
246 + " return (Integer) m.get(k)+ instB;"
247 + " }"
248 + " }"
249 + " "
250 + " public Integer get7(Map m, String k, Integer depth) {"
251 + " if (depth > 0) {"
252 + " return (Integer) m.get(k) + get8(m, k, --depth);"
253 + " } else {"
254 + " return (Integer) m.get(k)+ instC;"
255 + " }"
256 + " }"
257 + " "
258 + " public Integer get8(Map m, String k, Integer depth) {"
259 + " if (depth > 0) {"
260 + " return (Integer) m.get(k) + get9(m, k, --depth);"
261 + " } else {"
262 + " return (Integer) m.get(k)+ instD;"
263 + " }"
264 + " }"
265 + " "
266 + " public Integer get9(Map m, String k, Integer depth) {"
267 + " if (depth > 0) {"
268 + " return (Integer) m.get(k) + get10(m, k, --depth);"
269 + " } else {"
270 + " return (Integer) m.get(k)+ instA;"
271 + " }"
272 + " }"
273 + " "
274 + " public Integer get10(Map m, String k, Integer depth) {"
275 + " if (depth > 0) {"
276 + " return (Integer) m.get(k) + get11(m, k, --depth);"
277 + " } else {"
278 + " return (Integer) m.get(k)+ instB;"
279 + " }"
280 + " }"
281 + " "
282 + " public Integer get11(Map m, String k, Integer depth) {"
283 + " if (depth > 0) {"
284 + " return (Integer) m.get(k) + get12(m, k, --depth);"
285 + " } else {"
286 + " return (Integer) m.get(k)+ instC;"
287 + " }"
288 + " }"
289 + " "
290 + " public Integer get12(Map m, String k, Integer depth) {"
291 + " if (depth > 0) {"
292 + " return (Integer) m.get(k) + get(m, k, --depth);"
293 + " } else {"
294 + " return (Integer) m.get(k)+ instD;"
295 + " }"
296 + " }"
297 + "}";
298 }
299
300
301 class BenchLoader extends ClassLoader {
302
303 BenchLoader() {
304 super();
305 }
306
307 BenchLoader(ClassLoader parent) {
308 super(parent);
309 }
310
311 @Override
312 protected Class<?> findClass(String name) throws ClassNotFoundException {
313 if (name.equals(classNames[index])) {
314 assert compiledClasses[index] != null;
315 return defineClass(name, compiledClasses[index],
316 0,
317 (compiledClasses[index]).length);
318 } else {
319 return super.findClass(name);
320 }
321 }
322 }
323
324 CodeCacheStress.BenchLoader loader1 = new CodeCacheStress.BenchLoader();
325
326 @Setup(Level.Trial)
327 public void setupClasses() throws Exception {
328 Object[] receivers1;
329
330 compiledClasses = new byte[numberOfClasses][];
331 loadedClasses = new Class[numberOfClasses];
332 classNames = new String[numberOfClasses];
333
334 argumentMaps.add(new HashMap<String, Integer>());
335 argumentMaps.add(new LinkedHashMap<String, Integer>());
336 argumentMaps.add(new WeakHashMap<String, Integer>());
337
338 argumentMaps.get(0).put(k, v);
339 argumentMaps.get(1).put(k, v);
340 argumentMaps.get(2).put(k, v);
341
342 for (int i = 0; i < numberOfClasses; i++) {
343 classNames[i] = "B" + i;
344 compiledClasses[i] = InMemoryJavaCompiler.compile(classNames[i], B(i));
345 }
346
347 for (index = 0; index < compiledClasses.length; index++) {
348 Class<?> c = loader1.findClass(classNames[index]);
349 loadedClasses[index] = c;
350
351 Constructor<?>[] ca = c.getConstructors();
352 assert ca.length == 1;
353
354 receivers1 = new Object[instanceCount];
355 for (int j = 0; j < instanceCount; j++) {
356 receivers1[j] = ca[0].newInstance();
357 }
358 instancesOfClassMap.put(c, receivers1);
359
360 Method[] methods = new Method[methodNames.length];
361 IntStream.range(0, methodNames.length).forEach(m -> {
362 try {
363 methods[m] = c.getMethod(methodNames[m], java.util.Map.class, String.class, Integer.class);
364 } catch (Exception e) {
365 System.out.println("Exception = " + e);
366 e.printStackTrace();
367 System.exit(-1);
368 }
369 });
370
371 classToMethodsMap.put((receivers1[0]).getClass(), methods);
372
373 // Warmup the methods to get compiled
374 IntStream.range(0, methodNames.length).parallel().forEach(m -> {
375 IntStream.range(0, 12000).forEach(x -> {
376 try {
377 Object r = ((Object[]) instancesOfClassMap.get(c))[0];
378 Method[] mi = classToMethodsMap.get(r.getClass());
379 mi[m].invoke(r, argumentMaps.get(0), k, 5);
380 } catch (Exception e) {
381 System.out.println("Exception = " + e);
382 e.printStackTrace();
383 System.exit(-1);
384 }
385 });
386
387 });
388 }
389
390 System.gc();
391 }
392
393 Class chooseClass() {
394 ThreadLocalRandom tlr = ThreadLocalRandom.current();
395 int whichClass = tlr.nextInt(rangeOfClasses);
396 return loadedClasses[whichClass];
397 }
398
399 Object chooseInstance(Class c) {
400 ThreadLocalRandom tlr = ThreadLocalRandom.current();
401 int whichInst = tlr.nextInt(instanceCount);
402 return ((Object[]) instancesOfClassMap.get(c))[whichInst];
403 }
404
405 Method chooseMethod(Class c) {
406 ThreadLocalRandom tlr = ThreadLocalRandom.current();
407 int whichM = tlr.nextInt(methodNames.length);
408 return classToMethodsMap.get(c)[whichM];
409 }
410
411 Map chooseMap() {
412 ThreadLocalRandom tlr = ThreadLocalRandom.current();
413 int whichMap = tlr.nextInt(argumentMaps.size());
414 return argumentMaps.get(whichMap);
415 }
416
417 Integer callTheMethod(Method m, Object r, String k, Map map) throws Exception {
418 return (Integer) m.invoke(r, map, k, recurse);
419 }
420
421 @Benchmark
422 public Integer work() throws Exception {
423 int sum = 0;
424
425 // Call a method of a random instance of a random class up to the specified range
426 for (int index = 0; index < compiledClasses.length; index++) {
427 try {
428 Class c = chooseClass();
429 Object r = chooseInstance(c);
430 Method m = chooseMethod(c);
431 assert m != null;
432 Map map = chooseMap();
433 Integer result = callTheMethod(m, r, k, map);
434 assert result != null && result >= v;
435 sum += result;
436 } catch (Exception e) {
437 System.out.println("Exception = " + e);
438 e.printStackTrace();
439 }
440 }
441 return sum;
442 }
443
444 }
--- EOF ---