Today, I released version 0.4.3 of GBench, the benchmarking module for Groovy.
The release is a maintenance release, but it includes a new feature, anonymous (unlabeled) code block syntax. The syntax makes your benchmarking code simpler when you have only one code block to be benchmarked.
// benchmark { // label { // // code to be benchmarked // } // } benchmark { // code to be benchmarked }
Let's benchmark using the syntax. This time's target is "Optimization of primitive type conversions with the as operator (GROOVY-7140)". I wrote a the following benchmarking code:
// Bench.groovy import groovy.transform.CompileStatic @CompileStatic void test(char x) { ((((x as byte) as short) as int) as long) } benchmark { test('a') }.prettyPrint()
Here are the results of the code above with Groovy 2.3.0 and 2.4.0 in my environment.
$ grape install org.gperfutils gbench 0.4.3-groovy-2.3 $ gvm use groovy 2.3.0 $ groovy -cp `find ~/.m2 -name gbench-0.4.3-groovy-2.3.jar` Bench.groovy Environment =========== * Groovy: 2.3.0 * JVM: Java HotSpot(TM) 64-Bit Server VM (24.75-b04, Oracle Corporation) * JRE: 1.7.0_75 * Total Memory: 491.5 MB * Maximum Memory: 910.5 MB * OS: Mac OS X (10.10.1, x86_64) Options ======= * Warm Up: Auto (- 60 sec) * CPU Time Measurement: On user system cpu real 1822 3 1825 1828
$ grape install org.gperfutils gbench 0.4.3-groovy-2.4 $ gvm use groovy 2.4.0 $ groovy -cp `find ~/.m2 -name gbench-0.4.3-groovy-2.4.jar` Bench.groovy Environment =========== * Groovy: 2.4.0 * JVM: Java HotSpot(TM) 64-Bit Server VM (24.75-b04, Oracle Corporation) * JRE: 1.7.0_75 * Total Memory: 491.5 MB * Maximum Memory: 910.5 MB * OS: Mac OS X (10.10.1, x86_64) Options ======= * Warm Up: Auto (- 60 sec) * CPU Time Measurement: On user system cpu real 1543 3 1546 1549
It seems no doubt that the performance of "as" operator was improved in Groovy 2.4.0. So how did they make it? The difference between the following bytecode snippets is the answer.
Groovy 2.3.0
public void test(char); Code: 0: iload_1 1: invokestatic #74 // Method java/lang/Character.valueOf:(C)Ljava/lang/Character; 4: getstatic #80 // Field java/lang/Byte.TYPE:Ljava/lang/Class; 7: invokestatic #84 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.asType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; 10: invokestatic #90 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.byteUnbox:(Ljava/lang/Object;)B 13: invokestatic #93 // Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte; 16: getstatic #96 // Field java/lang/Short.TYPE:Ljava/lang/Class; 19: invokestatic #84 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.asType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; 22: invokestatic #100 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.shortUnbox:(Ljava/lang/Object;)S 25: invokestatic #103 // Method java/lang/Short.valueOf:(S)Ljava/lang/Short; 28: getstatic #106 // Field java/lang/Integer.TYPE:Ljava/lang/Class; 31: invokestatic #84 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.asType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; 34: invokestatic #110 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.intUnbox:(Ljava/lang/Object;)I 37: invokestatic #113 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 40: getstatic #116 // Field java/lang/Long.TYPE:Ljava/lang/Class; 43: invokestatic #84 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.asType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; 46: invokestatic #120 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.longUnbox:(Ljava/lang/Object;)J 49: pop2 50: return
Groovy 2.4.0
public void test(char); Code: 0: iload_1 1: i2b 2: i2s 3: i2l 4: pop2 5: return