Skip to content

Instantly share code, notes, and snippets.

@franz1981
Created February 13, 2023 21:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save franz1981/4234195ca0168f3833023b610d91ddff to your computer and use it in GitHub Desktop.
Save franz1981/4234195ca0168f3833023b610d91ddff to your computer and use it in GitHub Desktop.
package red.hat.puzzles.loom;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Warmup(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Fork(2)
public class VirtualThreadReflectionBenchmark {
private static final class Dummy {
private static Thread currentCarrierThread() {
return Thread.currentThread();
}
}
private static final Method CURRENT_CARRIER_METHOD = currentCarrierThreadMethod();
private static final Method DUMMY_CURRENT_CARRIER_METHOD = dummyCurrentCarrierThreadMethod();
private static final MethodHandle CURRENT_CARRIER_MH = currentCarrierThreadMH();
private static Method dummyCurrentCarrierThreadMethod() {
try {
Method currentCarrierThread = Dummy.class.getDeclaredMethod("currentCarrierThread");
currentCarrierThread.setAccessible(true);
Thread dummy = (Thread) currentCarrierThread.invoke(null);
return currentCarrierThread;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
private static Method currentCarrierThreadMethod() {
try {
Method currentCarrierThread = Thread.class.getDeclaredMethod("currentCarrierThread");
currentCarrierThread.setAccessible(true);
// this is to check if --enable-preview for Java 19 is correctly set
Thread dummy = (Thread) currentCarrierThread.invoke(null);
return currentCarrierThread;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
private static MethodHandle currentCarrierThreadMH() {
try {
MethodHandle mh = MethodHandles.lookup().unreflect(currentCarrierThreadMethod());
Thread t = (Thread) mh.invokeExact();
return mh;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
private static Thread currentCarrierThread() {
try {
return (Thread) CURRENT_CARRIER_METHOD.invoke(null);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static Thread dummyCurrentCarrierThread() {
try {
return (Thread) DUMMY_CURRENT_CARRIER_METHOD.invoke(null);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static Thread currentCarrierThreadInvokeExact() {
try {
// NOT USING primitive on purpose :"( Netty 4.1 is Java 1.6 sigh!
return (Thread) CURRENT_CARRIER_MH.invokeExact();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Benchmark
public Thread reflectiveCurrentCarrierThread() {
return currentCarrierThread();
}
@Benchmark
public Thread mhCurrentCarrierThread() {
return currentCarrierThreadInvokeExact();
}
@Benchmark
public Thread reflectiveDummyCurrentCarrierThread() {
return dummyCurrentCarrierThread();
}
}
@franz1981
Copy link
Author

Env:

# JMH version: 1.34
# VM version: JDK 19, OpenJDK 64-Bit Server VM, 19+36
# VM invoker: /home/forked_franz/.jdks/azul-19/bin/java
# VM options: --enable-preview --add-opens java.base/java.lang=ALL-UNNAMED
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)

Results:

Benchmark                                                                                 Mode  Cnt    Score    Error   Units
VirtualThreadReflectionBenchmark.mhCurrentCarrierThread                                   avgt   10    0.978 ±  0.023   ns/op
VirtualThreadReflectionBenchmark.mhCurrentCarrierThread:·async                            avgt           NaN              ---
VirtualThreadReflectionBenchmark.mhCurrentCarrierThread:·gc.alloc.rate                    avgt   10    0.001 ±  0.001  MB/sec
VirtualThreadReflectionBenchmark.mhCurrentCarrierThread:·gc.alloc.rate.norm               avgt   10   ≈ 10⁻⁵             B/op
VirtualThreadReflectionBenchmark.mhCurrentCarrierThread:·gc.count                         avgt   10      ≈ 0           counts
VirtualThreadReflectionBenchmark.reflectiveCurrentCarrierThread                           avgt   10  202.506 ± 11.494   ns/op
VirtualThreadReflectionBenchmark.reflectiveCurrentCarrierThread:·async                    avgt           NaN              ---
VirtualThreadReflectionBenchmark.reflectiveCurrentCarrierThread:·gc.alloc.rate            avgt   10   12.579 ±  0.700  MB/sec
VirtualThreadReflectionBenchmark.reflectiveCurrentCarrierThread:·gc.alloc.rate.norm       avgt   10   16.001 ±  0.001    B/op
VirtualThreadReflectionBenchmark.reflectiveCurrentCarrierThread:·gc.count                 avgt   10      ≈ 0           counts
VirtualThreadReflectionBenchmark.reflectiveDummyCurrentCarrierThread                      avgt   10    0.781 ±  0.012   ns/op
VirtualThreadReflectionBenchmark.reflectiveDummyCurrentCarrierThread:·async               avgt           NaN              ---
VirtualThreadReflectionBenchmark.reflectiveDummyCurrentCarrierThread:·gc.alloc.rate       avgt   10    0.001 ±  0.001  MB/sec
VirtualThreadReflectionBenchmark.reflectiveDummyCurrentCarrierThread:·gc.alloc.rate.norm  avgt   10   ≈ 10⁻⁵             B/op
VirtualThreadReflectionBenchmark.reflectiveDummyCurrentCarrierThread:·gc.count            avgt   10      ≈ 0           counts

Profiling reveal that reflectiveCurrentCarrierThread is performing some additional ops:
image

while the MethodHandle version:
image

and finally, the dummy version:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment