About Me

My photo
Author of Groovy modules: GBench, GProf, Co-author of a Java book パーフェクトJava, Game Developer at GREE

Saturday, June 27, 2015

GProf supports Groovy 2.4

GProf, the Groovy Profiler supports Groovy 2.4 in the new release version 0.3.1.

The release already has been available on the Maven Central repository and it means that now you can install GProf by the following one line:

@Grab('org.gperfutils:gprof:0.3.1-groovy-2.4') // v0.3.1 for Groovy 2.4

Here is a demo:

// slow !!
def fib(n) {
    if (n < 2) {
        n
    } else {
        fib(n - 1) + fib(n - 2)
    }
}

profile {
    fib(20)
}.prettyPrint()
and the output will be like this:
Flat:

 %    cumulative   self            self     total    self     total   self     total
time   seconds    seconds  calls  ms/call  ms/call  min ms   min ms  max ms   max ms  name
46.8        0.82     0.82      2   411.11   875.84  366.64   786.45  455.58   965.22  Prof.fib
35.1        1.43     0.61  21890     0.02     0.02    0.00     0.00    3.76     3.76  java.lang.Integer.minus
17.9        1.75     0.31  10945     0.02     0.02    0.00     0.00    1.79     1.79  java.lang.Integer.plus
 0.0        1.75     0.00      1     0.74  1753.00    0.74  1753.00    0.74  1753.00  Prof$_run_closure1.fib
 0.0        1.75     0.00      1     0.14  1753.15    0.14  1753.15    0.14  1753.15  Prof$_run_closure1.doCall

Call graph:

index  % time  self  children  calls        name
               0.00      1.75          1/1      
[1]     100.0  0.00      1.75            1  Prof$_run_closure1.doCall [1]
               0.00      1.75          1/1      Prof$_run_closure1.fib [2]
-----------------------------------------------------------------------------
               0.00      1.75          1/1      Prof$_run_closure1.doCall [1]
[2]      99.9  0.00      1.75            1  Prof$_run_closure1.fib [2]
               0.82      0.92          2/2      Prof.fib [3]
               0.00      0.00      2/21890      java.lang.Integer.minus [4]
               0.00      0.00      1/10945      java.lang.Integer.plus [5]
-----------------------------------------------------------------------------
               0.82      0.92          2/2      Prof$_run_closure1.fib [2]
[3]      99.9  0.82      0.92      2+21888  Prof.fib [3]
               0.61      0.00  21888/21890      java.lang.Integer.minus [4]
               0.31      0.00  10944/10945      java.lang.Integer.plus [5]
-----------------------------------------------------------------------------
               0.00      0.00      2/21890      Prof$_run_closure1.fib [2]
               0.61      0.00  21888/21890      Prof.fib [3]
[4]      35.1  0.61      0.00        21890  java.lang.Integer.minus [4]
-----------------------------------------------------------------------------
               0.00      0.00      1/10945      Prof$_run_closure1.fib [2]
               0.31      0.00  10944/10945      Prof.fib [3]
[5]      17.9  0.31      0.00        10945  java.lang.Integer.plus [5]
-----------------------------------------------------------------------------

Last of all,

I'm sorry for the late release and thanks for kicking my butt!

Saturday, February 14, 2015

GBench 0.4.3 released - Let's benchmark "as" operator in Groovy 2.4 and 2.3.

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