Sunday, November 23, 2008

Noise Cancelling

Short thoughts on KirinDave's post The Opposite of Momentum, which the anti-Ruby crowd has latched on to as more evidence that Ruby is "falling flat on its face".

Dave's post appears to largely lament the lack of progress in the C implementations, be it their lack of performance, presence of memory leaks, or relatively primitive GC implementations. He briefly mentions alternative implementations, calling Rubinius "the light and hope of the Rubyverse" and summarizing JRuby as "great" before dismissing both.

In Rubinius's case, he may have a point. There have been many setbacks, and after a year of funding six developers it's still not really usable for real apps. The sad news that their team has been cut to two paid developers certainly doesn't help. Most will agree that the Rubinius ideal, that of Ruby implemented in Ruby, is a great goal...perhaps the highest ideal for a Ruby implementation. But in practice, it's turned out to be a lot harder to execute...and impossible to execute in a year with six smart hackers.

Update: My intent here seems to have been missed. I am not in any way claiming "Rubinius sucks", because Rubinius isn't even finished yet. I would not make such a claim about any incomplete project. I mention Rubinius only because Dave dismissed both in the same breath; to address one case I felt I needed to address the other. Rubinius will succeed...of that I have no doubt. And already it has proven many things possible nobody ever expected to see. It's unknown how long it will take to "get there" but it will "get there" eventually, and be a major contender for replacing the standard impls. Dave's use of Rubinius to help prove a lack of momentum is almost as questionable as his out-of-hand dismissal of JRuby. We've started to attempt an integration of Rubinius's kernel into JRuby, and we've added Rubinius's FFI and MVM APIs because they were very well-designed. We continue to contribute to the RubySpecs, and I eagerly offer any technical help I can whenever the Rubinius team has questions about how JRuby solves certain problems. What more good things do you want me to say about Rubinius?

However when we look at JRuby, Dave is entirely wrong to dismiss it in a single sentence. JRuby *is* what he wants, in almost every way.

  • JRuby runs on the best available dynamic language VM, Hotspot. Hotspot has its roots in the Smalltalk world Rubyists hold in such high esteem, and real steps are being taken to make it even better than before for dynamic languages. The end result will be trivially inlinable dynamic calls nearly as fast as static Java code, which no other VM in the world can claim.
  • JRuby does not have GC headaches and memory leaks like the C impls because we use the existing GC options on Hotspot...which are, again, some of the best GC implementations in the world. It's folly to say that an implementation from scratch is going to be able to compete with JRuby on JVM, because the simple truth is that making a world-class VM and/or GC is super dooper hard.
  • Even where JRuby still stumbles a bit, like the classic JVM problem areas of memory use and startup time, we've made huge strides. JRuby starts up no slower than Rubinius at its fastest...in many cases under a second. And memory-wise, we pay little cost beyond the JVM's own 20-30MB memory tax, while still working to reduce our overall consumption with every release.
  • JRuby is also moving faster than any implementation available. A year ago, we were generally a bit slower than Ruby 1.8.6; this year, we're faster in most cases than Ruby 1.9. A year ago, there were a handful of early-adopting production users; this year, dozens of them, ranging from small orgs and agile projects to governments, banks, and telecom. There's no lack of momentum when it comes to JRuby.
  • To top all this off, there's still dozens of folks working to make Hotspot even faster, it's GCs even smarter, and we just come along for the ride. Yes, we've done a lot because we haven't implemented our own VM specifically for Ruby. But that's exactly the point...we've been able to focus on areas that matter most, like actually running apps and running them as fast as possible.

The bottom line, as far as I can see it, is that if the C impls of aren't moving fast enough, and the "light of the Rubyverse" is hitting a few roadblocks, you damn well better give JRuby more consideration than one sentence. I certainly hope this wasn't a reflection of any continuing anti-Java bigotry in the Ruby world, since as I've mentioned previously that attitude will only make things worse. If it reflects simple ignorance as to why JRuby really will be a better Ruby, then we're not doing a good enough job educating...help us do better.

JRuby is continuing to do things no other Ruby has been able to do. We've cracked the Enterprise glass ceiling, with numerous deployments inside some of the most rigidly Java-centric and intransigent organizations around. We've shown it's possible for Ruby to perform extremely well, and we've only scratched the surface of Hotspot's potential. We've implemented most of Ruby 1.9's features in just a few months, where other implementations have barely gotten 1.8 working. And we're just getting started...we have plans upon plans to continue improving compatibility, performance, scaling, and ease-of-use, and the various folks working on the JVM will continue making it a better and better host for dynamic languages. If you haven't used JRuby yet, you're really missing out. It's time to give it a try.

(Footnote: Don't take this as a shot at any of the other impls; it's obvious JRuby had a big head start, and we've been funded longer (albeit less) than most of them. This post is squarely focused on anyone like Dave that thinks if the C impls don't solve all Ruby's implementation woes, Ruby is doomed. The truth is that Ruby will survive despite any lingering issues in the C impls, and JRuby is a crucial part of that survival.)

23 comments:

murphee said...

"Rubinius's case, he may have a point. [..]after a year of funding six developers it's still not really usable for real apps."

Yeah: doing infrastructure work is always a thankless job. You seem to forget what's come out of all this:
- RubySpec, the massive amount of executable specs that are now run by every Ruby implementation; this didn't exist before Rubinius.
- ruby_parser (finally a Ruby based Ruby parser, something beneficial for _every_ Ruby implementation, and a first step to wean IDEs off the JRuby AST dependency)
- ffi (which JRuby seems to be happy to adopt, but it originated in Rubinius)
- the initial MVM implementation;
- Rubinius Kernel and all the bits that'll allow to make Ruby go full turtle-on-turtle at some point;

And I'm sure I'm forgetting a few other things. So yeah, it took longer to get up to speed than expected, but in the process the Rubinius gang improved the Ruby and Ruby VM space significantly.


A last Nitpick: "Hotspot has its roots in the Smalltalk world":
Who gives a plonk? The thing was forked off the Strongtalk source, what, 10 years ago. All the dynamic goodness has been covered by a thick Java/JVM crust... and now it'll take a few years to knock holes in that to get back at the fancyness that made Strongtalk fast. If it's "the best available dynamic language VM"s, why is it fighting dynamic languages all the way? (no lightweight classes, no lightweight methods, Permgen/classloader blowups, etc). I can definitely appreciate the technical and pragmatic benefits of Hotspot (stable codegen backend, wealth of stable GCs,...), but that kind of hyperbole is best left to marketing bunnies.

Tonio said...

I have used JRuby for a bit between 1.0 and 1.1.2 (I think) and it worked great for the most part. With the continuing improvements I imagine it is a great alternative to MRI.

Howhever, the biggest JRuby limitation and the one stopping me from using it fully is the support for most C-based gems. If I want to use NArray ou Ruby GSL I'm stuck with MRI.

Do you plan to solve this, somehow? I know FFI is a good mid/longterm solution but it doesn't cover all cases, especially if it isn't used by gem developers.

Cheers. Keep up the great work!

Charles Oliver Nutter said...

Murphee: Thanks for stopping by! WRT Rubinius you are correct on every point, except that JRuby was the first impl with MVM and the recent JRuby team work was to make good on bringing FFI to MRI. I explicitly called out Evan for coming up with the original API, and noted we and the Rubinius team worked together to improve that API. But you missed my point entirely. If Dave's purpose was to point out that C ruby users aren't a great deal better off running real apps on real, working implementations, Rubinius does not represent any sort of answer to that accusation. Many great things have come out of Rubinius, there's no denying it. But Rubinius itself is not yet a compelling answer to the "ruby implementations suck" claim. I've no doubt it will be some day.

WRT Hotspot, I'm not sure you have all the facts, or else you are unintentionally misrepresenting them. Hotspot is far more than a nice codegen backend and a handful of GCs. No other mainstream VM can inline across dynamically bound calls. No other can optimize those calls as a whole. No other can dynamically promote objects from the heap to the stack if they do not escape a compilation unit. And perhaps most importantly, no other can do all this aggressively, eagerly, knowing that its deopt capabilities will fire if early assumptions prove false. It is perhaps one of the least-appreciated pieces of software in the world, and belittling its potential simply because its Java exterior is most apparent is myopic at best. Yes, that exterior makes it a bit trickier to implement dynamic languages, but no more difficult than its nearest competitor, the CLR (with or without the DLR). And improvments are being made and will ship in Java 7...as if that were necessary, since Hotspot is OSS and buildable by anyone anyway. It is a far, far bigger deal than you give it credit for, and your do a disservice to your readers by saying such things.

james said...

"But Rubinius itself is not yet a compelling answer to the "ruby implementations suck" claim."

You *can* make a broader point concerning the Ruby community without taking potshots at Rubinius, can't you? I say this as an outsider; I don't program Ruby very often and my opinion may not be worth very much. But I follow JRuby somewhat loosely and I find that kind of behavior somewhat disconcerting.

It very much comes off as "Rubinius sucks. Oh, I don't really mean that; its developers have done so much good. But did I mention how much it sucks?"

Charles Oliver Nutter said...

james: I appreciate that my tone did not appropriately convey my intent. I do tend to be a bit clinical in my assessment of competing implementations. I've added a short update to help clear things up.

Charles Oliver Nutter said...

tonio: Wayne Meissner of the JRuby team made an attempt at supporting C extensions, and while he was able to get basic ones working, we decided very quickly that it was not going to be worth the effort. C extensions are not always simple nice-to-have add-ons, they are frequently also invasive memory-twiddlers inextricably married to C ruby's internal implementation. To support them in a general way means mimicing the internal APIs of String, Array, Hash, and others in such a way that they are indistinguishable from C ruby itself. In some cases, it's possible, albeit with horrendous performance overhead from copying data back and forth (to/from JVM heap to pinned memory the extension can use). In other cases, it's nearly impossible (many extensions depend on C ruby's specific implementation of Hash, which we can neither mimic nor reasonably marshal back and forth).

Ultimately, however, FFI is the only way Ruby itself is going to be able to grow beyond the current C impls. Because current extensions have full, unfettered access to the internal structure of Ruby objects, the optimization and redesign potential for C ruby is very limited. A new GC can't simply be dropped in, since extensions have access to live memory addresses. The memory model can't be changed substantially. Optimizations to object internals can't be implemented. C extensions are at once a great boon to the C impls and a great curse. Argue vigorously for their abandonment, and when using JRuby help implement or seek out alternatives that may already exist.

GroovyMag said...

This reminds me slightly of the GNU/Linux relationship that has evolved. Linux users say "Linux rocks!" Stallman points out that Linux is built on GNU tools, which were developed while putting down the basis for a kernel (IIRC). I know it's not an exact comparison, but it's what I was reminded of. :)

Greg said...

Don't let the concern trolls get to you Charles. Everyone with half a brain is going to be running JRuby a year from now because you guys are the only ones who thought maybe we shouldn't be re-inventing the wheel when it comes to the ruby interpreter, HTTP stack, and most 3rd party libraries in use in enterprise software today.

By not having to build all the basic building blocks (GC, JIT optimizer, etc), now you guys are over the hump and can really start innovating while everyone else is whining and going into a depression (no pun intended.) Of course there's still work to be done.. for example, on my Mac, JRuby startup time (with Rails + gems) is pretty brutal, but I am more than happy to deal with it for all the benefits I get by being able to integrate with all the great Java libraries out there. When we were exploring options for what Ruby setup to use 6 months ago, JRuby was a no brainer because -- just like "in the long run everyone's dead", "in the long run everyone's going to be using JRuby." At a high level, it's obvious, because of the leverage you guys have with the JVM.

You can criticize other people's work even if that work is high quality if it is a waste of time. People working on the other VMs that aren't leveraging a modern existing VM architecture should just suck it up and realize they should have just listened to you guys from the beginning, check out JRuby trunk, and start contributing. There's no real reason not to other than ignorance, pride, and the delusion that somehow they can on their own produce a better GC, JIT, and VM than the ridiculous amount of manpower that's gone into the JVM and Hotspot. You're right for posting this article, because the notion that a person can signal the deathknell for Ruby and in the same breath just gloss over JRuby is pretty disturbing if it reveals how the majority of the community sees things.

Keep up the great work!

DrGB said...

Charles:
I have been following JRuby's progress with great admiration, and using JRuby for small personal projects. (Still write Java for pay). JRuby is currently IMHO, the best Ruby implementation happening.
My question is, is there a way to bundle up JRuby with a minimal JVM, or does JRuby inevitably come with the entire 72 MB (and growing) JVM?
I know, we live in a world of cheap 8 GB flash drives, terabyte hard drives, and 4 GB of RAM so maybe I should just get over wanting something that feels tighter and smaller than JRuby with all of Java. I get the entire boatload whether or not I need it or use many portions of it in my application. Any thoughts?

shevegen said...

What annoyed me a lot was that the guy you mentioned (the one who was bitching against ruby losing momentum) - does not even have a comment field.

Blogs/Writeups without comment fields are a complete waste of time - and incredibly boring.

So, heads up for you!

rob said...

I read that guys whining post earlier in the day. I thought a lot of his concerns were answered by JRuby, which he obviously hasn't tried at all.

I also don't get why he dismissed 1.9, which I think is an important next-step for ruby.

Anyway - he's way off base...

Phil said...

> If it reflects simple ignorance as to why JRuby really will be a better Ruby, then we're not doing a good enough job educating...help us do better.

I was really encouraged by what I saw at the JRuby talk at RubyConf.

Previously you've focused on things like, "hey look, you have access to all these awesome Java libs" and "you can deploy WAR files". But your average Ruby dev sees those Java libs as just a set of really ugly APIs, and as for war files, I think the response is something along the lines, "We are a peaceful people..."

But this last talk I heard you focus a lot more on stuff that Rubyists care about. Memory savings, good I/O, easy deployment... these things speak a lot more loudly to us.

Anonymous said...

Egotism again.
It is funny to see fighting egos involved in an obsolete discussion.
Maglev will show us the path.

Hervé said...

@Anonymous: Why being Anonymous ? ;-)

@JRuby team: Keep the hard work, what you are doing with JRuby is great for Ruby AND great for the JVM too !!!

CH said...

Charles & Team,

You've done an amazing job with JRuby. It seems like only yesterday when JRuby was too slow even be usable. You've come so far. I agree that the only rational road forward is Ruby on JVM and hopefully CLR someday. However, it sounds like the 37 Signals team isn't using JRuby and I can't understand why they wouldn't unless it's conservatism/legacy issues. I'd love to hear their answer.

Chris Richards said...

Jruby runs faster than 1.8.x MRI in nearly all benchmarks... Apart from when running Rails. When will this change?

Eugueny Kontsevoy said...

Well said. Java is a great platform for "dark corner" server-side in-a-rack deployments and after evaluating JRuby for a while we're ready to move on in that direction. Very impressive indeed.

The original rant, however, is still valid. JRuby, while great on a server side, will never become a mainstream implementation precisely for the same reason Java was never able to, and you mentioned them yourself: minimum 30MB of JVM plus the startup delay, while MRI is 3MB and it starts under 100 milliseconds on my MBP.

When MRI gets to CPython level of maturity I'll see no reason not to develop general purpose software in it, reserving JRuby for high-performance, high-volume server work.

Beoran said...

First of all, the bug in Tom Werner's logger that KirinDave mentioned has been solved in Ruby 1.9.x. Together with many other problems.

The language and culture barrier may make it hard to see that our friends in in Japan are working hard on making MRI better. MRI 1.9.x is a huge improvement over MRI 1.8.x. For me, in the game I'm programing, it has proven to be between twice to ten times as fast, and it has a better memory use, better stability, less problems with garbage collection, and some nice extra features that round off the language and make it even more fun to use.

I think that with MRI 1.9.x, Ruby is as stable CPython as is technically feasible, seen the different way they handle memory. And with JRuby, it's more so, and a lot more secure to boot.

KirinDave seems to want more features in Ruby, but which one? If you think Ruby if moving forward too slowly, or if you want new features, then please help implementing them in stead of complaining. Matz himself knows well that Ruby needs more momentum, but he can't make it alone.

Finally, JRuby has an underestimated great potential: ever heard of the gjc, the Gnu Java compiler? Between JRuby's ability to compile Ruby to java bytecode, and gjc's ability to compile Java bytecode to native code, we're close to getting a Ruby-to-native compiler.

opinali said...

Just to be picky:

"No other mainstream VM can inline across dynamically bound calls." - I think you forgot other first-grade JVMs like IBM's and BEA^H^H^HOracle's, they both do aggresive devirtualization etc. (although the last time I checked, HotSpot was still the best on this). But this raises the interesting question, how well does JRuby run on top of these other JVMs? Some of these guys are also moving into the dynamic language bandwagon, e.g. check IBM's new WebSphere sMash (Groovy & PHP), if their JDK is not good at dynamic languages I'm sure they should be working on this now.

"No other can dynamically promote objects from the heap to the stack if they do not escape a compilation unit." - HotSpot doesn't do that yet (up to 6u10 or 6u6p). AFAIK this is now promised for Java SE 7.

opinali said...

Now on the JVM's minimum footprint. With 6u10 / HotSpot Client, a "Hello World" app runs on a 2Mb heap, with a live set of just 106K and 3ms full-GC events. The process has a 17M Private Bytes size and only a 8M Working Set (4M Private, 4M Shareable). Loading time was 78ms (46ms kernel + 31ms user). These numbers compete neck-to-neck with any lightweight interpreter; they show that HotSpot is really not the bloated VM that some people think.

I'm running HotSpot Client, but that's the choice for simple, short-lived programs that raise criticism for Java's costs in warm-up and RAM. Right now JRuby should depend a lot on HotSpot Server's superior optimization, but it seems that with JDK 7's dynamic enhancements, JRuby's compiler/runtime will become much simpler and it may generate code that can be sufficiently optimized by HotSpot Client.

Java's real bloat is in its frameworks. Not just because because they're not tuned to favor memory usage (but rather speed, security etc.), but also because they're completely implemented in pure-Java code. The Java bytecode format is pretty inefficient (Unicode everywhere, per-class constant pools... ZIP archiving and compression!) and JITted code+metadata is 10x bigger.

The obvious solution may be ignoring the Java frameworks and rolling your own. It's all open source, so let's just make a fork, purge all stuff that's not necessary to Ruby (even from java.* packages), tune the rest to Ruby, and reimplement JRuby's engine to be compatible with this JRE subset. If anyone doubts that this approach could result in a much lighter JRuby VM, just run a HelloWorld program with -verbose:class. You'll see classes for security; lots of java.io and java.nio; JAR archive handling (JRuby could use a smarter binary format); Java Serialization; stuff that's useless even for Java like ThreadGroup; some j.u.concurrent stuff; several collections; RAS features like shutdown hooks; and even classes that seem to be bad dependencies (sun.jkernel.DownloadManager). And that's for a freaking HelloWorld app; a more real-world app (like a JRuby HelloWorld) would load an enormous number of additional dependencies that could be great candidates for elimination, tuning or replacement.

The result of this effort would be a VM that could only run pure Ruby code (no Ruby+Java interop, no calls to any JavaSE APIs). It would also lack lots of Java Platform facilities - the security model (protected core/unsafe APIs); JMX management; high scalability (by dropping advanced perf libs like JSR166 completely)... But these are not Ruby features, the resulting VM would still beat CRuby easily on any deployment quality checklist. And I didn't even talk about trimming the VM itself, there are LOADS of HotSpot features that may be possible to remove by just #undef'ing and rebuilding. Stuff like strictfp maths, JVMTI, most of JNI, multiple choices of GC, dozens of logging/dumping/monitoring/checking switches (did you see the full list of HotSpot options? it's bewildering).

We could then have two deployment options for JRuby: 1) A "server-side" JRuby-over-JavaSE with HotSpot Server, full Java APIs, Java interop, deployment on Java EE application servers etc.; 2) A "Pure and Light Ruby" with the customized JRE, good for interactive use, small scripts, simple apps, and even for deployment of large pure-Ruby apps on Ruby-centric infrastructure (its own webservers, DBMS drivers etc.). This reduced JRE (if based on JDK 7 with its new dynamic glory) would probably be at least as fast as the best native Ruby VMs (for language-centric benchmarks like GCLS), and then it would be OUTRAGEOUSLY superior in other performance/scalability aspects (GC, multithreading), not to mention robustness.

This idea of trimming/customizing the JRE would of course be easier if the current JRE was well modularized, which is not true at least for the Java APIs. Check Mark Reinhold's recent blog. He doesn't mention the VM, but it seems to me to be in better shape.

Notice that this idea could work for other dynamic/scripting languages, like Python. Perhaps the users of several of these languages could get their act together and make an unified effort to derive a Lightweight Dynamic JRE from JDK7. Sun itself could do it, but I don't see Sun investing in this effort because it's not necessary for server-side apps, and Sun is still focused in that side with their dynamic language initiatives (e.g., JRuby/Rails apps on Glassfish).

Malson said...

Headius,
JRuby is something you work hard for and result speak for itself. It should be something you and the world should be proud of and hence I do not see why there are some developers here that express discomfort over your tone.
I do not see your tone as been arrogant becaue it is just your personality as you always speak and write in this tone.
No amount of words can describe you and your mate's contribution and achievement to the Ruby's world.
Do retain your individuality and personality because that is what make you a interesting genius.

Your sense of pride and "showmanship" are something seriously lacking in many developers'blog because they are over-conscious of offending the reader that they suppress their joy and pride. Little did they realize that joy, pride and ego may be factors that drive them to their goals. And hence by suppressing those innate "tone", they are missing a good part of been a great developer.

Keep it up !

Roger Pack said...

I like Jruby. I might start to prefer it over MRI, simply because of the stable GC [though I'll admit some attempts have been made to help MRI currently]. That being said, could you please direct me toward benchmarks showing that jruby is faster "in most cases" than 1.9? [re: cagiano's benchmarks, alioth shootout, etc.] Let's be honest with ourselves here...
Thanks!
-=R

Anonymous said...

Charles I love JRuby! Used it on Netbeans and it was great. Fast and everything works. I love the fact that I can call Java from ruby. Excellent excellent work. I initially doubted your project would work. You have shown everybody that your choice was the wisest.

One tip though, you have got to be a bit careful about how you come across. You tend to rub people the wrong way. Your a bit competitive so you have a tendency to run down other implementations. You will go a lot farther if you are careful about things like this.