|
22 | 22 | */ |
23 | 23 | package org.openjdk.bench.vm.lang; |
24 | 24 |
|
25 | | -import org.openjdk.jmh.annotations.*; |
26 | | -import java.util.concurrent.TimeUnit; |
| 25 | +import java.io.FileOutputStream; |
| 26 | +import java.io.IOException; |
| 27 | +import java.lang.classfile.*; |
| 28 | +import java.lang.classfile.constantpool.ClassEntry; |
| 29 | +import java.lang.constant.ClassDesc; |
| 30 | +import java.lang.constant.ConstantDescs; |
| 31 | +import java.lang.constant.MethodTypeDesc; |
| 32 | +import java.lang.invoke.MethodHandles; |
| 33 | +import java.security.SecureClassLoader; |
| 34 | +import java.util.Arrays; |
| 35 | +import java.util.List; |
27 | 36 | import java.util.Random; |
| 37 | +import java.util.concurrent.TimeUnit; |
| 38 | +import java.util.function.Function; |
| 39 | +import java.util.stream.Stream; |
| 40 | +import org.openjdk.jmh.annotations.*; |
| 41 | + |
| 42 | +import static java.lang.classfile.ClassFile.*; |
| 43 | +import static java.lang.invoke.MethodType.methodType; |
28 | 44 |
|
29 | 45 |
|
30 | 46 | @BenchmarkMode(Mode.AverageTime) |
|
34 | 50 | @Measurement(iterations = 3, time = 1) |
35 | 51 | @Fork(value = 5) |
36 | 52 | public class SecondarySupersLookup { |
| 53 | + @Param("100") |
| 54 | + private int interfaceCount; |
| 55 | + |
37 | 56 | interface J {} |
38 | 57 | interface I01 {} |
39 | 58 | interface I02 extends I01 {} |
@@ -145,6 +164,117 @@ static Class<?> getSuper(int idx) { |
145 | 164 | throw new InternalError("" + i); |
146 | 165 | } |
147 | 166 |
|
| 167 | + private static final ClassDesc CD_Hello = ClassDesc.of("Hello"); |
| 168 | + private static final MethodTypeDesc MTD_void_StringArray = MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String.arrayType()); |
| 169 | + private static final MethodTypeDesc MTD_void_String = MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String); |
| 170 | + private static final MethodTypeDesc MTD_Class_boolean = MethodTypeDesc.of(ConstantDescs.CD_boolean, ConstantDescs.CD_Class); |
| 171 | + private static final MethodTypeDesc MTD_Object_boolean = MethodTypeDesc.of(ConstantDescs.CD_boolean, ConstantDescs.CD_Object); |
| 172 | + private static final ClassDesc CD_System = ClassDesc.of("java.lang.System"); |
| 173 | + private static final ClassDesc CD_PrintStream = ClassDesc.of("java.io.PrintStream"); |
| 174 | + |
| 175 | + static class MyLoader extends SecureClassLoader { |
| 176 | + public Class defineClass(String name, byte[] bytes) { |
| 177 | + try { |
| 178 | + try (FileOutputStream fos = new FileOutputStream("/tmp/" + name + ".class")) { |
| 179 | + fos.write(bytes); |
| 180 | + } catch (IOException e) { |
| 181 | + throw new RuntimeException(e); |
| 182 | + } |
| 183 | + } catch (Exception e) { |
| 184 | + throw new RuntimeException(e); |
| 185 | + } |
| 186 | + return defineClass(name, bytes, 0, bytes.length); |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + static MyLoader LOADER = new MyLoader(); |
| 191 | + |
| 192 | + static String interfaceName(Integer suffix) { |
| 193 | + return String.format("I%04d", suffix); |
| 194 | + } |
| 195 | + |
| 196 | + private static Class<?>[] genInterfaces(Function<Integer, String> name, int first, int last) { |
| 197 | + Class<?>[] interfaces = new Class[last + 1]; |
| 198 | + for (int n = first; n <= last; n++) { |
| 199 | + byte[] bytes = ClassFile.of().build(ClassDesc.of(name.apply(n)), |
| 200 | + (ClassBuilder clb) -> clb.withFlags(ACC_INTERFACE | ACC_PUBLIC | ACC_ABSTRACT)); |
| 201 | + interfaces[n] = LOADER.defineClass(interfaceName(n), bytes); |
| 202 | + } |
| 203 | + System.out.println("LENGTH = " + interfaces.length); |
| 204 | + return interfaces; |
| 205 | + } |
| 206 | + |
| 207 | + Class<?>[] interfaces; |
| 208 | + |
| 209 | + public Class<?>[] genClasses(Class<?>[] interfaces, int length) { |
| 210 | + Class<?>[] impls = new Class[length]; |
| 211 | + for (int n = 0; n < length; n++) { |
| 212 | + var klass = genClass(List.of(interfaces).stream() |
| 213 | + .map(iface -> ClassDesc.of(iface.getName())).toList(), "Impl" + n); |
| 214 | + impls[n] = klass; |
| 215 | + } |
| 216 | + return impls; |
| 217 | + } |
| 218 | + |
| 219 | + private Class genClass(Class<?>[] interfaces, String name) { |
| 220 | + return genClass(List.of(interfaces).stream() |
| 221 | + .map(iface -> ClassDesc.of(iface.getName())).toList(), |
| 222 | + name); |
| 223 | + } |
| 224 | + |
| 225 | + private Class<?> genClass(List<ClassDesc> interfaceDesc, String name) { |
| 226 | + byte[] bytes = ClassFile.of().build(ClassDesc.of(name), |
| 227 | + clb -> { |
| 228 | + clb = clb.withFlags(ACC_PUBLIC); |
| 229 | + var cpool = clb.constantPool(); |
| 230 | + Stream<ClassEntry> interfaceEntryStream = interfaceDesc.stream() |
| 231 | + .map(cpool::classEntry); |
| 232 | + var interfaceEntries = interfaceEntryStream.toList(); |
| 233 | + clb |
| 234 | + .withInterfaces(interfaceEntries) |
| 235 | + .withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, |
| 236 | + ClassFile.ACC_PUBLIC, |
| 237 | + mb -> mb.withCode( |
| 238 | + cob -> cob.aload(0) |
| 239 | + .invokespecial(ConstantDescs.CD_Object, |
| 240 | + ConstantDescs.INIT_NAME, ConstantDescs.MTD_void) |
| 241 | + .return_())) |
| 242 | + .withMethod("isInstance", MTD_Class_boolean, |
| 243 | + ACC_PUBLIC, |
| 244 | + mb -> mb.withCode( |
| 245 | + cob -> cob |
| 246 | + .aload(1) |
| 247 | + .aload(0) |
| 248 | + .invokevirtual(ConstantDescs.CD_Class, "isInstance", |
| 249 | + MethodTypeDesc.of(ConstantDescs.CD_boolean, |
| 250 | + ConstantDescs.CD_Object)) |
| 251 | + .ireturn())) |
| 252 | + .withMethod("is" + interfaceEntries.get(Math.min(27, |
| 253 | + interfaceEntries.size() / 2)).name(), |
| 254 | + MethodTypeDesc.of(ConstantDescs.CD_boolean), |
| 255 | + ACC_PUBLIC, |
| 256 | + mb -> mb.withCode( |
| 257 | + cob -> cob |
| 258 | + .aload(0) |
| 259 | + .instanceOf(interfaceEntries.get |
| 260 | + (Math.min(27, |
| 261 | + interfaceEntries.size() / 2))) |
| 262 | + .ireturn() |
| 263 | + )); |
| 264 | + }); |
| 265 | + return LOADER.defineClass(name, bytes); |
| 266 | + } |
| 267 | + |
| 268 | + Object testInstance; |
| 269 | + |
| 270 | + @Setup |
| 271 | + public void init() throws Exception { |
| 272 | + interfaces = genInterfaces(SecondarySupersLookup::interfaceName, 0, interfaceCount); |
| 273 | + Class<?> barf = genClass(interfaces, "Humongous"); |
| 274 | + testInstance = barf.getConstructor().newInstance(); |
| 275 | + System.out.println("COUNT = " + interfaceCount); |
| 276 | + } |
| 277 | + |
148 | 278 | @Setup |
149 | 279 | public void warmup() { |
150 | 280 | for (int i = 0; i < 20_000; i++) { |
@@ -307,4 +437,8 @@ public void testPositive01() { |
307 | 437 | @Benchmark public void testNegative64() { |
308 | 438 | test(obj64, J.class, false); |
309 | 439 | } |
| 440 | + |
| 441 | + @Benchmark public void testPositiveN() { |
| 442 | + test(testInstance, interfaces[interfaceCount >>> 1], true); |
| 443 | + } |
310 | 444 | } |
0 commit comments