Monday, April 16, 2007

Paving the Road to JRuby 1.0: Performance

Since it looks like Antonio Cangiano is going to delay the next running of the bulls until Ubuntu 7.04 is released, I figured the next JRuby 1.0 update could be about performance.

Performance is such a tricky area for Ruby. Folks outside the Ruby community happily malign its performance, fueled by both FUD and by some truths. Folks within the Ruby community either aren't affected by Ruby's performance (it's "fast enough") or they simply don't care (it's slow, but I still love it too much to leave). A small part of the Ruby community takes what is in my opinion a rather anti-Ruby stance: "write it in C" as a targeted solution for identified bottlenecks. I suppose reality lies somewhere inbetween all these views, with Ruby's performance certainly not being stellar in the general case, but reasonable and sometimes surprisingly good for specific cases.

Ruby 1.9 has raised the promise of a new bytecode-based interpreter engine--a Ruby "virtual machine" by some reckonings--with the goal of improving performance foremost on the minds of Ruby's developers. And again the performance question is rather complicated. Antonio's recent shootout, running only Ruby 1.9's chosen benchmarks against all other implementations, shows it doing extremely well. It comes out many times faster than Ruby 1.8 in almost every test, and no other implementation even comes close. The truth however doesn't change very much; for non-synthetic benchmarks (like running Rails) the situation only improves by about 15% for some tests, and for many other tests performance actually degrades. Of course Ruby 1.9 is still under heavy development, and many more improvements are ahead, but the wide range of results demonstrates again that benchmarking must be taken with a grain of salt.

Et tu, JRuby?

So then there's us and JRuby. JRuby's professed goal has never been to be a better Ruby; at best, we're trying to build the best Ruby possible on top of what we believe is the best VM in existence. And toward that end we've put most of our time into compatibility and correctness above all else, aiming for the goal of complete Ruby language compatibility and near-complete builtin-class compatibility, hoping for JRuby to someday be treated as "just another Ruby implementation" on which people can run their apps and design their clever frameworks and libraries. But in the past year, it's become apparent that we could actually exceed Ruby's performance for specific cases in the near term, and for general cases over time. So the target market for JRuby seems to be changing from "Ruby users that must use Java VMs, libraries, and servers" to "Ruby users that want a better-performing, more scalable implementation". And our noble quest for near-complete compatibility gets muddled with all these fiddly performance details.

But that's life, right? The goals you set out for yourself and your projects rarely align perfectly with the goals others set out for you. The trick is achieving a balance between what you want to do with your life and what others (like your community members or your employers) want you to do. Perhaps the successful developer is the one who can derive pleasure from both tasks.

On the road to JRuby 1.0, we've done our best to balance compatibility and performance. We are now nearing the end of the compatibility road, with Ruby language features nearly 100% and builtin classes almost as complete as we can make them on the JVM. The real reason for a JRuby 1.0 now is that we believe we're finally approaching "Ruby compatibility" for some high measure of compatibility, such that the vast majority of platform-agnostic Ruby code should run successfully on JRuby. And that's certainly no small feat, given that just a year ago we celebrated a mostly-broken cobbled-together Rails 1.1 app slowly handling CRUD operations. Today, people are deploying JRuby on Rails apps in production, and the game has only gotten more interesting.

So then, performance. You're all wondering what the answer is to this performance thing, aren't you? Is JRuby going to blow away all competition, including the nascent Ruby 1.9 and mid-term projects like XRuby, Rubinius, and Ruby.NET? It's certainly possible, but it's not our goal. Is JRuby going to be faster than Ruby 1.8 when 1.0 is released? For specific cases, I'd say yes...there's plenty of areas we already perform better than Ruby 1.8. For the general cases, it's hard to say. We perform well serving up Rails requests today, but only about 50-70% of Ruby 1.8's performance. And though we know where most of the bottlenecks lie, we're a little resource limited trying to fix them. Do we believe that we'll be faster than Ruby 1.8 in all general cases in the near future? Yes, we strongly believe that will happen.

Now of course I could ramble on and on about performance and put you all to sleep, but actual numbers will probably keep your interest better than my droning.

The Test

Like the shootout, I'm just running the Ruby 1.9 benchmarks here. We have not done any targeted optimization for these tests; there's no Fixnum magic or anything like that under the covers. What we have done is implement multiple general-purpose optimizations to speed method and block invocation, object creation, and interpretation. We've also spent a little more time getting these tests to compile successfully, but of course any work done on the compiler is generally applicable as well.

These results are all based on JRuby trunk code, revision 3480. I'm running Java 6 on a MacBook Pro 2.16GHz Core Duo, and all code was compiled to target Java 6.

For the first set of results the JRuby command executed basically amounts to the following:

JAVA_OPTS=-Xverify:none jruby SERVER -O [script.rb]
  • JAVA_OPTS=-Xverify:none specifies not to verify classes on startup; this is a large part of the speed hit Java applications have when starting. We turn it off here to remove a little of that overhead from the benchmarks, since most of them are very short runs to begin with.
  • SERVER specifies that JRuby should use the "server" VM, which takes a bit longer to optimize a bit more heavily when JITting Java code into native instructions. JRuby generally performs best under the server VM, though using it impacts startup time.
  • -O disables ObjectSpace in JRuby. This may seem like cheating, but the truth is that when ObjectSpace is enabled we pay double or triple the object creation cost in JRuby since we have to track all objects separately. Ruby's ObjectSpace is essentially zero-cost...it's just a window into the memory manager. Since we don't have a low or zero-cost way to implement ObjectSpace, it falls into what I categorize as "optional incompatibility". If you don't need it, turn it off and JRuby performance will improve. You can call it cheating if you like...the truth is that practically no code actually depends on ObjectSpace.
And then there's the standard disclaimer for any Java application: these times include startup, about 1.0 to 1.3 seconds. I know you all are picky about how benchmarks are run, and you love to include startup time even though the vast majority of the world's work is not done in the first few seconds of execution, but if you'll forgive the disabling of ObjectSpace I'm willing to meet you half way. Startup time is included.
TEST                    MRI     JRuby
--------------------------------------
app_answer 0.584 2.239
app_factorial ERROR 4.248
app_fib 7.126 10.549
app_mandelbrot 2.346 10.300
app_raise 2.587 4.441
app_strconcat 1.829 2.141
app_tak 9.711 13.345
app_tarai 7.529 11.050
loop_times 5.475 6.903
loop_whileloop 9.982 11.797
loop_whileloop2 2.009 3.292
so_ackermann 13.726 26.132
so_array 7.257 8.801
so_concatenate 2.063 3.546
so_count_words 0.507 4.491
so_exception 4.342 8.346
so_lists 1.238 2.744
so_matrix 2.258 4.241
so_nested_loop 5.609 7.898
so_object 7.050 6.496
so_random 2.139 4.643
so_sieve 0.740 2.240
vm1_block 23.604 27.405
vm1_const 16.774 20.650
vm1_ensure 16.546 15.800
vm1_length 21.210 21.899
vm1_rescue 13.170 16.197
vm1_simplereturn 21.091 31.376
vm1_swap 25.114 17.949
vm2_array 6.049 5.690
vm2_method 13.528 17.759
vm2_poly_method 16.886 24.956
vm2_poly_method_ov 4.576 6.972
vm2_proc 7.060 7.797
vm2_regexp 4.421 9.353
vm2_send 4.332 8.198
vm2_super 4.992 7.944
vm2_unif1 3.838 6.095
vm2_zsuper 5.409 8.452
vm3_thread_create_join 0.019 1.592
Some of these are rather surprising results. This is JRuby running in plain old interpreted mode, with no compilation involved. The majority of the tests still have JRuby slower than Ruby 1.8, but the gap has narrowed an incredible amount since last year. In only a few tests are we more than twice as slow as MRI, and on a couple we're almost twice as fast. If you'll imagine startup time removed from these numbers, and believe me when I say Java takes far more than 30-60 seconds to rev up to full speed, then the situation looks even better. What's more, we've got a good several weeks before the first 1.0ish release is scheduled (something betaish or RCish that proudly proclaims it's "done") and a bunch of great committers and community members eyeing performance metrics.

Ok, you may be asking "what about JRuby's compiler?" Yes, there is a compiler in the works. It's primarily been my job to build out the compiler, though Ola has jumped in a few times to offer his excellent help. And progress has been slow but steady. You have to remember that in all the world, there's no known 100% complete Ruby compiler for a general-purpose VM. There's Ruby 1.9, but its bytecodes have been custom designed around Ruby. There's XRuby and Ruby.NET, but it's still unclear how complete they really are. So this is an open area of research and development. But the results are looking great so far.

For this test, both the ahead-of-time (AOT) compiler and the just-in-time (JIT) compiler modes are activated. This forces the target script to be compiled before execution and also compiles any methods hit heavily once execution gets going. The command amounts to the following:
JAVA_OPTS="-Djruby.jit.enabled=true -Xverify:none" /
jruby SERVER -O -C [script.rb]
  • -Djruby.jit.enabled=true enables the JIT compiler. The default threshold at which a (compilable) method gets compiled is 50 invocations.
  • -C tells JRuby to compile the target script before executing it. If the script can't be compiled, JRuby bombs out with an error.
The compiler can't handle all the Ruby 1.9 tests yet. Specifically, it doesn't handle multiple assignment (e.g. vm1_swap), exception handling (anything involving rescue or ensure), or full class definitions. But it compiles the majority of the tests.
TEST                    MRI     JRuby
--------------------------------------
app_factorial 0.029 3.459
app_fib 7.094 5.093
app_mandelbrot 2.340 9.011
app_strconcat 1.827 2.391
app_tak 9.714 5.394
app_tarai 7.515 4.642
loop_times 5.428 2.942
loop_whileloop 10.016 6.027
loop_whileloop2 2.012 2.191
so_ackermann 13.610 11.254
so_concatenate 2.043 2.396
so_lists 1.250 2.141
so_matrix 2.256 2.394
so_nested_loop 5.614 4.167
so_random 2.158 3.291
so_sieve 0.741 1.798
vm1_block 23.392 12.397
vm1_const 16.980 8.908
vm1_length 21.094 10.901
vm1_simplereturn 21.252 9.345
vm2_array 6.025 3.041
vm2_method 13.049 7.794
vm2_regexp 4.468 7.647
vm2_unif1 3.855 3.293
vm3_thread_create_join 0.017 1.389
Ahh, now things look a bit different! In almost every case, JRuby performs better than Ruby 1.8. In the long running cases, the difference is even more obvious. Short runs still put Ruby 1.8 ahead, but I'm totally ok with that. Java, and by extension JRuby, has never had stellar short-run and startup performance. But we don't really have to if the heavy, long-running apps people actually use end up running faster.

Note also that this is the first real compiler we've had; it's not doing any optimization like Ruby 1.9's and Rhino's compilers, and it's almost certainly far from being efficient. I'm no compiler expert, and this is my first real attempt. These numbers already looking so good demonstrates that there's a grand adventure ahead of us: Ruby really can be made to perform well on the JVM. It just requires a little confidence and a little more effort.

That pretty much wraps up this installment. The bottom line: As we approach JRuby 1.0, our performance is better than it's ever been--faster than Ruby 1.8 for many specific cases, with stellar across-the-board performance right around the corner. And with other implementations like Ruby 1.9, XRuby, Rubinius, and Ruby.NET rapidly coming of age, Ruby's future is looking extremely solid.

script type="text/ruby"

Remember way back when the IRB applet was first thrust upon the world, and I promised that you truly could now use Ruby in the browser? At the time, it was still a little cumbersome, and generally you could only use Ruby by passing it directly into the applet.

Well Dion Almaer has taken it to the next awesome step:

<script type="text/ruby">

Check out his blog entry on how to enable true Ruby scripting on the client side, as well as the link to his demo page (still loads up the applet in the background, but that's a minor implementation detail).

Awesome.

Friday, April 06, 2007

Paving the Road to JRuby 1.0: Unicode

Well friends, the countdown has begun. Within the next several weeks, we're looking at getting a 1.0 release out in either beta or RC form. Of course most of you OSS folks know how arbitrary release numbers are...the day after we branch 1.0 we're going to start committing to 1.1, and the cycle will continue. But it seems like the last year is coming to a very solid milestone, and these stabilization points are necessary once in a while. So it comes to pass.

As part of "closing the door" on 1.0, we're weighing features and bugs and wishes to decide what makes the cut and what must wait for a future release. To help get the word out, I'll be doing a series of entries on where we are, where we'll be for 1.0, and where we're going in the future. First off, one of the biggest issues for Ruby--Unicode support--and what it will look like in JRuby 1.0.

Unicode

Straddling two worlds is complicated. Ruby is dynamically typed, with open classes, redefinable methods, eval...a very different sort of world than Java. If those really obvious items weren't tricky enough, Ruby has another wrench to throw into the works: it cares not for Unicode.

Of all issues, this is probably the most problematic to solve. Java, as you know, is always unicode, representing strings internally as UTF-16 and supporting most external encodings you could ever possibly want. Ruby, on the other hand, pays little attention to encodings most of the time, preferring to just treat strings as an array of bytes. If it's encoded, so be it. If not, so be it. The majority of Ruby's string operations work with bytes (though there are a few smart exceptions), so multibyte-encoded strings can be damaged if you have high expectations.

We've spent the past year weighing options. We want JRuby to be compatible with the C implementation, since it's widely deployed and apps depend on its sometimes quirky string behavior. We also want JRuby to integrate well with Java, and the boundary to be as seamless as possible. How does one accomplish that? We've gone through a couple options:

  • At first, JRuby's string was represented with a String/StringBuffer internally. This allowed the original JRuby authors the most trivial path to a fully-working string implementation; almost all the heavy lifting was done for them, and for simple cases it worked fine. But the APIs did not conform to what Ruby applications expected, frequently returning 16 bit values for individual characters and reporting incorrect byte lengths for strings that couldn't encode into all 8-bit characters. It was broken, as far as Ruby code was concerned.
  • The second option, which is what we currently have, is to do what Ruby does...treat all strings as byte[], and implement all operations to the letter of the law. This allows us to make even the lowest-level APIs conform exactly, and to be honest that's been necessary to get applications like Rails up and running. Ruby blurs the distinction between a byte[] and a string so completely that Rails actually now ships with its own multibyte helper, a library that stands a good chance of becoming widely deployed. So to support Ruby and all its many wonderful apps, we fell in line.
At this point it's appropriate to answer a question many of you may have: If JRuby's strings are to be byte[]-based, how can you integrate with Java? A few answers have been proposed. The one I put forward some months ago was that we should treat Java strings like any other Java object, allowing developers to explicitly work with either Java strings or Ruby strings--never the twain meeting without full consent of both parties (or perhaps by using a few helper APIs). This allows Ruby's strings to work as you'd expect, and Java's strings to work as you'd expect, but it doesn't get the nicest flowthrough we really want in this case. So then there's a compromise option likely to be "it" for 1.0.

When crossing the boundary, Ruby will be assumed to use UTF-8 strings.

There are many benefits to this even beyond the fact that it took me 15 minutes to make it work. It allows Ruby strings to behave like Ruby apps expect them to. It allows Java strings to pass into and out of Ruby code without corruption. And it allows Ruby strings to pass into Java code with the only prerequisite (if you want to avoid garbled strings) being that they should be decodable UTF-8. That last item seems like a fairly reasonable requirement given that you're calling 100% unicode Java APIs, and in general the people that will care about this conversion are the ones already using unicode.

Of course, nobody calling these APIs would ever see this or have to do anything special...strings will pass back and forth and encode/decode as necessary, and things will generally just work. And there are performance tricks we can use to speed things up if that becomes a real issue. But it also does something no other proposed solution can: it solves the problem pretty darn well...in time for 1.0.

So for the record, this is the proposed solution we'll go with in the 1.0 timeframe:
  • Ruby strings are byte[] and conform to Ruby string semantics
  • Java strings passing into Ruby code will be encoded as UTF-8, with the implication that you should expect to be working with UTF-8 byte[] in the receiving code
  • Ruby strings passing out of Ruby into Java libraries will be assumed to be UTF-8, and the resulting string on the Java side of the call will reflect that assumption.
And what does the future hold? Well, there's a number of exciting areas happening post 1.0:
  • At some point, either before 1.0 or immediately after, there will be an installable gem that provides Java-native support for Rails' MultiByte library, which should provide a substantial performance boost there. As it is, the current pure Ruby version works almost 100%, so the extra performance should ultimately just be a bonus.
  • We will start implementing a Ruby 2.0-compatible string, but it's unclear when would be a good time to flip that switch. I would predict that JRuby users will have a choice of which to use fairly soon, and we'll swap out behavior depending on what you want.
  • It's also likely that we'll present clean ways to "just use Java strings" for developers that want to do that. This would not result in code that's compatible with stock Ruby, since we'd obviously have to "unicodify" many Ruby methods, but it fits into a larger goal of Ruby-enabling the Java platform as well as possible
  • And of course, some combination of these three and the current solution is possible, given some time and thought. Ultimately it will be driven by you, the users of JRuby, and what you want to see done with it. Because that's what we here at Sun are trying to be all about...what users and developers want to be doing.
So that about wraps up the unicode story for 1.0. I believe it's an excellent compromise between full Ruby compatibility and tight integration with the Java platform, and it gets us where we need to be right now. So what do you think?

Saturday, March 31, 2007

ActiveRecord 100%, Performance Doubling, Java Support Improving

I haven't blogged a solid JRuby update recently...and this one isn't going to be as solid as I'd like, but it should let you all know that the wheels are still moving and the train is picking up speed. It's astounding how much progress is being made.

ActiveRecord Now Fully Supported

It's good to have such incredible community members and great core team members.

Ola Bini, who recently accepted an offer from ThoughtWorks, has been hitting Rails unit tests hard. And the module with the most failures has always been ActiveRecord. It's the most complicated and the weakest point in the JRuby on Rails story.

Until now.

1031 tests, 3839 assertions, 0 failures, 0 errors
That headline should read "ActiveRecord Fully Supported on JRuby", for you press affiliates out there.

Of course there's a few caveats, but they're pretty minor in this case. That's zero failures and zero errors when running against MySQL, but it's certainly a de-facto standard in the Rails world. The next step is to get as many other databases to 0/0 as possible. And those are much smaller steps than this first one.

So what does this mean for JRuby on Rails users? Well in general, it means that the heart of your DB-driven JRoR apps--ActiveRecord--is now the best-supported piece of the puzzle. It also means that overall Rails support is well into the 99% range and still climbing.

Performance Continuing to Improve

In addition to the focus on getting Rails working well (which is really a focus on improving overall Ruby compatibility) we've been hitting performance much harder the past couple weeks. I especially have been splitting my time between interpreter and compiler work. As a result of all our efforts, the current trunk code is almost another 2X the performance of 0.9.8, released less than a month ago. The compiler also supports almost 2X the number of syntactic constructs it did in 0.9.8, and it's getting faster and more stable every day.

There are actually a number of benchmarks running faster than MRI (the C version of Ruby) even when interpreted, showing that we're finally making good on our promise to have interpreted JRuby run as fast as MRI. So it truly does seem that the compiler will be "bonus" performance once we work out the remaining bottlenecks in the system.

Java Support Looking and Feeling Better

There have been two areas of focus for Java support the past few months: rounding out features and syntax and improving performance. Both had some great jumps in JRuby 0.9.8, with a couple small fixes improving performance many times and with concrete/abstract class extension finally making it into a release.

Since 0.9.8 we've continued to make progress on both fronts. I've made a few additional small changes to improve overall performance, and Ola's been moving some especially critical pieces of code into Java, since they represented a lot of back-and-forth across that barrier. Where calling into Java code was previously as bad as twenty times slower than calling Ruby code, it's now a modest two times slower. We expect to improve that further, getting as close to Ruby invocation speed as possible.

We've also just added what should hopefully be the final revision to our various "import" syntaxes. Oddly enough, we've come full circle to "import":

# create a new constant "System" pointing at it
import java.lang.System

# use Java::syntax for less-common packages
import Java::my.weird.package.MyClass
And we're considering some improvements to how multiple classes are imported and how to specify an alternate constant name (for classes like String, which would be a bad idea to overwrite).

You can expect to see this all solidifying as we run these last miles toward JRuby 1.0.

--

I will blog more on all this as we get into April, but it's truly shaping up to be Springtime for JRuby in JavaLand.

Friday, March 30, 2007

Can Your Ruby Do This?

# scripting.rb

include Scriptable

x = 'Hello'
y = 'world'

groovy <<G
def z = 'multilanguage'
println("${x}, ${z} ${y}")
G


javascript <<JS
z = 'multilanguage'
println(x + ', ' + z + ' ' + y)
JS


# both => "Hello, multilanguage world!"

Saturday, March 10, 2007

Ruby on Grails? Why the hell not?

I've spent some time this weekend looking over the Grails codebase. After a truly gigantic checkout (Grails bundles production JARs for all the libraries it integrates) and after swimming through a substantial, single-project sea of source files, I think I'm starting to follow it (though a tour by someone familiar with Grails internals would be nice). But there's something else I've learned in the process...

There's really no reason this couldn't use Ruby just as easily as Groovy.

Let's look at some really dumb numbers. Grails, as it turns out, is actually over 2/3 Java code, unlike Rails which is 100% Ruby. Grails contains around 436 Java source files for about 54kloc of code and 273 Groovy source files for about 23kloc of code. So Java outnumbers Groovy by a 2:1 ratio. And yes, Java's probably more verbose than Groovy in most cases. But my point stands, and I'll back it up with more...

Then there's the libraries. The Grails binary release weighs in at a whopping 26MB compressed and 49MB uncompressed (*wow*). But 31MB of that is third-party libraries completely independent of Groovy, things like Hibernate, XFire, Quartz, Apache Commons, and so on. Almost all of which (or perhaps all of which) are pure Java code. So the ratio is even more heavily toward Java.

These numbers and my exploration of the code base lead me to a surprising conclusion: Grails is not a "Groovy on Rails" by any means. It's a large number of popular, well-established Java libraries stitched together mostly by Java code with a thin layer at the front (20kloc is not much) of Groovy code for end-users to see and work with. And I guarantee you it's not the Groovy part that's most interesting here. Hell, you could write that part in Java if you followed enough "convention over configuration", and you certainly could write it in Ruby.

I decided to do this exploration after realizing there's nothing about Ruby or JRuby that couldn't be used to wire a bunch of Java libraries together--indeed, that's one of the biggest selling points of JRuby, the fact that you can write plain old Ruby code but call Java libraries almost seamlessly (with day-by-day improving integration points and performance numbers). So why not take Grails and do a port to Ruby? Groovy code isn't far off from Ruby in many ways, and everything you can do in Groovy you can do in Ruby (plus more), so the port should be pretty straightforward, right?

Then I find out that Grails is mostly Java code. And instead of being annoyed or repulsed by this revelation, I was intrigued. Why not make Grails work with Ruby as well? And really, why not make it work with any scripting language? Why limit that nicely-integrated back end by forcing everyone to use Groovy, regardless of preference? Have we learned nothing from a decade of "100% Pure Java"?

So I propose the following.

  • I believe it would be possible to completely isolate Grails' tastier bits from Groovy. I don't claim it would be easy, but Grails is fairly well interfacified, injectificated, and abstractilicious, so making that dividing line clear and bold is certainly possible (and heck, that's the point of dependency injection and component frameworks anyway, isn't it?). Really, I could start implementing a few of those interfaces right now with Ruby code, but that's not the right approach.
  • Grails' value is not in Groovy, but in the clean integration of established libraries behind the "Convention Over Configuration" mantra. There's nothing specifically Groovy (or Ruby) about that religion, and there's nothing Groovy does in Grails that another JVM language couldn't do just as easily. This is *truth*. And Grails would benefit tremendously by expanding to other languages. Being tied to Groovy alone will hold it back (just as a framework tied to any one language will be held back, Ruby included).
  • I also believe that if the Grails project doesn't make an official effort in this direction, others are likely to take up that mantle themselves. Though the debates will forever rage about which JVM dynlang should carry us into the future, the same facts that have guided programming for half a century still apply today: No one language will be enough to carry us forward; no languages will survive indefinitely; and the language you'll use in ten years you've probably not even heard of yet. Did anyone expect Java to be the most widely-used, widely-deployed language ten years ago? Well today, it is. It won't always be.
  • There's also a very interesting angle here for language enthusiasts. Nobody doubts that the Java platform is officially becoming multi-lingual, possibly a complete reversal from years past. But there are going to be growing pains here, and we're already feeling them on some projects at Sun. We have this wonderful platform with all these wonderful languages...and they all do things differently. We have a first stab at official "Scripting" support in JSR-223, but it only defines the integration point from Java to the language in question...only partially defining the integration points for those languages calling back to Java (a tricky problem) and defining *nothing* related to making those languages interoperate with each other. Again, Java becomes the integration point, and the bottleneck (as in "how do you pass a dynamic-typed object from one language to another by sending it through a statically typed language?"). The very exercise of making Grails (or any other framework) work equally well across multiple user-facing language interfaces will require better interoperability and more clearly-defined integration points for dynamic and static languages on the JVM.
  • The Phobos guys at Sun are already tackling many of these same problems while expanding their JavaScript-mostly Rails-inspired web framework to other JSR-223 languages (notably, Ruby). So anyone taking on the Grails effort would certainly not be alone, and there are some damn smart engineers at Sun interested in tackling these very same problems.
And I guess in the end it's a question of how visionary we all can be. Can the Grails team envision a future for Grails that doesn't require the use of Groovy? Are you (and am I) as a non-Groovy developer willing to help them get there? And in the end, isn't this what thought leaders on a multi-lingual platform are supposed to be doing?

--

So, enough of the inflammatory bull-baiting. I dare you to tell me why I'm wrong here. Even better, I dare you to look into it yourself. 75kloc of code, only a third of it Groovy. Established libraries you're sure to have used at some point. Clean integration, following the principals inspired and proven by Rails.

But fronted with your language of choice.

Make it so.

Tuesday, March 06, 2007

NetBeans 6 Ruby Support Even Better Than Expected

See, here's the deal. I'm not a very advanced IDE user. I used Eclipse for years and JDE before that, and I wouldn't ever have considered myself a "pro" with either. I learned what I needed to get the job done and not a whole lot else. Beyond that I never knew some of the coolest features, and was never so good at convincing people to make a switch. This actually has helped my migration to NetBeans, since it basically does all the things I was used to in Eclipse. But it has been problematic when I try to convey how cool the new NetBeans Ruby support really is.

It's super cool. It does things I've not seen any other IDE or editor do.

However, it's best not to take my advice, and Roumen Strobl has recorded two seriously excellent demonstrations of Rails and Ruby support in NetBeans. They make the sale way better than I could (though I now have many good ideas for how to sell it better in the future).

If you have any interest in Ruby editors or IDEs, you should take the time and watch these, especially the Ruby support.

Two Demos: JRuby on Rails and Advanced Ruby Editing in NetBeans!

Behind The Scenes: JRuby 0.9.8 Released!

Things have been moving quickly in JRuby-land, and we've just kicked out a major release in JRuby 0.9.8. Among the big features:

  • Ruby classes can extend concrete/abstract Java classes and override methods
  • New Java primitive array syntax
  • Reimplementation of String, Numeric classes, and Array to be more correct and performant
  • Significant bottlenecks have been identified. In some cases IO is 6.5x faster than previous releases. Java included classes are significantly faster than in the past.
  • 220 Jira issues resolved since last release
Now these are certainly delicious. Performance improvements are always great, and we're been improving Java integration little by little. But there's another bullet I omitted that is worth a little more attention:
  • Ruby on Rails support
Now you may feel free to read that as you wish. I read it as "we expect pure-Ruby Rails apps to work." By "pure Ruby", I mean no unsupported C-language extensions required. By "work" I mean "work."

It was our original plan over two months ago that by Februrary's end we'd be passing enough of Rails' own test cases to call it "supported". And in a relative rarity for an open source project, we hit that milestone. It was actually early last week we started saying we officially support Rails, which at the time meant better than 95% of Rails test cases passing. By the time we cleaned up the remaining release items, we had passed 98%. And we're still climbing.

So what does this mean for a practical Rails user? Well, it means that the dream of deploying Rails apps on any Java-based server in any Java-based organization is another big step closer to coming true. I've actually been demoing Rails apps running in a WAR file on GlassFish for all my recent talks, and it's pretty solid. I don't imagine it's perfect, of course, but it's looking really very good. It will be "ready" by the time we hit 1.0.

A few folks have been wondering about the jump from JRuby 0.9.2 to JRuby 0.9.8. The simple answer is this: we're two releases away from 1.0. We wanted to make it obvious how much work has gone into 0.9.8 (as many SVN revisions in this release as the last three combined) and show our commitment to getting a 1.0 release out very soon. The 0.9.8 release will be followed by 0.9.9, which will be followed by...well, you get the picture. We're in the home stretch now.

The other core team members have reported on the release, so I won't cover all that. Here's a list of links:

Tom Enebo
Ola Bini
Nick Sieger

In related news, there's also a new release of ActiveRecord-JDBC, the gem-installable module enabling JDBC databases to be used by ActiveRecord. This is also very welcome, since there have been a number of bug fixes in trunk that weren't easily installable the past couple months. Now you should be able to run both Rails 1.2.2 and 1.1.6 well with JRuby, just using released code. It helps complete the puzzle.

So what's next for JRuby? Well, with the 0.9.8 release we've reached a really big milestone as far as Ruby compatibility, so we're likely to turn our attentions toward areas not directly related to Ruby 1.8. Specifically:
  • Work will continue on the bytecode compiler, and I'm hoping to have the JIT permanently enabled within the next week
  • Java integration will get a heavier focus, to start bringing us more in line with languages like Groovy for ease-of-integration. In general things are looking pretty solid right now, but we know there's a lot of work to do here. I'd like for us to come as close as possible to Groovy's level of tight integration as possible.
  • We will also start working on native Unicode support, by exposing a Chars class like the one provided in Rails' MultiByte library. This will allow us to maintain compatibility with Ruby and provide solid Unicode support without introducing an incompatible set of features.
  • Both Tom and Ola have expressed an interest in continuing work on our Ruby 2.0 bytecode engine. Ola would like to continue improving and expanding the bytecode support, while Tom would like to play with a second parser that goes straight to bytecode. Both of those will help feed into a potentially faster interpreted mode in the future, giving us many of the perf gains shown in Ruby 1.9 benchmarks.
For me personally, performance is going to be the big item. The recent Ruby Shootout showed that we have some work to do to bring out performance up to MRI standards, but it didn't tell the whole story. The truth of the matter is more like this:
  • JRuby interpreted mode is roughly 3-4x as slow as Ruby 1.8
  • JRuby compiled mode is roughly 1.5-2x faster than Ruby 1.8
  • KRI (Ruby 1.9, "Koichi's Ruby Implementation") is many times faster than MRI for the posted benchmarks
  • KRI is 2x or less faster than MRI for the general case
And this is without us doing any optimization of the compiler or adding in any special-case optimizations like KRI provides for fixnums. We're still looking great for future performance, and I want to start tackling this area heavily over the next couple months.

As always, we welcome contributors, bug reports, patches, and user anecdotes for JRuby. We love to hear about bugs, we want to know your opinion, and we respect and honor our community whenever we get the chance. Remember, JRuby is your project, and can only be successful with your help.

Visit www.jruby.org to learn more about the project, download the release, and get involved!

On to the next release!

Thursday, March 01, 2007

JRuby World Tour: Bangalore

After my adventures in Hyderabad, the next stop on the tour took me to the heart of India's IT industry, where I would present my TechDays talk again for Sun Microsystems' Indian Engineering Center. But I scheduled in a little free time as well, to do some exploring.

I arrived on Saturday the 24th after a short flight on Sahara Air. For once, my travel arrangements worked out, and I was able to buy my ticket upon arriving at the Hyderabad airport. A few hours later, I had landed in Bangalore. A hotel car was waiting for me (this is now only the second time I've had a car waiting with my name on a sign, and I think I like it), so I headed straightaway to the Richmond Hotel in central Bangalore.

The Richmond has a fairly old-world feel, apparently having been converted from an apartment or office building some time in the past. It had a musty smell, but was otherwise pretty clean and well-kept. I'm generally more comfortable in "clean and comfortable" hotels than in "pristine" hotels like the Novotel, so the Richmond was just fine. That night I made arrangements for the next day's tour, ordered in some room service, and caught up on sleep.

It still amazes me how everything has changed since blogging became mainstream. Even as short as five years ago, I had a devil of a time finding local tour guides for my travels. Generally the best bet was to fork over too much money to a paid guide or to travel with a tour group. Neither of those are really conducive to free-form exploration, so I've always been forced to strike out on my own. But those days are certainly behind us.

In response to my "World Tour" blog posts, I had no less than 5 different people offer to show me around Bangalore. They ranged from JRuby enthusiasts to casual readers, and for once I had more guide offers than I could use. So I settled on the first two to contact me: K "Venkat" Venkatsubramaniyan and Håkan Raberg.

Venkat was up first on Sunday the 25th. He had actually stopped into the JRuby IRC channel (#jruby on FreeNode) as well as emailing me. His plan for the day included a trip to a cultural/folk center outside the city, lunch and dinner at a couple places to be determined, and shopping stops for a few things I wanted to purchase. So we started out bright and early at 9AM.

We made the following stops, in order:

  • Rajarajeshwari Temple in western Bangalore, where I paid my respects and was gifted with a wreath of flowers and a tilak on my forehead. It was a moving experience, even though I felt a bit out of place in my western clothes. And the classical architecture of this modern shrine was quite impressive.
  • Janapada Loka (Folk-Cultural World), a collection of folk art museums and exihibits on the road from Bangalore to Mysore.
  • Kamath Yathrinivas Restaurant, where I had my first traditional Indian meal, complete with banana-leaf plate and eating with my hands. It was very tasty.
  • Nalli Silk Arcade, to purchase sarees for my wife. I think I was the only guy in there shopping, other than a few poor saps who were obviously dragged along by their wives.
  • Cauvery Crafts Emporium on Mahatma Ghandi Road, a nice collection of handicrafts, clothes, and nick-nacks, but very much tailored to western tourists. I looked at pearls for my wife briefly here, but decided not to buy.
  • Navaratna Jeweleries, up the road from Cauvery, where I found the pearls I wanted. I went back on Tuesday before flying home to have the string extended and a clasp put on.
  • Garuda Mall and Bangalore Central Mall...we just kinda ended up here looking for a place to buy tea and wasting time before our evening's restaurant opened.
  • Amaravathi Restaurant, another more traditional place (more banana leaves and eating with hands), but this time with some delicious spicy curries and a crab dish as tasty as it was difficult to eat.
Venkat was an excellent guide, and I can certainly claim a new friend in Bangalore.

Monday the 26th was my day on the spot at Sun Microsystems. Conveniently, Sun's office in Bangalore was only about two blocks from the Richmond, so I headed over at around 10AM to prepare for my 11AM talk.

For some reason, every Sun office I visit is a terrific maze of doors, offices, and cubicals. The Menlo Park office has all L-shaped buildings with multiple intersecting hallways. Santa Clara's offices are plus-shaped (I think) which is often even more confusing. The Bangalore office put them to shame. It is hosted on the 4th, 5th, 6th, and 7th floors of a circular building with a central unsecured walkway and multiple secure entries and exits as you progress around the outer ring of offices. What complicated things most was that hallways extended out from the center of the ring like spokes of a wheel, but the perpendicular hallways did not always curve to match the circle. This meant that hallways often met at bizarre angles, and it was impossible to maintain any sense of direction. I got lost more than once wandering around, and unlike the California offices, I had no guarantee that following a given hallway would eventually lead me to an outside wall. Bizarre.

Anyway, I did the same talk from TechDays for Sun folks from 11AM to 12PM. Unfortunately, I had lost favor with the demo gods. My NetBeans demonstration froze up and refused to come back. My Rails demo failed with an error I still haven't isolated. And because of a peculiar scheduling snafu, the last 15 minutes of my talk had to be condensed into five minutes, resulting in more errors and a very flustered presentation. What had gone perfectly a few days earlier at TechDays went very poorly at Sun. Luckily, it seems to have still been well-received, and the classic JIRB demo was enough to wow the crowd.

The rest of the day was spent meeting with various folks at the IEC, talking about their projects and how JRuby could help. There's some cool stuff coming out of Sun India, so keep your eyes and ears open over the next few months.

Monday night my new friend Hakan Raberg from ThoughtWorks arranged to meet with me and two other ThoughtWorkers for dinner. It turns out Hakan is an old friend of Anders Bengsston, one of the premier JRuby contributors in the old days before I joined the project. Such a small world.

As a nice change of pace, the ThoughtWorkers and I had dinner at Barbeque Nation, a trendy, modern-feeling restaurant featuring fairly traditional northern Indian cuisine. In other words, lots of meat. Barbeque Nation is one of those "cook it yourself" places, where the center of the table is a small charcoal stove and the meats are delivered mostly-cooked on metal skewers. After marinating with a choice of marinades and allowing the food to cook the last 10% of the way, a delicious entree results. To make it even more acceptable to a western eater: it's all-you-can-eat, with skewers continually delivered until you tell them to stop. For the first time in a week, I was stuffed. It's very hard to turn down delicious seasoned and grilled meats when they keep arriving in front of you.

The ThoughtWorkers also had a number of interesting projects going on, many related to Ruby and a couple likely to use JRuby in the near future. Hakan had even worked on a code-generated implementation of the JRuby selector table's switch statements, which he promised to tidy up and send me. It could be very useful for us to optimize user-created classes in the same way we're starting to optimize core classes, and it was actually a technique we considered doing ourselves. More on that as it develops.

Tuesday, I packed up and checked out of the hotel. I was a little sad to be leaving Bangalore, but since I still had an entire day before my flight I walked up to M.G. Road to take care of some last minute souvenir shopping. My first stop was at Navaratna Jewelers for the modifications to my wife's new pearl necklace I mentioned earlier. While I waited for the work to be completed, I visited a few shops off the main boulevard and purchased the following items to bring home:
  • A small wooden Ganesha (about 4" tall), to bring success and prosperity to my office.
  • A small wooden Emblem of India, atop a 4" wooden column with a felt base. It is a perfect reminder of my Indian adventure (and probably the coolest national emblem I know). This is my favorite souvenir by far.
  • A small wooden puzzle box for my son.
  • Two larger versions of the puzzle-box with hand-painted images of Indian women on the tops.
I had also previously purchased the following items:
  • Two silk ties with different images of dancing elephants on them.
  • Two painted clay hippopotamuses, as gifts for whoever.
  • A comic/cartoon book of stories of "Heroes of the Mahabharata", for my boy to read.
For lunch, I was not feeling particularly adventurous, and also decided something familiar might be safest before 24 hours of plane flights, so I ate at Pizza Hut. Yeah, I know. It wasn't even that good.

Comfortably full from lunch and loaded down with all my new possessions, I returned to the hotel to catch a cab to the airport. Goodbye, Bangalore...or so I thought.

Bangalore Airport was to be a baffling ordeal. People were lined up for a solid block to get into the terminal, mostly encumbered with a half-dozen bags each. To confuse even more, there was an option whereby non-flying visitors could pay for admission to the terminal. Luckily, I figured out I didn't have to pay, and after muscling my way to the entrance, I moved on to the second challenge in this travel gauntlet: Thai Airways had no reservation for me.

My original tickets to India were incorrectly issued to "Charles Onutter", an unfortunate combination of my middle initial and last name. Normally, this wouldn't have been a big deal, but apparently international flights are extremely strict about the name for which tickets are issued. If it doesn't match your passport exactly, you're out of luck and can't check in. In a flurry of activity the day before I left, I managed to arrange new tickets. But because it was such short notice, I would have to buy two of my tickets at the originating airports: Sahara Air from Hyderabad to Bangalore and Thai Airways from Bangalore to Bangkok. Sahara air went fine...either because they found my reservation or because they had plenty of room on the flight. But Thai Airways had cancelled my reservation because they overbooked the flight.

Luckily, after talking to the ticket agents and American Express's emergency travel service, I was able to purchase an unused seat for the flight. I would fly first-class to Bangkok. I rushed through security, ran to the gate, and finally settled into my seat. Onward to the next stop in the tour: Bangkok, Thailand!

Friday, February 23, 2007

The Great Hyderabad Adventure

I have many pictures to share, but they will have to wait for an updated post. Allow your imagination to escort you for now.

So! I had been trapped in the "green zone" at the Novotel Hyderabad for two solid days, and I think I just finally had enough of it. The Novotel is surrounded by about five acres of land, and the edge of that property terminates with a heavy fence (or wall in places) with either barbed wire or razor wire keeping the rabble out...or perhaps keeping the westerners in? I had taken a few strolls on the grounds, and after walking a few hundred meters from one end to the other this morning, I realized I was tired of living in the bubble. It was time to leave.

A number of folks had expressed interest in escorting me into and around Hyderabad, but nothing materialized. Since this was my last chance to see the city, and potentially the last time I'd be in Hyderabad for many years, I had to take matters into my own hands. I asked the front desk for a map, a guidebook, and a few recommendations. Then the transportation desk set up a cab ride to my first destination, Charminar, the square, four-towered heart of the city. It was a perfect if clichéd place to start my day of exploration.

Unfortunately, Charminar is also right in the heart of the "old city", the other half of Hyderabad housing the less privileged portion of the population. Charminar itself was certainly astounding. Even though the minarets were closed, the view from the top main floor was quite impressive. In each of the four directions, thoroughfares stretched off into the distance, revealing a bustling mass of humanity. I could see the swarming rickshaws, the teeming bazaar, and the hoards of Hyderabadi citizens going about their lives. It was humbling really. But it was a bit more personal once I descended back into those masses to find my next destination.

India has truly awakened. Here more than anywhere I have visited the contrast between technology and tradition is grossly apparent. Construction projects involve teams of mean working by hand, like ants assembling a colony. But every ant has a cell phone with text messaging and a camera. In the streets, beggars clutch at you constantly, but often speak extremely capable English and understand the western world. Above, signs advertise BizTalk Server and Oracle Training. Below, people sleep in the streets and beg for pennies in roaring traffic. And in the streets of Hyderabad this paradoxical turmoil boils unabated.

So I climbed back down the stairs of Charminar, a bit concerned for my future. Walking out the secured gate, I was immediately mobbed by beggars, young and old. A few (usually the youngest) spoke surprisingly good English. They asked where I was from, what I needed, where I was going. Some (the older members of the hoard) only knew what they needed to elicit a response from western hearts and minds..."food"..."baby"..."hungry"..."please". Why exactly had I decided to launch into this city alone? Ahh, for adventure, I remember. And what happens at this point in an adventure? When the odds and numbers are stacked against you? A gallant escape, with nary a rupee spared. How noble of me.

No, I did not make a contribution to the Hyderabadi poor. I certainly could have. Is it heartless of me? I don't think so; to have made one contribution would have certainly brought a torrent upon me, and it was already a worrisome situation. Instead, I made for a nest of rickshaws near the monument. One particularly bold youth, Abdul, stepped out of the crowd with a map of monuments, a plan for visiting them, and a welcoming smile. He was a salesman with exactly the product I needed, and for the next six hours he would be my guide. Off we went.

One of the previous would-be escorts had suggested Golkonda Fort as a place I should not miss. So naturally, with a friendly and aware guide at my side, that was the next place to visit.

Golkonda is the ruined former home of the Qutub Shahis, before they moved their home to what is now Hyderabad. It is an extensive maze of towers, walls, tunnels, temples, mosques, and overlooks atop a the highest hill in the area. After climbing several hundred feet to the summit, the sprawl of the city extends in all directions. No other view in Hyderabad gives such a complete picture of its size and extent. The fort itself is also impressive, with four-story defensive walls, a remarkable acoustic communication system, and numerous gardens tucked away behind tunnels and through towering arches. The climb to the top required a few breaks, during which Abdul told me what he knew of the fort's history in broken English and snapped photos of me at the major landmarks.

Eventually, we descended, and headed for the next stop on the tour: the Qutub Shahi Tombs.

There were seven Qutub Shahi kings, and they are all interned here in monumental tombs. Each tomb hosts an enormous dome (complete with perfect acoustics for singing, chanting, or tourists clapping and hooting), multiple entrances and side-tunnels, and inside the center, a symbolic (or perhaps more-than-symbolic?) monument to the hosted monarch. Most of the tombs contain a single such monument, but one had three: mother, father, and child. There are tombs here for the seven kings, many of their wives, and other relations. The writing on the monuments is in Arabic. That on the tour signs, in Telugu. Both Hindi and Muslim visit and pay their respects. And the city surrounds. I would say that the Qutub Shahi Tombs are at least as important a stop as the Golkonda Fort. If you are to visit the height of the kings' success, you should at least say an appropriate goodbye.

The rest of my adventure is somewhat less compelling, but perhaps still interesting. Hyderabad as we know it in the western world is really two cities to the Indians: Hyderabad and Secunderabad. In a peculiar quirk of fate, this pair is known to most Indians as "The Twin Cities", just like my home towns of Minneapolis and St Paul are known to Minnesotans (and many Americans) as "The Twin Cities". The very center of Hyderabad, bridging these two cities, is a large lake called Hussein Sagal. Arriving here, I have come full-circle to the heart of the "Twin Cities" on the other side of the world. Unfortunately, the interesting part of that story ends here. Hussein Sagal now appears to be little more than a tourist trap, firmly ensconced in the "new" part of the city. It is surrounded by miraculous science museums and modern family amusement parks. Put simply, I had no interest in anything more than driving past. Yes, it's a large lake in the middle of the city. Yes, there's a 60-foot-tall Buddha standing in the center. The noise of the traffic surrounding and the lights of the shopping malls and Pepsi signs tends to dampen any beauty or majesty that once was there. We moved on.

One item I had expressed interest in when talking to the front desk was "shopping". Unfortunately, we had different definitions of shopping. To me, shopping was visiting a "bazaar of the people", where I could browse sarees for my wife, hand-made toys for my son, and myriad other handicraft and localities. To the hotel, shopping meant "Macy's".

360s, $1 The final stop on my 8-hour Hyderabad tour was, sadly, the LifeStyle department store. It wasn't a bad store, really, since it did host a few local items and the prices were a little better than those at home. But it was a truly western-style store, with little interest to me as anything other than a landing zone. I wandered up to its second story, where I found discounted XBoxVCDs, and a coffee shop so grossly plastered with Windows Vista branding that they even had a "Windows Logo" menu prouding selling "Windows Vista WOWffee". Naturally, I ordered one due to the sheer absurdity of getting a free "Vista Wristlet", a white rubber bracelet emblazoned with the words "Windows Vista. The "Wow" starts here." To make it even more stomach-turning, it was a latte with orange flavoring. Delicious.

Of course I couldn't let the day end with a "WOWffee" and high-quality merchandise at low, low prices, so I called a fellow Sunny from the hotel to join me for a very satisfactory dinner. And then, after consuming excessive amounts of vegetable biriyani, chicken tikka masala, and garlic naan, I returned to the "green zone".

So what have I learned from my excursion?

  1. Exploring the poor districts of an unknown city alone can be frightening, but it certainly gives one perspective on the meaningless nonsense we labor over in the western world.
  2. Don't wait for someone to show you the world; just go see it yourself.
  3. The world is disappearing...so you better see it soon.
I hope I can return to Hyderabad some day to see more of its hidden treasures. For now, I move on to the next JRuby World Tour Stop: BANGALORE.

Wednesday, February 21, 2007

Tech Days Talk a Success

I just completed my talk at Tech Days Hyderabad, and I think it was resounding success. If I heard right, the total head count for my "JRuby Essentials" talk was over 1200 people, by far the largest crowd I've presented for. There was a hoard of questioners immediately after the talk, who followed me into the hall to keep asking. I gave out my entire in-pocket batch of business cards, and even received a round of applause after the JRuby on Rails demo. All this even after my talk was delayed and cut to 40 minutes because the earlier talks ran too long. It was a sprint, but I think it went very well.

I will be posting the slides, but they're basically the same content many of you have probably seen the past couple months. For those who haven't had the pleasure, we'll start posting slides more regularly, and I'll try to blog a few example walkthroughs for those of you who want to duplicate them for your own talks. Feel free to steal it all and spread the word.

Tuesday, February 20, 2007

JRuby World Tour: Hyderabad

Success! It is now 3:00AM in Hyderabad India, and I have checked into the Novotel in HiTec City. The flight was uneventful; I got my demos basically working and then slept the majority of the time. The immigration and customs at the airport were perhaps the least threatening of any I've seen. Immigrations basically just looked for my visa, took my arrival card, and stamped my passport. Customs was little more than a big white X-ray machine with "CUSTOMS" printed on a white piece of paper taped to the front.

The smooth arrival was just what the doctor ordered. The reason I'm on this round-the-globe trip is because my original tickets to India were issued to the wrong name: Charles O'Nutter instead of Charles (O) Nutter. I never noticed the slip-up on the itinerary (nor would I ever have recognized it as a slip-up, since the output was pretty mainframe-ish), and only upon arriving at the airport in Minneapolis was I told I could not check in. Twenty-four hours and a lot of scrambling later, I had new tickets issued on the only flights available, hence my return trip heading east via Bangkok and Tokyo. But aside from that initial snafu, the trip has been pretty routine. Saturday will be interesting...my ticket to Bangalore has to be purchased in-person, so there's great potential for more headaches. But I can take it in stride, and I'm sure things will work out well in the end.

Hyderabad is about what I expected, and I'm looking forward to getting into the city one of these three days before I leave for Bangalore. It feels very similar to Beijing, with old meeting new and construction everywhere. The city looks very appealing; street-level shops line almost every sidewalk, there's green plant life everywhere, and everything has sort of a dusty, dingy, comfortable feel. It's not dirty, don't get me wrong...just dusty, like any city kicking earth into the air with a thousand construction projects might be. The auto-rickshaws (three-wheeled rickshaw-looking things with small engines) are pervasive; I can imagine that during the day they swarm and buzz like bees. They drive on the British side of the road, which isn't much of a surprise, really.

It's really unfortunate when traveling to more exotic places like this that I have to check into a tidy, pristine western-style hotel. I'd rather stay in the midst of the city, where I can walk out the front door into daily Hyderabadian life. I doubt I'll be at the hotel for all three days of the conference...there's exploring to be done.

If you're located in Hyderabad or Bangalore and might be able to show me around a day this week (Thursday or Friday for Hyderabad) or next Sunday or Tuesday (Bangalore) I'd appreciate it. I'm not put off by crowds of people, even if many don't speak my language, but knowing the hot spots for shopping and eating makes exploring a bit more productive.

I give my presentation in just over 12 hours, so I'm going to try to settle in with a nice Kingfisher lager beer and relax. The conference schedule has things starting up in about 6 hours, so I should be fine for sleep and fairly well-delagged by then.

On the plane, I managed to duplicate the rails-integration WARfile-based deployment of a simple webapp, but I was unable to get an embedded Derby DB to work with it. I'm sure it's a matter of permissions, connection pooling, and such. I may or may not have time to get that working by tomorrow, but if I demo the basic app working with WEBrick and then WAR it up and show the non-DB stuff working in GlassFish, I think the point will have been made. I must also keep reminding myself that my talk is only 50 minutes now. I can fill 50 minutes without batting an eye. So we'll see if there's even time to get to GlassFish, since it eats up a good five minutes building and deploying the app from scratch.

JRuby World Tour: Amsterdam

I'm now relaxing at Schiphol Airport, where I've just plunked down my 10 euros for internet access. After a light breakfast, my current task is now to ensure demos are ready and working for TechDays. I discovered a bit ago that the Rails Integration project uses maven to build, and so I need internet access for that first install command (to pull down required dependencies). I had worried a bit about finding internet access, but this connection appears to be pretty good. I've managed to build the Rails Integration module, and hopefully I'll be able to get Rails-in-a-WARfile working in the next few hours.

If I discover anything interesting in the process, I'll post it. From what I've observed, the rails-integration guys and Ashish have had lots of success lately.

If you happen to be at Schiphol before I depart at 11:55, I'm in Lounge 2 near the brasserie. I'm the one with the MacBook Pro, fighting jetlag and overlooking the concourse.

The next stop will be Hyderabad. My 11:55 flight departs from gate F8.

Update: I managed to get Rails up and running nicely in a WAR file. Huzzah! I'll play with it a bit on the plane and get our usual Rails demo to feed directly into "now we'll make it a WAR file". Thanks much to the rails-integration guys for putting together a really slick piece of work. Gotta love this whole open-source thing.

Side note: jet lag is a weird feeling. Right now it's 9:49 in Amsterdam and around 14:19 in Hyderabad, so I have to pretend it's mid-afternoon. My flight will board in about two hours, and toward the end of the flight I'll have to pretend it's evening and start getting a little sleep. It's quite unfortunate that I'm arriving in Hyderabad at 1:20AM on the day I'm presenting, but that's how it goes!

Sunday, February 18, 2007

The JRuby WORLD TOUR 2007

Yes, you read that right! I'm announcing my intent to circumnavigate the globe in only NINE DAYS spreading JRuby and joy at every stop. The Earth shall be wrapped in JRuby goodness!

To celebrate this monumental occasion, I'm inviting all developers with an interest in Ruby or Java to join the JRuby mailing lists and volunteer your services. Eternal gratitude and temporary fame could be yours, for the small price of bug reports, bug fixes, or high-performance rewrites of core JRuby libraries (it's so easy!). Operators are standing by to receive your emails and direct you to the promised land!

Now, on to the tour!

First Stop: Amsterdam, Netherlands

The first leg of my journey takes me from my home in Minneapolis, Minnesota to beautiful Amsterdam. There I will enjoy an delicious airport breakfast followed by a five-hour tour of the terminal. I will be available for autograph signing from 7:00 to 11:30 by appointment only. The first annual JRuby New-Age Concert of Magic will follow from 11:30 to 11:35. Tickets are first-come, first-served; cash only, please! I will depart from Amsterdam at 11:55 via a chartered jet I'm generously sharing with three-hundred other passengers and a small KLM Dutch Airways flight crew, en route to exotic Hyderabad, India!

Second Stop: Hyderabad, India

The next part of the JRuby "World Domination Tour 2007" takes me to Sun's Tech Days event in Hyderabad. In addition to my JRuby session, Tech Days will host such scintillating topics as "iPod Giveaway" and "Java Jacket Giveaway". But on Wednesday the 21st at 3:35PM you too can learn why JRuby is the programming alchemist's "Developer's Stone", transmuting static lead into dynamic gold. The topics covered will be exactly like those in recent JRuby talks, except cooler, faster, and after 26 hours of non-stop travel and five hours of sleep. Prepare for a punchy, laugh-riotous affair! Will I be able to maintain 90WPM during my interactive demonstrations? Will I remember that "alias" takes two arguments *without* a comma and with the original method first? Join me for what's sure to be 50 minutes you'll remember the rest of your life!

Third Stop: Bangalore, India

From Hyderabad, I take a short one-hour flight to Bangalore, home to Sun Microsystems India and the third leg of the JRuby Globe-trotting Festival of Light! On Monday, February 23rd, I will present JRuby to my tropical counterparts on the opposite side of the earth, featuring the exact same topics from Tech Days...in RANDOM ORDER. You never know what I'm going to do next.

I actually have all that Sunday free and Tuesday up until about 8:00 free to do some exploring. Event suggestions are welcome, and I challenge any of you to find food too spicy for me to eat! (impossible, I say!)

Fourth Stop: Bangkok, Thailand

The next step takes me from Bangalore to beautiful Thailand, jewel of Southeast Asia and home to one of my favorite cuisines. I will be available from 4:00AM to 5:30AM for a special, once-of-a-lifetime event I'm calling "JRuby Dawn at Bangkok Airport". And the great question on everyone's lips will be foremost on my mind:

Will the airport's Thai restaurants open before I board my next flight at 6:00? Stay Tuned!

Fifth Stop: Tokyo, Japan

Continuing the Asian leg of the tour, I'll spend 80 fun-filled minutes exploring the international terminal in Narita, only an hour's train ride from the Emperor's Palace! Naturally I'll be available for handshakes and baby-kissing, and hopefully the always-humorous photograph of "buying beer from a vending machine". This trip will serve as a preview for the main Tokyo event: Ruby Kaigi 2007 in June, where I'll finally present JRuby to the Land of the Rising Sun. Come 3:10PM it's time for "so long Japan", but I'll be back soon!

Sixth Stop: Minneapolis, Minnesota

And just 9 days after I departed, I'll be home in Minneapolis again, ready for my next thrilling adventure: The Greater Wisconsin Software Symposium (a No Fluff Just Stuff event) in Milwaukee from March 2-4. Join me for my two fabulous sessions "Bringing Ruby and Rails to the JVM" and "Become Super-Powerful with JRuby", putting the greatest dynamic language ever in the palm of your JVM.

Wednesday, February 14, 2007

Jython 2.2 Beta 1 Released!

The Phoenix Is Rising!

After being considered dead for many years, Jython is back in business with the beta 1 release of Jython 2.2. It's been teetering on the edge of 2.2 compatibility for a long time, but over the past several months the core team and several contributors have rounded off the edges, to the point that a beta release of the long-awaited 2.2 version is now available.

For all you Pythonistas, this should mean two things:

- You have a project for the day. Go get Jython 2.2, try it out with your Python 2.2-compatible apps and libraries, and report any issues you find.
- Start contributing your time, either helping with the Java coding, helping to debug Python apps and libraries, or help on efforts to write C-based libraries in Python.

I will also remind you that a large percentage of JRuby's success is due entirely to its community. Can you really get up in the morning and look at yourself in the mirror without knowing you've helped Jython get back up to speed? Can you?

Tuesday, February 13, 2007

Rails Support Status Update

Things are moving along well...so well I've found time to reimplement String with byte[], work on closures in the compiler, and, well those are posts for another day. Today I update you on the progress of supporting Rails in JRuby.

Rails is quite an interesting beast. I've learned more about Ruby looking through Rails code than from any other source...out of sheer necessity. They say the best way to learn a language is through immersion, right? Is debugging 50-deep stack traces on a questionable interpreter, digging for a reducible test case immersive enough for you? Yeah, I thought so.

We actually hit Rails support hard right at the beginning of this month, or the end of January. The early results were pretty solid, so our efforts have been distracted onto other large JRuby issues not necessarily Rails-related (but all still critical for an eventual 1.0). We also made the bold move of jumping to Rails 1.2.x for all our testing, to show we can keep up with the Rails development process. And to show that we've made great progress, I give you the following results.

ActiveSupport

ActiveSupport is the base module for Rails. It monkey-patches a number of core classes, provides a multibyte String wrapper for UTF-8 encoded text, and handles most of the details of running and configuring an application. It "supports" the other libraries, and is the first crucial leg needed to run Rails. And so we must "support" it well.

Here's the current results of a full test run:

498 tests, 1809 assertions, 16 failures, 6 errors

That's already in the 95% range, so we're in darn good shape. But it turns out a number of these errors are caused by a glitch in our parser related to KCODE. So if we work around that known issue, the results improve to:

498 tests, 1845 assertions, 12 failures, 1 errors

So more like 97% passing once we fix the KCODE parser problem. The remaining issues are almost all related to time-formatting bugs, with a couple multibyte and exception-handling issues tossed in. I haven't attacked the formatting bugs because Printf code is bloody painful, the multibyte issues are waiting on the KCODE parser fix, and the others...well, they're boring.

ActionPack

ActionPack is the brains of a Rails app, housing the mechanisms for controllers, views, and any code to support them. So it's leg two of the crucial three-legged support necessary to run a Rails application.

I hit ActionPack hard in the past, and a bit this month. Ola took off with it and completed most of the remaining failures. Running with the same KCODE workaround, we have the following results today:

1157 tests, 4811 assertions, 7 failures, 14 errors

That's above 98% passing. The remaining failures include a number of dupes (a single failure that breaks a number of tests), some additional string-formatting failures, and a couple that run ok outside of Rake. So it's damn close to perfect.

ActiveRecord

You should all know ActiveRecord by now, right? It's Rails' DB layer, based on the ActiveRecord pattern. It is the third core leg of the Rails platform, though you can certainly have apps that don't use AR for database support.

ActiveRecord is an extensive piece of code and it's the only part of Rails that usually requires a native library to run properly (though there is a pure Ruby impl of its MySQL support that nobody uses). In order to support AR, a number of the JRuby community members have cooperated over the past 9 months to build ActiveRecord-JDBC, a gem-installable module that provides ActiveRecord DB support via JDBC. It's a great bit of hackery, and runs surprisingly well considering our less-than-beautiful Java integration performance (under repair).

To limit the scope of this month's Rails work, we're targetting MySQL, since it's the de-facto standard for Rails apps in most quarters. But most of the work we're doing will apply equally well to the other databases, since JDBC is generally very consistent. Tom has been spending lots of time on Derby, for example, to the point that our ActiveRecord-on-Derby failures are almost entirely limited to SQL features it doesn't support yet.

So then ActiveRecord test results, minus the KCODE workaround (since Tom ran these for me):

1012 tests, 3417 assertions, 41 failures, 35 errors

Here our results dip to around 92% passing, but it's really the extended features of AR that have failures here. We've come a long way on this; results on the months-outdated JRuby wiki show Rails 1.1.6's ActiveRecord only passing about 60% of that release's tests, so there's been a ton of improvement since November. And we generally understand how to fix the remaining failures, so it's only a matter of (short) time.

ActionMailer

Can you guess what ActionMailer does? ActionMailer provides support for composing, formatting, and sending email from a Rails app. And that's about it. It's not a huge library, but it's essential for many apps.

64 tests, 142 assertions, 9 failures, 6 errors

That's about 75% passing, with a grand total of six test scripts. We haven't focused on this much, other than Tom's initial KCODE work a few months back. The current failures are almost all mail-formatting or SMTP-wrangling issues. I don't expect them to be hard to repair.

ActionWebservice

Because ActionWebservice's tests require some database setup, we haven't tackled them yet. But I would lay even money that they'll be comparable to ActiveRecord at their worst. For now, they all just fail because MySQL isn't set up correctly for them.

96 tests, 0 assertions, 0 failures, 96 errors

Any community member that wants to dive into ActionWebservice or ActionMailer would fast become a JRuby Hero.

Railties

Railties is the final piece of the Rails puzzle, and it...well..."ties Rails" together. I show the results here mostly for completeness; many of the libraries in Railties we'll never support (fcgi, for example) and most JRuby-on-Rails deployments will use alternative mechanisms for hitting the web.

5 tests, 19 assertions, 2 failures, 0 errors

Official Rails Support?

Because things are looking pretty solid, we've been looking for an answer to this question: What does Rails Support in JRuby mean? Do we have to pass all test cases 100% to "officially" support Rails? That might never happen, since there's POSIX and external library stuff we won't ever handle (nor will we need to for JRuby on Rails apps). So then is it a certain percentage? 95%? 98%?

I think the truth is that we could really announce support for Rails now. Almost all the visible, outstanding issues with actually *running* Rails apps have been resolved, and most apps and scripts work fine. There's ongoing work to improve ActiveRecord-JDBC's support for other databases, but that's an endless quest. And of course there's more work needed to support Grizzly, Mongrel, and WAR-based deployment of JRuby on Rails, but those are peripheral to the official announcement. Even when we do make an official announcement, it will be for a pre-1.0 version of JRuby, since we know there's another few months left on 1.0 fixes and features.

So what do you think, dear reader? At what point would you feel safe saying "Let's have our non-JRuby hackers try using JRuby on Rails"? You probably would *be* safe right now, since even if you found issues we've got a busy community ready to help solve them. And we'll probably tiptoe closer to "perfect" Rails support in JRuby over the next couple months, chasing the long tail of Ruby compatibility. But how do these numbers and this update make you feel about JRuby on Rails today?

And as always, we love to have additional contributors, so we'll bend over backwards to make it easy for you to help. Join the lists, join #jruby on freenode IRC, or toss us email privately. JRuby is an amazing community-driven success story, and the only thing missing is you.

California Schemin'

Last week Tim Bray and I were at Menlo Park to meet with the Open Source Software Society Shimane, a delegation of developers, managers, and company heads from the Shimane prefecture of Japan. They were visiting Sun to talk with us about opportunities for cooperation, Sun hardware and software, and most importantly: Ruby. For you see Shimane is the home of Yukihiro "Matz" Matsumoto, creator of Ruby, and he accompanied the group to California.

The evening before the event, we went to Fuki Sushi in Palo Alto, a short two blocks from my hotel. I don't believe I've ever eaten such quantity or variety of Japanese cuisine, and I think our guests felt the same way. They marveled at the size of most dishes, especially the ice-cream-scoop-sized lump of wasabi and the two-foot-long sushi tray. They also photographed almost everything...I think I posed for a couple dozen snaps.

During the following day, Thursday, they sat through numerous presentations on Sun hardware and software. Tim and I also discussed Sun's position on Ruby and JRuby for an hour before and about forty minutes after lunch. Tim hit the high-level points about where Ruby will likely fit into the Java ecosystem in the future, and I supplied details and demos of JRuby. I also threw in a demo of JRuby's compiler beating Ruby 1.8 in the standard fib algorithm, which elicited a smile and laugh from Matz himself (whew! I was worried how he'd react!).

Most interesting to me, however, was my discussion with Matz that night.

I was invited to join the delegation for a crab dinner in San Francisco. We went to Crustacean, a moderately upscale joint near California and 101. And after the attendant tied my plastic bib on, we were ready to go.

Since Matz and I ended up sitting together, and since very few others at the table spoke English, we managed to get in some time discussing a couple Ruby 2.0 design issues. Here's a quick summary:

  • Matz seems to have come around to my visibility proposal for "private" in Ruby 2.0, which is largely the same as how Java handles private visibility. I believe this model is a good simplification over the original proposal. See ruby-core:9996 and related for the original discussion. The basic facts of private then would be:
    • You must dispatch to private methods using a functional call, as in foo() versus xyz.foo(). I didn't like this at first, but I've come around to using call syntax to force certain aspects of visibility.
    • Dispatches to private methods will only look in the same class for the method definition.
    • Methods that are public in superclasses can't be made private in subclasses.
    • Methods that are private in superclasses are not visible to subclasses, and so new methods of the same name and any visibility can exist in subclasses.
  • Protected methods in Ruby 2.0 could potentially act like private methods now, though Matz is worried it would be too much of a change. I think it's appropriate; current private method behavior is very similar to Java's model for protected methods, where the methods can't be seen from outside the hierarchy, but can be called and overridden within the hierarchy as normal. I voiced my opinion, so we'll see where Matz goes from here.
  • Matz is still comfortable with removing set_trace_func if a better mechanism for profiling and debugging can replace it. I had a few suggestions for alternate mechanisms, but I also promised to look into Java's model, since it seems to work quite well. I also suggested there may be something to learn from DTrace.
  • Matz has come around to the idea that encoded character sequences are a different type than unencoded byte arrays, though he still wants them to have the same outward interface.
This last item warrants a bit more discussion.

The topic of encoded character strings came up a few times during Matz's visit, usually with him asking how we're doing things in JRuby. I explained that we mostly just follow Ruby 1.8, with our String now being backed by a byte[], but that we're also providing out-of-the-box native support for the new Rails ActiveSupport::MultiByte Chars class, a wrapper around string that enforces character boundaries and encodings.

At dinner, we continued the discussion. I made my case for a separate type with the following points:
  • A separate type would not require String's interface to change, and it could remain a byte array
  • By having separate types, we can use polymorphic behavior to avoid checking and re-checking encodings for every operation
The first item was mostly a non-issue...Matz is fairly intent on changing the String interface in 2.0, and much of that work is already complete. But he had an interesting response to the second item: he's already planning to have separate types internally for encoded character strings. This was very good news to me, since it meant that JRuby could easily support M17N in the future by simply providing different String types that handle the other encodings, where our UTF-16 String implementation could simply be backed up by java.lang.String/StringBuffer/Builder.

So the result of the String discussion can be summarized in a few points:
  • String's interface will change from 1.8 to work with characters rather than bytes, both in the encoded and unencoded forms of String. The plan for String methods' behaviors does not change from current Ruby 1.9.
  • String will have subtypes that represent encoded character data, though in most cases you won't need to know about those types. If you do need to go after a UTF8String (my name), you can, but there will also be some sort of factory model for generating encoded strings and Ruby 2's encoding pragma will handle literals.
All told, I think it was a very productive trip, and it was great to help Matz work through a few Ruby 2.0 design questions.

Wednesday, January 31, 2007

The End is Nigh: Help Squash Rails Bugs

My friends, the end of the road is in sight for official Rails support.

Tom, Ola, and I have been working over the past week to get remaining Rails issues wrapped up. As a result of our efforts:

  • ActionPack is now "practically" 100% working, minus a test or two we can't support and a few tests that are broken or that run fine in isolation (it would be nice to know *why* those fail)
  • ActiveSupport is well above 95% passing
  • ActiveRecord is in the 90% range passing with MySQL and in the 80% range with Derby
The remaining modules are still yet to be worked, but they are mostly in high % as well: ActionWebService, ActionMailer, Railties

So here is a set of instructions on how YOU, dear reader, can help round out JRuby's support for Ruby on Rails.
  1. Getting a testbed set up
    1. Update JRuby trunk (http://svn.codehaus.org/jruby/trunk/jruby), build it (ant clean jar), love it
    2. Install Rake (gem install rake)
    3. Fetch Rails 1.2.1 from Rails SVN at http://dev.rubyonrails.org/svn/rails/tags/rel_1-2-1/

  2. Running the tests
    1. From within the module under test (like activesupport/ or actionmailer/) just run "rake". All tests should execute and a report should be provided at the end. You can usually run tests individually as well, although a few depend on the side-effects of previous tests.
    2. ActiveRecord requires some additional setup; I'll update this post with Tom's instructions and patches shortly.

  3. Reporting issues
    1. Reporting failures in Rails is good
    2. Reporting reduced test cases or clear explanations for failures in Rails is better
    3. Reporting reduced test cases and including patches for failures in Rails is BEST
    4. Don't forget to check if your issue has already been reported, and please sync up on the mailing list while you're working
    5. Patches will probably not be accepted without a reusable test case. We're trying to grow our regression suite as a result of this work.
    6. JRuby's JIRA is here: http://jira.codehaus.org/browse/JRUBY

  4. Caveats, things to watch for, things to try
    1. ActiveRecord (with the AR-JDBC adapter) could use wider DB testing. We've done quite a bit of work with MySQL and Tom has been improving Derby support, but there are lots of other databases out there. Pick your favorite database, follow Tom's instructions to get up and going, and report issues (in the JRuby JIRA at least, but also report to jruby-extras project if appropriate)
    2. Railties includes code that will never run under JRuby, like its fcgi-based dispatcher tests. You should confirm with us that they're expected, and then ignore or delete them for your future runs.
    3. Rails is a very...interesting...application to debug. Feel free to ask on-list if you simply don't get something. I've seen things in Rails code no man should have to see, so I know it can be frustrating to debug at times.
We're on the home stretch now, and Rails is getting more and more solid every day. With you all helping, we should be able to finish off the remaining failures, clean up major outstanding JRuby issues, and kick out a pretty sweet "Rails-supporting" JRuby release in the next couple weeks.

Update: A couple folks pointed out that the codebase didn't compile under Java 1.4.2. That has been corrected!

Update 2: A few folks are seeing a problem installing gems related to the %p operator to printf. We're working on that, and it's a fairly minor issue, but to avoid it there's one additional step before you install rake: set the JRUBY_HOME env var to the root of your JRuby stuff.

Tuesday, January 30, 2007

Improving Java Integration Performance

I created JRUBY-501 to track performance improvements to Java integration, since it's come to light recently that it may be one of our biggest bottlenecks now. And I found a ripe, juicy fix already.

For every call to a Java type, we call JavaUtilities.matching_method with a list of potential methods and the given argument list. matching_method compares the available methods and the types of the arguments, choosing the best option and returning it to be called. This is essentially our heuristic for choosing an overloaded method from many options, given a set of arguments.

Problem was, we didn't cache anything.

Given a list of argument types and a list of methods, there's only ever going to be one appropriate choice. Unfortunately our code was doing the search for every single call, and you can imagine how much additional overhead that added. Or perhaps you can't, and I'll show you.

Here's the numbers before my tiny change:

 38.862000   0.000000  38.862000 ( 38.861000)
40.230000 0.000000 40.230000 ( 40.230000)
This test basically just instantiates a StringBuffer and appends the same character to it 100_000 times. It takes roughly 40 seconds to do that with the old code.

And here's with my changes:
  3.295000   0.000000   3.295000 (  3.294000)
2.933000 0.000000 2.933000 ( 2.933000)
Yes, you're reading that right. It's an 13 times improvement.

And the change was trivial: given the list of methods and argument types, cache the correct method. So simple, so elegant, so effective.

So does this affect regular Ruby code? You better believe it does!

I had been intrigued by the fact that some of the first methods JITed during rdoc generation were all JavaSupport methods. That told me something in rdoc was using a class we provide through Java integration, rather than natively or in pure Ruby. So I figured with this change, I'd re-run the numbers.

Before the change, a full rake install with rdoc took about 42s, or about 31s with ObjectSpace disabled. And now, the "after" numbers:
with ObjectSpace:
real 0m29.765s
user 0m28.843s
sys 0m2.169s

without ObjectSpace:
real 0m24.984s
user 0m23.559s
sys 0m1.757s
This is by far the largest increase we've seen in rdoc performance in several months. The fix should also drastically improve the performance of libraries like ActiveRecord-JDBC, which is extremely Java-integration-heavy.

Another area that's been painful was installing Rails with all docs. It used to take over an hour, but now it's under *seven minutes*.

I hope those of you who've seen or blogged about performance problems (especially with the aforementioned ActiveRecord-JDBC) will try re-running your tests. This improvement ought to have a very noticeable effect on benchmarks.

Now the only concern I have with the caching is that it's a little coarse; there may be better places to do the caching, or finer-grained items to cache against. And we could probably pre-fill the cache with some likely candidates. But an improvement like this outweighs those concerns, so it's been committed...and there's bound to be similar improvements as well.

Boy oh boy is that low-hanging fruit looking ripe.

Friday, January 19, 2007

Velocity, F3, Grizzly on Rails, JParseTree

Roundup!

Martin Fowler enjoys using JRuby with Velocity

Jean Lazarou creates an F3 clone with JRuby

Ashish Sahni posts a walkthrough for JRuby on Rails under Grizzly

Werner Shuster releases JParseTree, sexp-based JRuby parse tree generator.

JRuby, JRuby, JRuby.

Thursday, January 18, 2007

JRuby Compiler: In Trunk and Ready to Play

Times they are a-changing.

I posted previously on JRuby's compiler work. There have been various iterations of the compiler, many purely prototype and never intended to be completed, and a few genuine attempts at evolving toward full Ruby support. However I believe in the recent weeks I've settled on a design that will carry us to the JRuby compiler endgame.

For the past year, we've emphasized correctness over performance nine times out of ten. When we did focus on performance, it was solely on improving JRuby's interpreter speed, in an attempt to match Ruby's performance in this area and because we knew that JRuby could never entirely escape interpretation. Ruby's just too dynamic for that. So while compatibility with Ruby 1.8.x continued to improve by leaps and bounds, our performance was rather poor in comparison.

This past fall, things started to change. Compatibility reached a point where we could finally be confident about our set of regression tests and our understanding of "how Ruby works" across all its weirdest features. As we understood better the design of the C implementation and the quirky intricacies of the language, we started to see a path to enlightenment. We started to realize how we could support Ruby as it exists today while simultaneously evolving JRuby into a more efficient and cleaner design. And so the performance numbers started to change.

From 0.9.0 to 0.9.1, we had a clean doubling of performance across the board. Our favorite benchmark--RDoc generation--was easily twice as fast, and other simpler benchmarks like fib had similar improvements. 0.9.2 was more of a rushed release for JavaPolis, but we had a good 1/4 to 1/3 speedup even then, since the ongoing refactoring removed another large chunk of overhead from JRuby's core runtime.

From 0.9.2 to current trunk, however, has been a different matter entirely.

The first major change is that we've started to seriously alter the way JRuby does dynamic method dispatching. I did some research, read a few papers, and mocked up and benchmarked a few options. What we've settled on for the moment is a combination of STI for the core classes (STI provides a large table mapping methods and classes to actual code) and various forms of inline caching for non-core classes (basically, for pure Ruby classes; though this is yet to be implemented in trunk). STI provides an extremely fast path for dispatch on those hardest-hit methods, since it reduces calling most core methods to two array indexes and a switch, a vast improvement over the hash lookup and multiple layers of abstraction and framing we had before.

We are continuing to expand our use of STI as it is applicable, and I will soon start exploring options for interpreted-mode inline caching (polymorphic, likely, though I need to run a few trials to get numbers balanced right). So fast dynamic dispatching is well on its way, and will improve performance across the board.

Then there's the compiler work. You have no idea how much it's irritated me to hear people talk about JRuby the past year and say "yeah, but it doesn't compile to Java bytecode." This obviously amounts to pure FUD, but beyond that it totally ignores the complexity of the problem: not a single person on this earth has managed to compile Ruby to a general-purpose VM yet. So complaining about our missing compiler is a bit like complaining that we haven't moved mountains. Honestly people, what do you expect?

Of course, there's the flip side of this statement: compiling Ruby is a hard problem, and I like hard problems. For me it's doubly hard, since I've never written a compiler before. But hell, before JRuby I'd never even worked on an interpreter or language implementation before, and that seems to have gone alright. So there it is...Mount Ruby, waiting to be climbed. And climb it I must!

The current compiler design lives in two halves: the AST-walking half; and the code-generation half. I chose to split these two because it make several things easier. For starters, it allows me to abstract all the bytecode generation logic behind a simple interface, an interface that presents coarse-grained operations like invokeDynamic() and retrieveLocalVariable(). The ultimate implementation of those operations can then be modified at will. It also allows us to evolve the AST independently of the compiler backend, even to the point of swapping in a completely different parser and in-memory code representation (like YARV bytecodes) without harming the evolving code generator backend. So this split helps future-proof the compiler work.

The current design also has another advantage: not all of Ruby has to compile for it to be useful. Currently, as the AST walker encounters nodes, if it finds a node it can't deal with it simply raises an exception. Compilation terminates, and the compiler's client can deal with the result as it will. This leads to a really powerful feature of this design: we can install the compiler now as a JIT and as it evolves more and more code will automatically get optimized. So once we're confident that a given node type is 100% compiling correctly, that node will now be eligible for JIT compilation. As an example, here's the output from a gem installation with the current compiler enabled as a JIT (with my logging in place, naturally):

compiled: TarHeader.empty?
compiled: Entry.initialize
compiled: Entry.full_name
compiled: Entry.bytes_read
compiled: Entry.close
compiled: Entry.invalidate
Successfully installed rake, version 0.7.1
Installing ri documentation for rake-0.7.1...
compiled: LeveledNotifier.notify?
compiled: LeveledNotifier.<=>
compiled: RubyLex.getc
compiled: null.debug?
compiled: BufferedReader.ungetc
compiled: Token.set_text
compiled: RubyLex.line_no
compiled: RubyLex.char_no
compiled: BufferedReader.column
compiled: RubyToken.set_token_position
compiled: Token.initialize
compiled: RubyLex.get_read
compiled: RubyLex.getc_of_rests
compiled: BufferedReader.getc_already_read
compiled: BufferedReader.peek
compiled: RubyParser.peek_tk
compiled: TokenStream.add_token
compiled: TokenStream.pop_token
compiled: CodeObject.initialize
compiled: RubyParser.remove_token_listener
compiled: Context.ongoing_visibility=
compiled: PreProcess.initialize
compiled: AttrSpan.[]
compiled: null.wrap
compiled: JavaProxy.to_java_object
compiled: Lines.next
compiled: Line.isBlank?
compiled: Fragment.add_text
compiled: Fragment.initialize
compiled: ToFlow.convert_string
compiled: LineCollection.add
compiled: Entry_.path
compiled: Entry_.directory?
compiled: Entry_.dereference?
compiled: AttrSpan.initialize
compiled: Entry_.prefix
compiled: Entry_.rel
compiled: Entry_.remove
compiled: Lines.rewind
compiled: AnyMethod.<=>
compiled: Description.serialize
compiled: AttributeManager.change_attribute
compiled: AttributeManager.attribute
compiled: ToFlow.annotate
compiled: NamedThing.initialize
compiled: ClassModule.full_name
compiled: Lines.initialize
compiled: Lines.empty?
compiled: LineCollection.normalize
compiled: ToFlow.end_accepting
compiled: Verbatim.add_text
compiled: FalseClass.to_s
compiled: TopLevel.full_name
compiled: Attr.<=>
Installing RDoc documentation for rake-0.7.1...
compiled: Context.add_attribute
compiled: Context.add_require
compiled: Context.add_class
compiled: AbstructNotifier.notify?
compiled: Context.add_module
compiled: LineReader.read
compiled: null.instance
compiled: HtmlMethod.path
compiled: HtmlMethod.aref
compiled: ContextUser.initialize
compiled: HtmlClass.name
compiled: TokenStream.token_stream
compiled: LineReader.initialize
compiled: TemplatePage.write_html_on
compiled: Context.push
compiled: Context.pop
compiled: HtmlMethod.name
compiled: Context.find_local_symbol
compiled: SimpleMarkup.add_special
compiled: TopLevel.find_module_named
compiled: Context.find_enclosing_module_named
compiled: HtmlMethod.<=>
compiled: ToHtml.annotate
compiled: HtmlMethod.visibility
compiled: HtmlMethod.section
compiled: HtmlMethod.document_self
compiled: LineReader.dup
compiled: Lines.unget
compiled: ToHtml.accept_paragraph
compiled: ContextUser.document_self
compiled: ToHtml.accept_heading
compiled: Heading.head_level
compiled: ToHtml.accept_list_start
compiled: ToHtml.accept_list_end
compiled: ToHtml.accept_verbatim
compiled: SimpleMarkup.initialize
compiled: AttributeManager.initialize
compiled: ToHtml.initialize
compiled: ToHtml.end_accepting
compiled: HtmlMethod.singleton
compiled: Context.modules
compiled: Context.classes
compiled: ContextUser.build_include_list
compiled: HtmlMethod.description
compiled: HtmlMethod.parent_name
compiled: HtmlMethod.aliases
compiled: HtmlClass.parent_name
compiled: ContextUser.as_href
compiled: ContextUser.url
compiled: ContextUser.aref_to
compiled: HtmlFile.<=>
compiled: HtmlClass.<=>
You can see from the output that not only are RubyGems methods getting compiled, but so are stdlib methods and our own Java integration methods. And this is with the current compiler, which doesn't support compiling class defs, blocks, case statements, ... Hopefully you get the picture; this bit-by-bit implementation of the compiler allows us to slowly grow our ability to optimize Ruby into Java bytecodes.

So then, how well does it perform? It performs just dandy, when we're able to compile. Witness the following results for a simple recursive fib algorithm running under Ruby 1.8.5 and JRuby trunk with the JIT enabled.

$ ruby test/bench/bench_fib_recursive.rb
12.760000 1.400000 14.160000 ( 14.718925)
12.660000 1.490000 14.150000 ( 14.648681)
$ JAVA_OPTS=-Djruby.jit.enabled=true jruby test/bench/bench_fib_recursive.rb
compiled: Object.fib_ruby
8.780000 0.000000 8.780000 ( 8.780000)
7.761000 0.000000 7.761000 ( 7.761000)
Yes, that's nearly double the performance of the C implementation of Ruby. And this is absolutely real.

Now JITing is great, and it's obviously carried Java a long ways. The HotSpot JIT is an unbelievable piece of work, and any app that runs a long time is guaranteed to perform better and better as deeper optimizations start to take hold. But We're talking about Ruby here, which starts up at C-program speeds, and runs as fast as it does immediately. So then JRuby needs a way to compete for immediate execution performance, and the most straightforward way to do that is with an ahead-of-time compiler. That compiler is now also available in JRuby trunk.

The name of the command is "jrubyc", and it does just what you'd expect, it outputs a Java class file for your Ruby code. However the mapping from Ruby code to a class file is not as straightforward as you'd expect: a Ruby script may contain many classes or no classes at all, and those classes may be opened and re-opened by the same script or other scripts at runtime. So there's no way to map directly from a Ruby class to a Java class given the strict limitations of Java's class model. But there is a much smaller unit of code that does not change over time, aside from being mercilessly juggled around: methods.

Ruby, in the end, is a creative and sometimes complicated jumble of method "objects", floating from class to class, from module to module, from namespace to namespace. Methods can be renamed, redefined, added and removed, but never can they be directly modified. And so here is where we have our immutable item to compile.

JRuby's compiler takes a given Ruby script and generates the following Java methods out of it: One Java method for the top-level, straight-through execution of the script, including class bodies and "def"s and the like (called "__file__" in the eventual Java class...thanks Ola for the idea), and a Java method for every Ruby method body and closure contained therein, named in such a way as to avoid conflicts. So for the following piece of code:
require 'foo'

def bar
baz { puts "hello" }
end

def baz
yield
end
There would be four Java methods generated: one for the toplevel execution of the script, two for the bar and baz methods, and one for the closure contained within bar. The resulting class file would store these as static methods, so they are accessible from any class or object as necessary, and the toplevel run-through would bind the two Ruby methods to their appropriate names in Ruby-space.

Quite simple, really!

So then an example of the precious, precious JRuby compiler:
$ cat fib_recursive.rb
def fib_ruby(n)
if n < 2
n
else
fib_ruby(n - 2) + fib_ruby(n - 1)
end
end

puts fib_ruby(34)
$ jrubyc fib_recursive.rb
$ ls fib_recursive.*
fib_recursive.class fib_recursive.rb
$ time java -cp lib/jruby.jar:lib/asm-2.2.2.jar:. fib_recursive
5702887

real 0m8.126s
user 0m7.632s
sys 0m0.208s
$ time ruby fib_recursive.rb
5702887

real 0m14.649s
user 0m12.945s
sys 0m1.480s
Again, about twice as fast as Ruby 1.8.5 for this particular benchmark.

Now I don't want you going off and saying JRuby has a perfect compiler that will double the performance of your Rails apps. That's not true yet. The current compiler covers only about 30% of the possible code constructs in Ruby, and the remaining 60% (Update: 70%...that's what I get for late-night blogging) contains some of the biggest challenges like closures and class definitions. It's sure to be buggy right now, and the JIT isn't even enabled by default, plus it has my nasty logging message burned into it, to discourage any production use.

But it is very real. JRuby has a partial but growing compiler for Ruby to Java bytecode now.

And oh my, look at the time. Tonight I have to finish my visa application for a trip to India, nail down schedules and descriptions for several upcoming talks, and prepare some slides and notes for presentations in the coming weeks. You will see more about the Java compilation and our developing YARV/Ruby 2.0 bytecode support over the next couple months...and you can expect JavaOne to be an interesting time for Ruby on the JVM this year ;)