I was interested in performance question about determine type of object and write some benchmarks with 2 ways of determination.
Can someone explain me why variant with instanceof
faster 350 times than variant with class name switching with strings?
Code:
class A {}
class B extends A {}
public class InstanceOfBenchmark {
public static final Object a = new A();
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testInstanceOf()
{
if (a instanceof B) {}
else if (a instanceof String) {}
else if (a instanceof ArrayList) {}
else if (a instanceof HashMap) {}
else if (a instanceof HashSet) {}
else if (a instanceof A);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testName() {
String class_name = a.getClass().getSimpleName();
switch (class_name) {
case ("B") :
case ("String") :
case ("ArrayList") :
case ("HashMap") :
case ("HashSet") :
case ("A") :
}
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(InstanceOfBenchmark.class.getSimpleName())
.warmupIterations(20)
.measurementIterations(100)
.forks(1)
.build();
new Runner(opt).run();
}
}
Results:
Benchmark Mode Cnt Score Error Units
InstanceOfBenchmark.testInstanceOf thrpt 100 3482.001 ± 25.447 ops/us
InstanceOfBenchmark.testName thrpt 100 10.579 ± 0.078 ops/us
I run tests with 200 times warmup and 2000 iteration too, result was same.
Bytecode for the String version:
0: aload_0
1: invokevirtual #2 // Method java/lang/Object.getClass:()Ljava/lang/Class;
4: invokevirtual #3 // Method java/lang/Class.getSimpleName:()Ljava/lang/Str
7: astore_1
8: aload_1
9: astore_2
10: iconst_m1
11: istore_3
12: aload_2
13: invokevirtual #4 // Method java/lang/String.hashCode:()I
16: lookupswitch { // 6
-1932803762: 118
-1932797868: 132
-1808118735: 90
65: 146
66: 76
578806391: 104
default: 157
}
76: aload_2
77: ldc #5 // String B
79: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
82: ifeq 157
85: iconst_0
86: istore_3
87: goto 157
90: aload_2
91: ldc #7 // String String
93: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
96: ifeq 157
99: iconst_1
100: istore_3
101: goto 157
104: aload_2
105: ldc #8 // String ArrayList
107: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
110: ifeq 157
113: iconst_2
114: istore_3
115: goto 157
118: aload_2
119: ldc #9 // String HashMap
121: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
124: ifeq 157
127: iconst_3
128: istore_3
129: goto 157
132: aload_2
133: ldc #10 // String HashSet
135: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
138: ifeq 157
141: iconst_4
142: istore_3
143: goto 157
146: aload_2
147: ldc #11 // String A
149: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
152: ifeq 157
155: iconst_5
156: istore_3
157: iload_3
158: tableswitch { // 0 to 5
0: 196
1: 196
2: 196
3: 196
4: 196
5: 196
default: 196
}
196: return
Bytecode for the instanceof
version:
0: aload_0
1: instanceof #12 // class B
4: ifeq 10
7: goto 57
10: aload_0
11: instanceof #13 // class java/lang/String
14: ifeq 20
17: goto 57
20: aload_0
21: instanceof #14 // class java/util/ArrayList
24: ifeq 30
27: goto 57
30: aload_0
31: instanceof #15 // class java/util/HashMap
34: ifeq 40
37: goto 57
40: aload_0
41: instanceof #16 // class java/util/HashSet
44: ifeq 50
47: goto 57
50: aload_0
51: instanceof #17 // class A
54: ifeq 57
57: return
Does it still surprise you that instanceof
is faster? The number of operations is significantly fewer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With