Sunday, January 06, 2008

Application-Specific Performance Tuning for Groovy

You all know that if we bypass meta-layer of Groovy, the performance will be boosted. I have 1 class, 1 closure (as an inner class) and 3 method calls in the following codes (System#currentTimeMillis, times, and println):


package org.codehaus.groovy.aop

class Test {
static void main(args) {
def s1 = System.currentTimeMillis()
1000000.times {
int i = 10
println "test ${i}"
}
def s2 = System.currentTimeMillis()
println "time: ${s2-s1}"
}
}



I have 100% confidence that those calls won't be dynamic. Then I can have an aspect (written in AspectJ) applied to them for the performance reason. Although, it's quite complicated piece of codes here, but at least your application will be faster.


public aspect TestBooster {

pointcut inv_println(String methodName, Object[] params):
call(static * ScriptBytecodeAdapter.invoke*Method*N(..)) &&
(
withincode(public static void org.codehaus.groovy.aop.Test.main(..))
|| withincode(public Object org.codehaus.groovy.aop.Test$_main_closure1.doCall(..))
)
&& args(..,methodName,params)
&& if("println".equals(methodName));

Object around(String methodName, Object[] params):
inv_println(methodName, params) {
DefaultGroovyMethods.println((Object)null, params[0]);
return null;
}

pointcut inv_times(Object self, String methodName, Object[] params):
call(static * ScriptBytecodeAdapter.invokeMethodN(..)) &&
(
withincode(public static void org.codehaus.groovy.aop.Test.main(..))
)
&& args(Class,self,methodName,params)
&& if("times".equals(methodName));

Object around(Object self, String methodName, Object[] params):
inv_times(self, methodName, params) {
DefaultGroovyMethods.times((Number)self, (Closure)params[0]);
return null;
}

}


The above aspect is for by-passing calls to "times" and "println" with the replacement of the real targets. The woven code is averagely 15% faster (in both client and server VM) because the call distance is shorten from 36 levels to 17.
With this technique, you can apply the optimisation in the way you want just before you deploying the app (without touching your source). Anyway, don't forget to do the regression tests after the optimisation ;-).

1 comment:

Anonymous said...

Wow, a good performance tuning Idea!

Can you also write something like that for algorithmic operations.

There is a performance test program called "fannkuch" (name comes from the german "Pfannekuchen"). The performance in Groovy is very worse. (A simple java application is ~707 times faster).

If you have a look at this thread: http://www.groovy-forum.de/read.php?2,766,877#msg-877
you can see that every call is handled by the Meta Object Modell.

Because java is about 707 times faster, groovy looks like a nice "rapid prototyping" or "toy" language wich isn't the right choise for critical algorithmic parts.

Maybe you can bypass the Meta Modell in this steps (addition, initialising...) and give the Groovy Developers a advice.

I think the meta modell isn't needed in every step! The groovy compiler/tokenizer should detect steps where the metamodell isn't usable and use direct calls.