Saturday, March 8, 2008

RubyInline for JRuby? Easy!

With JRuby 1.1 approaching, performance looking great, and general Ruby compatibility better than its ever been, we've started to branch out into more libraries and applications. So a couple days ago, I thought I'd make good on a promise to myself and have a look at getting RubyInline working on JRuby.

RubyInline is a library by Ryan "zenspider" Davis which allows you to embed snippits of C code into your Ruby scripts. RubyInline does a minimal parse of that source, and based on the function signature you provide it wires it to the containing class as a Ruby method entry point and performs appropriate entry and exit type conversion to whatever C types you happen to use. It's particularly useful if you have a small algorithm you need to run fast, and you'd like to run it in a somewhat faster language by "throwing work over" to it.

Here's the example of RubyInline from Ryan's page:
class MyTest

def factorial(n)
f = 1
n.downto(2) { |x| f *= x }
f
end

inline do |builder|
builder.c "
long factorial_c(int max) {
int i=max, result=1;
while (i >= 2) { result *= i--; }
return result;
}"
end
end

The interesting bit is the builder.c call, one of several functions on the C builder. Others allow you to add arbitrary preamble code, imports, and "bare" methods (with no type conversion) among other things. And as you'd expect, performance is greatly improved by writing some algorithms in C instead of Ruby.

Naturally, we want to have the same thing work in Java, and ideally use the same API and the same RubyInline plumbing for the rest of it. So then, I present to you java_inline, a RubyInline builder for JRuby!

This represents about four hours of work, and although it doesn't yet have a complete complement of tests and could use some "robustification", it's already working in about 100 lines of code. It's made far easier in JRuby than in C Ruby because we already have a full-features Java integration layer to handle the argument mapping.

Here's a sample java_inline script to show what it looks like, similar to the fastmath.rb sample provided with RubyInline.

So how does it work? Well the Ruby side of things is largely the same as RubyInline's C builder...parse signature, compose the code together and compile it, and bind the method. It wires directly into the RubyInline pipeline, so all you need to do is install RubyInline, require the java_inline.rb script and you're all set. On the Java side, it's using the Java Compiler API, provided in Java 6 implementations. (OT: This has to be the worst-designed API I've ever seen. Go see for yourself. It's cruel and unusual. I won't dwell on it.) So yes, this will only work on Java 6. Deal with it...or submit a patch to get it working on Java 5 as well :)

It's not released in any official form yet, but I'll probably try to wire up a gem or something. I have to make sure I'm dotting my eyes and crossing my tees when I release stuff, even if it's only 100loc. But the repository is obviously public, so play with it, submit patches and improvements, and let me know if you'd like to use it or help work on it more. I'm also interest in suggestions for other libraries you'd like to see special JRuby support for, so pass that along too.

7 comments:

  1. This is so useful, Charles, you should consider making it part of teh standard jruby distribution.

    ReplyDelete
  2. That's great news. Could you include a result from the benchmark in simple math file?

    Offtopic: do you have a road map for post 1.1? The one on the wiki seems to be a bit stale now that 1.1 is around the corner.

    ReplyDelete
  3. Hello Charles:
    The C code will be debugged step to step, like the Jruby code?
    Many thanks. :-)

    ReplyDelete
  4. @juan Yeah, I wish...I think we have a while to wait for all-in-one debugging through ruby and java in the same tool. But it will come.

    ReplyDelete
  5. "I'm also interest in suggestions for other libraries you'd like to see special JRuby support for, so pass that along too."

    Hadoop and Nutch are at the top of my list for the next couple weeks. Seems like there's some room for interesting JRuby support there. (Not sure what, since I'm still in the "frantic reading" stage of the project...)

    ReplyDelete
  6. I tried this on Mac OS X 10.5.1 and got an error.

    ./vendor/java_inline.rb:19: cannot load Java class javax.tools.ToolProvider (NameError)

    I'm just playing with optimizations framework inside StrokeDB, i'm not a Java guru at all. I have no idea what to do with this error.

    Hope this helps:

    $ java -showversion
    java version "1.5.0_13"
    Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)
    Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)

    $ uname -a
    Darwin oleganza.local 9.1.0 Darwin Kernel Version 9.1.0: Wed Oct 31 17:46:22 PDT 2007; root:xnu-1228.0.2~1/RELEASE_I386 i386 i386 MacBook2,1 Darwin

    ReplyDelete
  7. @oleg This requires a Java 6 implementation that has the Java Compiler API built in. It won't currently run on anything lower than Java 6.

    ReplyDelete