Sunday, January 13, 2008

Dataflow Analysis with ASM 3.1 and StackMap

Very surprised that I've got a huge benefit from stack map feature of Java 6.

I'm trying to do some dataflow analysis over compiled Groovy classes, and what I've found in ASM 3.1 is that it comes with method visitor for doing this for me. I've just added some partial evaluation for constants (for almost kinds of JVM instructions with a constant as their operand) and when the visitor visits method instruction, like INVOKESTATIC, I've got type information of each wrapped arguments (Groovy uses Object[] to hold them for dispatching the call with ScriptBytecodeAdapter).

Now my new unwrapping meta-method algorithm is blazing fast. Thanks ASM guys !!.

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 ;-).