Skip to content

Instantly share code, notes, and snippets.

@Groostav
Created September 11, 2023 16:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Groostav/1668990b982fc542f62014f6d776f655 to your computer and use it in GitHub Desktop.
Save Groostav/1668990b982fc542f62014f6d776f655 to your computer and use it in GitHub Desktop.
attempt 2 at benchmarking different switch types
package org.example;
public class JavaSwitchExpressions {
public static int intSwitchExpression(Node node) {
return switch(node.typeInt()){
case 0 -> ((Node.FirstNodeType) node).firstId();
case 1 -> ((Node.SecondNodeType) node).secondId();
case 2 -> ((Node.ThirdNodeType) node).thirdId();
case 3 -> ((Node.FourthNodeType) node).fourthId();
default -> throw new UnsupportedOperationException();
};
}
public static int intSwitchStatement(Node node) {
int result = -1;
switch(node.typeInt()){
case 0: result = ((Node.FirstNodeType) node).firstId(); break;
case 1: result = ((Node.SecondNodeType) node).secondId(); break;
case 2: result = ((Node.ThirdNodeType) node).thirdId(); break;
case 3: result = ((Node.FourthNodeType) node).fourthId(); break;
}
return result;
}
public static int typeIfInstanceofElseIf(Node node) {
int result = -1;
if(node instanceof Node.FirstNodeType first){
result = first.firstId();
}
else if (node instanceof Node.SecondNodeType second){
result = second.secondId();
}
else if (node instanceof Node.ThirdNodeType third){
result = third.thirdId();
}
else if (node instanceof Node.FourthNodeType fourth){
result = fourth.fourthId();
}
return result;
}
public static int typeSwitchExpression(Node node) {
return switch (node) {
case Node.FirstNodeType first -> first.firstId(); //warning: boxing
case Node.SecondNodeType second -> second.secondId();
case Node.ThirdNodeType third -> third.thirdId();
case Node.FourthNodeType fourth -> fourth.fourthId();
};
}
}
package org.example
fun typeSwitchExpression(node: KtNode): Int {
return when(node){
is KtNode.FirstNodeType -> node.firstId
is KtNode.SecondNodeType -> node.secondId
is KtNode.ThirdNodeType -> node.thirdId
is KtNode.FourthNodeType -> node.fourthId
}
}
fun intSwitchExpression(node: KtNode): Int {
return when(node.typeInt){
0 -> (node as KtNode.FirstNodeType).firstId
1 -> (node as KtNode.SecondNodeType).secondId
2 -> (node as KtNode.ThirdNodeType).thirdId
3 -> (node as KtNode.FourthNodeType).fourthId
else -> TODO()
}
}
fun ifIsElseIfExpression(node: KtNode): Int {
return if(node is KtNode.FirstNodeType){
node.firstId
}
else if(node is KtNode.SecondNodeType){
node.secondId
}
else if (node is KtNode.ThirdNodeType){
node.thirdId
}
else if (node is KtNode.FourthNodeType){
node.fourthId
}
else TODO()
}
package org.example
sealed interface KtNode {
val typeInt: Int
data class FirstNodeType(val firstId: Int): KtNode {
override val typeInt: Int get() = 0
}
data class SecondNodeType(val secondId: Int): KtNode {
override val typeInt: Int get() = 1
}
data class ThirdNodeType(val thirdId: Int): KtNode {
override val typeInt: Int get() = 2
}
data class FourthNodeType(val fourthId: Int): KtNode {
override val typeInt: Int get() = 3
}
}
package org.example;
public sealed interface Node {
int typeInt();
record FirstNodeType(int firstId) implements Node {
@Override public int typeInt() { return 0; }
}
record SecondNodeType(int secondId) implements Node {
@Override public int typeInt() { return 1; }
}
record ThirdNodeType(int thirdId) implements Node {
@Override public int typeInt() { return 2; }
}
record FourthNodeType(int fourthId) implements Node {
@Override public int typeInt() { return 3; }
}
}
11:45:53 a.m.: Executing 'jmh'...
> Task :compileKotlin UP-TO-DATE
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileJmhKotlin NO-SOURCE
> Task :compileJmhJava
> Task :processJmhResources NO-SOURCE
> Task :jmhClasses
> Task :jmh
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureJavaIntSwitchExpression
# Run progress: 0.00% complete, ETA 00:03:30
# Fork: 1 of 1
# Warmup Iteration 1: 55.617 ops/us
# Warmup Iteration 2: 52.328 ops/us
# Warmup Iteration 3: 51.634 ops/us
# Warmup Iteration 4: 52.062 ops/us
# Warmup Iteration 5: 53.844 ops/us
Iteration 1: 51.511 ops/us
Iteration 2: 53.812 ops/us
Iteration 3: 52.358 ops/us
Iteration 4: 52.177 ops/us
Iteration 5: 52.487 ops/us
Result "org.example.SwitchBenchmarks.measureJavaIntSwitchExpression":
52.469 �(99.9%) 3.233 ops/us [Average]
(min, avg, max) = (51.511, 52.469, 53.812), stdev = 0.839
CI (99.9%): [49.236, 55.701] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureJavaIntSwitchStatement
# Run progress: 14.29% complete, ETA 00:03:02
# Fork: 1 of 1
# Warmup Iteration 1: 55.972 ops/us
# Warmup Iteration 2: 53.776 ops/us
# Warmup Iteration 3: 53.104 ops/us
# Warmup Iteration 4: 48.923 ops/us
# Warmup Iteration 5: 53.761 ops/us
Iteration 1: 53.310 ops/us
Iteration 2: 52.765 ops/us
Iteration 3: 50.783 ops/us
Iteration 4: 50.682 ops/us
Iteration 5: 55.217 ops/us
Result "org.example.SwitchBenchmarks.measureJavaIntSwitchStatement":
52.551 �(99.9%) 7.293 ops/us [Average]
(min, avg, max) = (50.682, 52.551, 55.217), stdev = 1.894
CI (99.9%): [45.259, 59.844] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureJavaTypeIfInstanceofElseIf
# Run progress: 28.57% complete, ETA 00:02:32
# Fork: 1 of 1
# Warmup Iteration 1: 390.515 ops/us
# Warmup Iteration 2: 397.152 ops/us
# Warmup Iteration 3: 390.768 ops/us
# Warmup Iteration 4: 386.496 ops/us
# Warmup Iteration 5: 387.350 ops/us
Iteration 1: 385.158 ops/us
Iteration 2: 379.060 ops/us
Iteration 3: 396.496 ops/us
Iteration 4: 385.880 ops/us
Iteration 5: 382.926 ops/us
Result "org.example.SwitchBenchmarks.measureJavaTypeIfInstanceofElseIf":
385.904 �(99.9%) 24.989 ops/us [Average]
(min, avg, max) = (379.060, 385.904, 396.496), stdev = 6.490
CI (99.9%): [360.915, 410.893] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureJavaTypeSwitchExpression
# Run progress: 42.86% complete, ETA 00:02:01
# Fork: 1 of 1
# Warmup Iteration 1: 168.107 ops/us
# Warmup Iteration 2: 164.831 ops/us
# Warmup Iteration 3: 170.895 ops/us
# Warmup Iteration 4: 161.600 ops/us
# Warmup Iteration 5: 168.239 ops/us
Iteration 1: 167.644 ops/us
Iteration 2: 171.129 ops/us
Iteration 3: 163.984 ops/us
Iteration 4: 173.327 ops/us
Iteration 5: 173.622 ops/us
Result "org.example.SwitchBenchmarks.measureJavaTypeSwitchExpression":
169.941 �(99.9%) 15.782 ops/us [Average]
(min, avg, max) = (163.984, 169.941, 173.622), stdev = 4.098
CI (99.9%): [154.159, 185.723] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureKotlinIfIsElseIf
# Run progress: 57.14% complete, ETA 00:01:31
# Fork: 1 of 1
# Warmup Iteration 1: 368.072 ops/us
# Warmup Iteration 2: 377.689 ops/us
# Warmup Iteration 3: 383.745 ops/us
# Warmup Iteration 4: 397.663 ops/us
# Warmup Iteration 5: 388.396 ops/us
Iteration 1: 354.269 ops/us
Iteration 2: 365.076 ops/us
Iteration 3: 375.591 ops/us
Iteration 4: 326.270 ops/us
Iteration 5: 390.406 ops/us
Result "org.example.SwitchBenchmarks.measureKotlinIfIsElseIf":
362.322 �(99.9%) 93.074 ops/us [Average]
(min, avg, max) = (326.270, 362.322, 390.406), stdev = 24.171
CI (99.9%): [269.248, 455.397] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureKotlinIntSwitchExpression
# Run progress: 71.43% complete, ETA 00:01:00
# Fork: 1 of 1
# Warmup Iteration 1: 52.908 ops/us
# Warmup Iteration 2: 53.305 ops/us
# Warmup Iteration 3: 54.545 ops/us
# Warmup Iteration 4: 54.599 ops/us
# Warmup Iteration 5: 53.088 ops/us
Iteration 1: 49.434 ops/us
Iteration 2: 53.440 ops/us
Iteration 3: 51.749 ops/us
Iteration 4: 52.458 ops/us
Iteration 5: 54.060 ops/us
Result "org.example.SwitchBenchmarks.measureKotlinIntSwitchExpression":
52.228 �(99.9%) 6.920 ops/us [Average]
(min, avg, max) = (49.434, 52.228, 54.060), stdev = 1.797
CI (99.9%): [45.308, 59.148] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 20.0.2, OpenJDK 64-Bit Server VM, 20.0.2+9
# VM invoker: C:\Program Files\Zulu\zulu-20\bin\java.exe
# VM options: --enable-preview --enable-preview -Dfile.encoding=windows-1252 -Duser.country=CA -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 5 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.example.SwitchBenchmarks.measureKotlinTypeSwitchExpression
# Run progress: 85.71% complete, ETA 00:00:30
# Fork: 1 of 1
# Warmup Iteration 1: 354.683 ops/us
# Warmup Iteration 2: 399.424 ops/us
# Warmup Iteration 3: 398.986 ops/us
# Warmup Iteration 4: 397.786 ops/us
# Warmup Iteration 5: 388.968 ops/us
Iteration 1: 393.240 ops/us
Iteration 2: 384.008 ops/us
Iteration 3: 364.472 ops/us
Iteration 4: 371.634 ops/us
Iteration 5: 381.845 ops/us
Result "org.example.SwitchBenchmarks.measureKotlinTypeSwitchExpression":
379.040 �(99.9%) 43.105 ops/us [Average]
(min, avg, max) = (364.472, 379.040, 393.240), stdev = 11.194
CI (99.9%): [335.934, 422.145] (assumes normal distribution)
# Run complete. Total time: 00:03:33
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
extra caution when trusting the results, look into the generated code to check the benchmark still
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
different JVMs are already problematic, the performance difference caused by different Blackhole
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
Benchmark Mode Cnt Score Error Units
SwitchBenchmarks.measureJavaIntSwitchExpression thrpt 5 52.469 � 3.233 ops/us
SwitchBenchmarks.measureJavaIntSwitchStatement thrpt 5 52.551 � 7.293 ops/us
SwitchBenchmarks.measureJavaTypeIfInstanceofElseIf thrpt 5 385.904 � 24.989 ops/us
SwitchBenchmarks.measureJavaTypeSwitchExpression thrpt 5 169.941 � 15.782 ops/us
SwitchBenchmarks.measureKotlinIfIsElseIf thrpt 5 362.322 � 93.074 ops/us
SwitchBenchmarks.measureKotlinIntSwitchExpression thrpt 5 52.228 � 6.920 ops/us
SwitchBenchmarks.measureKotlinTypeSwitchExpression thrpt 5 379.040 � 43.105 ops/us
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.3/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 3m 34s
4 actionable tasks: 2 executed, 2 up-to-date
11:49:28 a.m.: Execution finished 'jmh'.
package org.example;
import org.openjdk.jmh.annotations.*;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 1)
@Fork(1)
@State(Scope.Benchmark)
public class SwitchBenchmarks {
@State(Scope.Thread)
public static class MyState {
public final Node[] javaNodes = new Node[]{
new Node.FirstNodeType(0),
new Node.SecondNodeType(1),
new Node.ThirdNodeType(2),
new Node.FourthNodeType(3),
};
public final KtNode[] kotlinNodes = new KtNode[]{
new KtNode.FirstNodeType(0),
new KtNode.SecondNodeType(1),
new KtNode.ThirdNodeType(2),
new KtNode.FourthNodeType(3),
};
private final int[] sequence = new int[1_000];
private int index;
public int result;
@Setup(Level.Iteration)
public void reallyFastSetup(){
var random = new Random(0);
for(int i = 0; i < sequence.length; i++){
sequence[i] = random.nextInt(4);
}
}
public Node nextJavaNode(){
var result = javaNodes[sequence[index]];
index += 1;
if(index == sequence.length) index = 0;
return result;
}
public KtNode nextKotlinNode(){
var result = kotlinNodes[sequence[index]];
index += 1;
if(index == sequence.length) index = 0;
return result;
}
}
@Benchmark
public void measureJavaTypeSwitchExpression(MyState state) {
// this is by far the most elegant
var node = state.nextJavaNode();
var result = JavaSwitchExpressions.typeSwitchExpression(node);
state.result = result;
}
@Benchmark
public void measureJavaTypeIfInstanceofElseIf(MyState state){
var node = state.nextJavaNode();
var result = JavaSwitchExpressions.typeIfInstanceofElseIf(node);
state.result = result;
}
@Benchmark
public void measureJavaIntSwitchStatement(MyState state){
var node = state.nextJavaNode();
var result = JavaSwitchExpressions.intSwitchStatement(node);
state.result = result;
}
@Benchmark
public void measureJavaIntSwitchExpression(MyState state){
var node = state.nextJavaNode();
var result = JavaSwitchExpressions.intSwitchExpression(node);
state.result = result;
}
@Benchmark
public void measureKotlinIntSwitchExpression(MyState state){
var node = state.nextKotlinNode();
var result = KotlinSwitchExpressionsKt.intSwitchExpression(node);
state.result = result;
}
@Benchmark
public void measureKotlinTypeSwitchExpression(MyState state){
var node = state.nextKotlinNode();
var result = KotlinSwitchExpressionsKt.typeSwitchExpression(node);
state.result = result;
}
@Benchmark
public void measureKotlinIfIsElseIf(MyState state){
var node = state.nextKotlinNode();
var result = KotlinSwitchExpressionsKt.ifIsElseIfExpression(node);
state.result = result;
}
}
@Groostav
Copy link
Author

running on:

  • Windows 10
  • Java 20,
  • Ryzen 7 4750U, 32GB
  • --enable-preview passed to match switch statements work.
  • Unconfigured GC, XMX, XMH, etc.

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