Tuesday, April 6, 2010

JRuby + Coroutines = really fast

Today I did some tests to see how much, and if at all, JRuby could benefit from implementing Ruby fibers with real coroutines. I was hoping to see some improvements, and that maybe JRuby will even surpass the native Ruby implementation in fiber performance.

So I got myself the current Ruby (Ruby 1.9.1-p376) and JRuby (from git) and made up a simple program that passes messages through a number of fibers.

Running a number of tests with different parameters on Ruby and an unmodified JRuby wasn't too difficult (after realizing that -1.9 isn't the same as --1.9).
But in order to run JRuby with coroutine support I first had to hack together coroutine support for JRuby :-)
As I'm not familiar with the JRuby code I had to dive around a bit. Somewhere along the way I realized that I'll probably need CoroutineLocals, so I had to implement those first. Then some more hacking and I got myself the (probably) first JRuby implementation to use real coroutines.
I only had to modify:
FiberLibrary.java (to use coroutines instead of threads)
ThreadService.java (to use CoroutineLocals instead of ThreadLocals)
Ruby.java (I'm not sure why, but I had to change the way the fiber library is included to addLazyBuiltin)

That was easy! A good sign for the quality of the JRuby code, I suppose...

But here are the results:
I ran different numbers of messages through a line of 5, 50, 500 and 5000 consecutive fibers. Here are the charts that compare Ruby, JRuby with coroutine support and JRuby without coroutine support. (the x-axis shows the number of messages, and the y-axis is time taken in seconds)

5 fibers:

50 fibers:

500 fibers:

5000 fibers:

I somewhat expected JRuby with coroutine support to win over JRuby without by a large margin (because Threads are used to emulate fibers), but being faster than Ruby is of course a nice bonus!

But now I should get back to work and integrate all the fixes I had to do to make it work...