About Me

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

Tuesday, May 31, 2011

@Benchmark annotation for Groovy has been updated to fix bugs

Today I updated @Benchmark annotation for Groovy to resolve the following (embarrassing) bugs:


- java.lang.VerifyError occurs with Groovy 1.7
- java.lang.UnsupportedClassVersionError occurs with JVM 1.5


Now I have made sure that @Benchmark works with Groovy 1.7/1.8 and JVM 1.5/1.6. 
If you are new to @Benchmark, please read the last post.

Saturday, May 28, 2011

I released @Benchmark annotation for Groovy!

@Benchmark annotation allows to measure execution time of methods without adding code. The following example is the standard use of @Benchmark:
----
@Benchmark
def method() {
    // an operation takes 1 ms
}


method()
----
In this case, "1000000" will be printed out in the standard output. @Benchmark express the time in nanoseconds.


There are two options to customize the output format:
----
@Benchmark(prefix = 'execution time of method(): ', suffix = ' ns')
def method() {
    // an operation takes 1 ms
}


method()
----
Then the output will be "execution time of method(): 1000000 ns".


@Benchmark uses this.println(value) for printing out. So you can redirect the outputs by setting an appropriate writer for the purpose to the "out" property:
----
def results = new StringBuffer()
this.setProperty("out", new StringBufferWriter(results))


// method calls


this.setProperty("out", null)


results.toString().eachLine { result ->
    println "${result.asType(int.class) / 1000000} ms"
}
----


You can download it from here.

Friday, May 27, 2011

Groovy Mascot Tournament - Mr.Groovy

I've been thinking that Groovy should have a mascot.
One idea that I have is "Mr. Groovy":


 Gr--
  oo-;
   v 
 / y  \


Something good, isn't it?


Sorry, it's a joke... but I really want a cool mascot for Groovy.

Sunday, May 22, 2011

Exploring Groovy 1.8 - Is the code really optimized?


Groovy's biggest issue is performance. In 1.8, optimizations for integer operations and direct method calls are done as the beginning of further performance improvements. Both of the optimizations has the same concept, "Try to compile as it is written". It does not mean the Groovy compiler had failed syntax analysis. Groovy source code are dramatically converted on compilation time to make possible flexible metaprogramming (I mentioned about it in my previous post, "Inside GDK"). The problem is there is no exception. I'll take a method to calculate a Fibonacci number as example:
----
int fib(int n) {
    if (n < 2) return n
    return fib(n - 1) + fib(n - 2)
}
----

The code is not needed to receive benefits of metaprogramming, but it is converted like the following code in 1.7 or the earlier versions(in the actual code,  Groovy APIs appear and it is little more complicated but I omit them because they are not important in this post). All of the operations/methods are replaced to reflections and boxing and unboxing are done to the integers repeatedly:
----
int fib(int n) {
    // Get the Method objects
    if (lessThanMethod.invoke(Integer.valueOf(n), Integer.valueOf(2))) 
        return Integer.valueOf(n).intValue();
    return 
        ((Integer) plusMethod.invoke(
            fibMethod.invoke(
                minusMethod.invoke(Integer.valueOf(n), Integer.valueOf(1)
            ),
            fibMethod.invoke(
                minusMethod.invoke(Integer.valueOf(n), Integer.valueOf(2)
            )
        )).intValue();
    );
----

It will be optimized as the following in 1.8. This is the meaning of "Try to compile as it is written" that I said in the beginning:
----
int fib(int n) {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}
----

By the way, the story is not ended yet. Because, the requirements to receive benefits of the optimizations are much harder than we expect. Obeying the following warnings that are written in the release notes is not enough:
1. If you want the optimization of integer operations, must not mix int type and Integer type in a expression
2. If you want the optimization of direct method calls, the method must be done on "this" and all of the argument types must be primitive or final.

See the following table. This is what I compare the execution time between 1.8.0 and 1.7.10 with methods in different ways to calculate a Fibonacci number:
Execution time of Fibonacci(30) in nanoseconds
Rank
Algorithm / Style
v1.8
v1.7
Improvement
1
Iteration + Groovy-style for loop
49720
46276
-6.93%
Down :-(
2
Iteration + Java/C-style for loop
129360
77824
-39.84%
Down :-(
3
Recursion + if + return
45080820
548522821
+1116.75%
Up :-)
4
Recursion + ternary operator
120489259
538476119
+346.91%
Up :-)
5
Recursion + if-else
259945537
546381516
+110.19%
Up :-)

The result shows us three things:
1. The performances of iteration versions were slightly worse
2. The performances of recursion versions were highly improved
3. The performance gaps among the coding style got wider

Those reasons are simple. Most of the cases failed both or one of the optimizations. In the table, all of the cases except for recursion + if + return version failed. For example, recursion + if + else version is this:
----
int fib(int n) {
    if (n < 2) n
    else fib(n - 1) + fib(n - 2)
}
----

The code will be converted like the following (I'll repeat that it slightly differs from the actual code to be exact):
----
int fib(int n) {
    if (n < 2) return n;
    else return 
        ((Integer) plusMethod.invoke(
            fibMethod.invoke(Integer.valueOf(n - 1)),
            fibMethod.invoke(Integer.valueOf(n - 2))
        )).intValue();
    );
}
----

That small difference affects success of the optimizations and make the more than five times difference in performance. I guess this problem will be fixed in a future release, but at least, it is not time to rejoice yet.

By the way, is the code really optimized?


(The code list that I used for the calculation)

Iteration + Groovy-style for loop:
----
int fib(int n) {

    int a = 0
    int b = 1
    int c
    for (int i in 2..n) {
        c = a + b
        a = b
        b = c
    }
    b
}
----

Iteration + Java/C-style for loop:
----
int fib(int n) {
    int a = 0
    int b = 1
    int c
    for (int i = 2; i <= n; i++) {
        c = a + b
        a = b
        b = c        
    }
    b
}
----

Recursion + if + return:
----
int fib(int n) {
    if (n < 2) return n
    return fib(n - 1) + fib(n - 2)
}
----

Recursion + ternary operator:
----
int fib(int n) {
    n < 2 ? n : fib(n - 1) + fib(n - 2)  
}
----

Recursion + if-else:
----
int fib(int n) {
    if (n < 2) n
    else fib(n - 1) + fib(n - 2)
}
----

Wednesday, May 4, 2011

Gj is updated!

I updated "Gj" today. "Gj" is a script to convert Groovy program to distribution format quickly, easily, and without any effort (please see also my previous post).


Updates are as follows:


1. Option to bundle Groovy
-bg/--bundlegroovy option has been added. You can use the option to bundle Groovy instead to specify Groovy libraries directly.
----
> cat src\PrintHello.groovy
println 'hello'


> groovy Gj.groovy -bg -s src -m PrintHello printhello.jar


> java -jar printhello.jar
hello
----


2. The default Java version for javac
The default java version for javac has been changed to be the same version on your system.


3. Option to print Gj version
-ver/--version option has been added. You can use the option to check the Gj version.
----
> groovy Gj.groovy -ver
Gj version: 11.05.05
----


You can download new version from here. Please try it out ;-)

Monday, May 2, 2011

Poison for Groovy

Last night, I bumped into a bug of groovy.bat while testing Gj script (that I wrote to make executable jar files easily. See my previous post for the details). groovy.bat cannot handle any command-line arguments that contain * (asterisk).


For example, when entering the following commands, groovy.bat will not respond.
----
groovy Foo.groovy "*"
----
or
----
groovy -e "println '*'"
----


When passing the arguments directly to GroovyMain, it works.
----
java -cp "%GROOVY_HOME%\embeddable\groovy-all-1.8.0.jar" groovy.ui.GroovyMain -e "println '*'"
*
----


I searched to see if this is a known bug, and I found GROOVY-3043.  Fortunately the bug is already fixed 2 years ago. OK, so I... fixed? Wait, it's still here.


Strangely the fixed (not sure, but it seems to work) version of startGroovy.bat (that is called by groovy.bat and parses command-line arguments) is attached to GROOVY-3043. I have no idea why it is not provided us yet...


I reported this bug as new issue, GROOVY-4805. And also I attached a patch which I wrote to the issue, so you might try the patch if you are Windows NT-based user. Anyway, I hope this bug is fixed ASAP.