Friday, January 19, 2007

JRuby JDBC performance

I'm writing server-side application (webchat) which communicates with DB.
I wanted to use JRuby as the main implementation language, just rewrite som parts in pure java.
So I intended to use with ActiveRecord-JDBC. I used H2 database in embedded mode.

My test simulates retrieving of last 10 rows in small table 1000 times in sequence.
Every row is converted to set of custom classes which implent interfaces of business objects: ChatMessage containing MessageID, Sender, Recipient, MessageText,Timestamp, etc...
There is previous java implementation of these interfaces, previous to decision of choosing JRuby.

As I sad first prototype used ActiveRecord. Every record was converted to set of java data classes. I used JRuby 0.9.2 and it took 3o seconds to run the test.
After upgrade to trunk version from 2007-01-07 test sped up to 23 seconds.
Great improvement, but still it wasn't fast enough.
And after evaluation of pure Java performance it was really incredibly slow. Pure looping through JDBC ResultSet in Java took less than a second.
On the other side, when I increased number of iterations 10x, AR took ~700 seconds. You see,  it's not linear. Java didn't have this problem: 5.5 seconds.
I have switched to trunk version with first JIT compiler on Friday, and it really helps - just 210 seconds instead of 700. Still it consumes too much memory, but finally with usual linear behaviour (21 seconds for 1000 iterations).

So I decided to try some hybrid implementations. These were performed with 2 weeks old trunk version as this is the fastest for me ATM.
  • Ruby code calling raw JDBC interface with existing Java data classes: 114 seconds.
  • The same ruby code but with new Ruby data classes (fresh implementations of same Java interfaces): 62 seconds.
  • Pure ruby code without any Java objects, assigning just to flat Hash: 40 seconds.
  • Experimental replacement of direct JDBC calls with Java class wrapping DB row in Object[], multiple assigment to local variables: 40 seconds.
  • Same replacement returning HashMap instead of Object[]: 80 seconds.
Quite various results, aren't they? I'm not sure, what is the brake here.
I thought it could be because of converting from Ruby to Java and back, but I'm not sure yet. I tried keeping instances of Java objects in Hash not to construct them everytime, but it doesn't help a lot, just few seconds.

What to do next? I will try some metaprogramming hacks and mix ruby code generation with more universal Java wrappers to achieve good performance and configurability at the same time.