Skip to content

Commit 35c0053

Browse files
author
Eric Caspole
committed
8345405: Add JMH showing the regression in 8341649
Reviewed-by: redestad, coleenp
1 parent 166c127 commit 35c0053

File tree

1 file changed

+175
-0
lines changed

1 file changed

+175
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright (c) 2024, 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.runtime;
24+
25+
import java.lang.invoke.*;
26+
import java.lang.reflect.Constructor;
27+
import java.util.*;
28+
import java.util.concurrent.ConcurrentHashMap;
29+
import java.util.concurrent.ThreadLocalRandom;
30+
31+
import java.util.concurrent.TimeUnit;
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+
49+
import org.openjdk.bench.util.InMemoryJavaCompiler;
50+
51+
@State(Scope.Benchmark)
52+
@Warmup(iterations = 18, time = 5)
53+
@Measurement(iterations = 10, time = 5)
54+
@BenchmarkMode(Mode.Throughput)
55+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
56+
@Threads(1)
57+
@Fork(value = 2)
58+
public class MethodHandleStress {
59+
60+
// The number of distinct classes generated from the source string below
61+
// All the classes are "warmed up" by invoking their methods to get compiled by the jit
62+
@Param({"1000"})
63+
public int classes;
64+
65+
// How many instances of each generated class to create and use in the measurement phase
66+
@Param({"100"})
67+
public int instances;
68+
69+
@Benchmark
70+
public Integer executeOne() throws Throwable {
71+
Class c = chooseClass();
72+
Object r = chooseInstance(c);
73+
MethodHandle m = prebindMethods.get(c).get(r);
74+
assert m != null;
75+
return callTheMethod(m, r);
76+
}
77+
78+
private Map<Class, Object[]> instancesOfClassMap = new HashMap<>();
79+
private Map<Class, Map<Object, MethodHandle>> prebindMethods = new ConcurrentHashMap<>();
80+
81+
private Class[] loadedClasses;
82+
83+
private class BenchLoader extends ClassLoader {
84+
85+
private static String classString(String name) {
86+
return "public class " + name + " {"
87+
+ " int instA = 0;"
88+
+ " int getA() {"
89+
+ " return instA;"
90+
+ " }"
91+
+ " public Integer get(Integer depth) throws Throwable {"
92+
+ " return getA();"
93+
+ " }"
94+
+ "}";
95+
}
96+
97+
private Class<?> generateClass(String name) {
98+
byte[] classBytes = InMemoryJavaCompiler.compile(name, classString(name));
99+
return defineClass(name, classBytes, 0, classBytes.length);
100+
}
101+
}
102+
103+
@Setup(Level.Trial)
104+
public void setupClasses() throws Exception {
105+
MethodHandleStress.BenchLoader loader = new MethodHandleStress.BenchLoader();
106+
107+
Object[] receivers1;
108+
109+
loadedClasses = new Class[classes];
110+
111+
MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
112+
MethodType generatedGetType = MethodType.methodType(Integer.class, Integer.class);
113+
114+
for (int i = 0; i < classes; i++) {
115+
Class<?> c = loader.generateClass("B" + i);
116+
loadedClasses[i] = c;
117+
118+
Constructor<?>[] ca = c.getConstructors();
119+
assert ca.length == 1;
120+
121+
// Build the list of prebind MHs
122+
ConcurrentHashMap<Object, MethodHandle> prebinds = new ConcurrentHashMap<>();
123+
124+
receivers1 = new Object[instances];
125+
for (int j = 0; j < instances; j++) {
126+
Object inst= ca[0].newInstance();
127+
receivers1[j] = inst;
128+
MethodHandle mh = publicLookup.findVirtual(c, "get", generatedGetType);
129+
mh = mh.bindTo(inst);
130+
prebinds.put(inst, mh);
131+
}
132+
instancesOfClassMap.put(c, receivers1);
133+
prebindMethods.put(c, prebinds);
134+
}
135+
136+
// Warm up the methods
137+
for (int n = 0; n < classes; n++) {
138+
try {
139+
IntStream.range(0, 5000).parallel().forEach(x -> {
140+
try {
141+
executeOne();
142+
} catch (Throwable e) {
143+
}
144+
});
145+
} catch (Throwable e) {
146+
System.out.println("Exception = " + e);
147+
e.printStackTrace();
148+
System.exit(-1);
149+
}
150+
}
151+
152+
System.gc();
153+
}
154+
155+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
156+
Class chooseClass() {
157+
ThreadLocalRandom tlr = ThreadLocalRandom.current();
158+
int whichClass = tlr.nextInt(classes);
159+
return loadedClasses[whichClass];
160+
}
161+
162+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
163+
Object chooseInstance(Class c) {
164+
ThreadLocalRandom tlr = ThreadLocalRandom.current();
165+
int whichInst = tlr.nextInt(instances);
166+
return ((Object[]) instancesOfClassMap.get(c))[whichInst];
167+
}
168+
169+
static final Integer recurse = 1;
170+
171+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
172+
int callTheMethod(MethodHandle m, Object r) throws Throwable {
173+
return (Integer) m.invokeExact(recurse);
174+
}
175+
}

0 commit comments

Comments
 (0)