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 @SuppressWarnings("initialization") 325 CodeCacheStress.BenchLoader loader1 = new CodeCacheStress.BenchLoader(); 326 327 @Setup(Level.Trial) 328 public void setupClasses() throws Exception { 329 Object[] receivers1; 330 331 compiledClasses = new byte[numberOfClasses][]; 332 loadedClasses = new Class[numberOfClasses]; 333 classNames = new String[numberOfClasses]; 334 335 argumentMaps.add(new HashMap<String, Integer>()); 336 argumentMaps.add(new LinkedHashMap<String, Integer>()); 337 argumentMaps.add(new WeakHashMap<String, Integer>()); 338 339 argumentMaps.get(0).put(k, v); 340 argumentMaps.get(1).put(k, v); 341 argumentMaps.get(2).put(k, v); 342 343 for (int i = 0; i < numberOfClasses; i++) { 344 classNames[i] = "B" + i; 345 compiledClasses[i] = InMemoryJavaCompiler.compile(classNames[i], B(i)); 346 } 347 348 for (index = 0; index < compiledClasses.length; index++) { 349 Class<?> c = loader1.findClass(classNames[index]); 350 loadedClasses[index] = c; 351 352 Constructor<?>[] ca = c.getConstructors(); 353 assert ca.length == 1; 354 355 receivers1 = new Object[instanceCount]; 356 for (int j = 0; j < instanceCount; j++) { 357 receivers1[j] = ca[0].newInstance(); 358 } 359 instancesOfClassMap.put(c, receivers1); 360 361 Method[] methods = new Method[methodNames.length]; 362 IntStream.range(0, methodNames.length).forEach(m -> { 363 try { 364 methods[m] = c.getMethod(methodNames[m], java.util.Map.class, String.class, Integer.class); 365 } catch (Exception e) { 366 System.out.println("Exception = " + e); 367 e.printStackTrace(); 368 System.exit(-1); 369 } 370 }); 371 372 classToMethodsMap.put((receivers1[0]).getClass(), methods); 373 374 // Warmup the methods to get compiled 375 IntStream.range(0, methodNames.length).parallel().forEach(m -> { 376 IntStream.range(0, 12000).forEach(x -> { 377 try { 378 Object r = ((Object[]) instancesOfClassMap.get(c))[0]; 379 Method[] mi = classToMethodsMap.get(r.getClass()); 380 mi[m].invoke(r, argumentMaps.get(0), k, 5); 381 } catch (Exception e) { 382 System.out.println("Exception = " + e); 383 e.printStackTrace(); 384 System.exit(-1); 385 } 386 }); 387 388 }); 389 } 390 391 System.gc(); 392 } 393 394 Class chooseClass() { 395 ThreadLocalRandom tlr = ThreadLocalRandom.current(); 396 int whichClass = tlr.nextInt(rangeOfClasses); 397 return loadedClasses[whichClass]; 398 } 399 400 Object chooseInstance(Class c) { 401 ThreadLocalRandom tlr = ThreadLocalRandom.current(); 402 int whichInst = tlr.nextInt(instanceCount); 403 return ((Object[]) instancesOfClassMap.get(c))[whichInst]; 404 } 405 406 Method chooseMethod(Class c) { 407 ThreadLocalRandom tlr = ThreadLocalRandom.current(); 408 int whichM = tlr.nextInt(methodNames.length); 409 return classToMethodsMap.get(c)[whichM]; 410 } 411 412 Map chooseMap() { 413 ThreadLocalRandom tlr = ThreadLocalRandom.current(); 414 int whichMap = tlr.nextInt(argumentMaps.size()); 415 return argumentMaps.get(whichMap); 416 } 417 418 Integer callTheMethod(Method m, Object r, String k, Map map) throws Exception { 419 return (Integer) m.invoke(r, map, k, recurse); 420 } 421 422 @Benchmark 423 public Integer work() throws Exception { 424 int sum = 0; 425 426 // Call a method of a random instance of a random class up to the specified range 427 for (int index = 0; index < compiledClasses.length; index++) { 428 try { 429 Class c = chooseClass(); 430 Object r = chooseInstance(c); 431 Method m = chooseMethod(c); 432 assert m != null; 433 Map map = chooseMap(); 434 Integer result = callTheMethod(m, r, k, map); 435 assert result != null && result >= v; 436 sum += result; 437 } catch (Exception e) { 438 System.out.println("Exception = " + e); 439 e.printStackTrace(); 440 } 441 } 442 return sum; 443 } 444 445 }