Hacker News new | past | comments | ask | show | jobs | submit login
Massive Ruby speed boost with JRuby and Java 7. (jruby.org)
147 points by donw on Dec 20, 2011 | hide | past | web | favorite | 78 comments

I've been eyeing jruby for quite a while but never had much success with it. There seems to always be the odd gem not supporting it (e.g. jruby-openssl is broken which a lot of my stuff depends on).

OpenSSL is a terribly complex library to support, but we've managed to steadily improve our pure-Java port (originally a herculean effort by Ola Bini, it wraps bouncycastle in an OpenSSL-lookalike API). There's still probably issues, but we have tried to fix them as they come up.

Yes, that was no criticism, I'm hoping jruby will succeed.

Fwiw, I'm currently blocked on http://jira.codehaus.org/browse/JRUBY-6260

Apparently the fix is already done but hasn't made it into the 0.7.5dev gem yet.

Invoke dynamic means less of a need for C-Extensions, which is probably one of the largest sources of pain for JRuby compatibility. The JVM is better at running plain old ruby code now, so there's less need to go outside of it with C or Java.

Well, you don't need extensions, but lots of library interfaces still use them because they haven't been updated to FFI. So sometimes you can't use the code you want. The good news is that it's usually pretty painless to whip up your own FFI binding.

We've been very lucky to have a lot of ingenious JRuby users help port some of those extensions or produce API-compatible wrappers around equivalent Java libs. There's still C exts we don't have equivalent JRuby exts for, but things are much better than they used to be.

That's definitely our hope. JRuby has come further than any implementation in making Ruby fast enough to replace C or Java, and if it's possible to write more in Ruby then all implementations will benefit. C extensions are the number one thing holding the C/++ implementations back.

jruby-openssl has worked fine for me for several years. Making outgoing SSL connections was one of the primary functions of the last app I worked on.

It could indeed be that my jruby endeavors just happened at unfortunate times. The jruby-openssl bug that I'm currently seeing is a different one from last time - and it's apparently fixed in git-HEAD already, just not in the gems yet.

So I'm looking forward to the 0.7.5-release and with a bit of luck I might finally be able to jump on the jruby-bandwagon then.

The only pain for me? The JRuby binaries are on Amazon S3 and my "expletive" company blocks Amazon S3.

That is truly bizarre.

There's probably mirrors of the JRuby downloads somewhere, eh? I don't know them offhand...

I tried to find one but I ended up "cheating". If you try and send from a gmail account it will fail but gmail will keep the file on its servers. So I can log into gmail at work, and download the binary from the failed sent message. sigh

Cute :)

In other news, I realized our "org.jruby:jruby-dist" maven artifact hasn't been publishing a full dist tarball, like it's supposed to, so we'll get that fixed.

Anyone done any comparisons between this and the official (MRI/KRI/YARV) 1.9.2 or 1.9.3?

I obviously have, but I have a lot of respect for the MRI folks and usually don't publish those results. You're free to try it out yourself. In general, JRuby should be significantly faster than MRI 1.9.2 or 1.9.3 for running Ruby code, and usually faster inside the core classes (String, Array, etc). If it's not, I consider it a bug, and encourage bug reports to that effect.

Until recently I dismissed JRuby, but then I needed to run some stress testing code against an Oracle DB. With MRI Ruby and the OCI8 library, the Ruby overhead stopped me being able to push the database hard enough. I switched to JRuby and JDBC and the results were amazing, so I am converted. The only niggle I have is the startup overhead, which I know is a JVM thing. Not a problem for my benchmarking, but a little bit of an annoyance for a command line ruby tool I use.

As an aside, I did run across a bug, not performance related, but a case around popen where JRuby doesn't behave the same as MRI - I raised a ticket (JRUBY-6225) but it doesn't seem to be getting any traction. I understand there are probably plenty of tickets to be prioritized, but this one is a bit of a show stopper for me. If anyone from the JRuby team is see this and has time to have a look at it I would be grateful.

There seem to be a few bug reports all around the same phenomenon. See also JRUBY-6162. I started digging into it a bit last week, but I was in the middle of a release and didn't have time to get very far. My working hypothesis is that the external process is getting killed off by JRuby before it has time to finish executing. The (hacky) workaround is to make your code wait a bit so the process has time to complete. Faster JRuby execution might only make the problem worse :-), but I'm looking forward to playing with it!

6162 does sound very like my issue. I must try sticking a sleep inside the block passed to popen and see if that gets past my problem. It's not a solution as such, as the process I am calling could take anywhere from a second to many minutes to execute, but it will be interesting to try. If you manage to solve these bugs it will be a great help to me, so good luck!

Recently we've been working more on JRuby master, on new features and performance. With JRuby 1.6.6 coming soon and 1.7 starting to stabilize, we'll be circling back to these bugs.

Of course, we can always use help too :)

I'd love to help, but my Java skills are almost none existent! If I thought I could make any headway into that bug I'd jump in - maybe over the holidays I will try and have a look.

Why would respect for the MRI devs stop you from publishing benchmarks? It's not an insult to say that something else is faster. I'd imagine the competition might drive them to improve if at all possible.

I feel like the MRI devs get beat on a bit too much, and they're doing the best job possible with very few paid developers. I'm more interested in showing how JRuby is improving over time than rubbing salt in the wounds.

Speaking of very few paid MRI developers, does anyone know if the Ruby project takes donations to hire full-time paid developers? If yes, could you please point us to the donations page?

After matz announced he was hired by Heroku I spoke with him on how he plans to hire developers. He doesn't want one company to have all the MRI developers, so the best way to create a new paid MRI committer is to hire one specifically to do so.

Great attitude.

Really glad that @headius is getting some recognition for his dogged work in this space. I know he's been up against a sometimes quite hostile Ruby crowd but has remained a gentleman throughout.

Indeed! He has been super helpful and friendly.

I'm not really familiar with the ruby development process and community and haven't used the language much, but I could imagine the MRI developers spend considerable time advancing the actual design of the language (syntax, standard APIs, etc.) whereas the other Ruby implementations "only" need to implement that design and thus can maybe focus more on performance. If MRI were to die, that design work would still need to be done in order to push the language forward. As such it could be seen as a symbiotic relationship, not competition.

Yes, I'd say that's at least partially true. The innovation on JRuby's side is in new ways to integrate with the JVM, rather than in API or language design. MRI folks need to innovate both in how they design the language and APIs and in how they implement them.

On my computer Linux 3.0.0-15-generic #24-Ubuntu SMP Mon Dec 12 15:23:55 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux RAM=6G "Intel(R) Core(TM)2 Quad CPU Q8200 @ 2.33GHz"

A simple comparison using binary-tree (from computer language benchmark) between ruby1.9.1 and jruby show that on my machine jruby time is half of 1.9.1

~/lang/ruby/jruby-1.6.5/bin$ time ./jruby --server -J-Xmx2000m algo.rb 18 gives real 0m32.447s user 0m38.478s sys 0m0.904s

time ruby1.9.1 algo.rb 18 real 1m11.388s user 1m11.192s sys 0m0.148s

./jruby --server -J-Xmx2000m -v jruby 1.6.5 (ruby-1.8.7-p330) (2011-10-25 9dcd388) (OpenJDK 64-Bit Server VM 1.7.0_147-icedtea) [linux-amd64-java]

the algo.rb file is the version contributed by # contributed by Jesse Millikan # Modified by Wesley Moxam and Michael Klaus

this is n=18, for n=16,17 ... results are similar. Half the computing time.

"Compared to JRuby on Java 6, JRuby on Java 7 without invokedynamic is around 25% faster, and JRuby with invokedynamic is nearly 3 times faster."

JRuby already is as fast or faster than MRI, and a 3X increase in performance is absolutely huge.

a) There will be problems in the Computer Language Benchmark. Like with the excellent LuaJit, they only allow one version of a language, so a war of implementations to appear would not be surprising.

b) Now that Python is in a difficult transition to Python 3, a strong ruby is a game changing point. Antonio Cangiano `New Relic Holy Shmoly, Ruby 1.9 smokes Python away!` could be Jruby smokes Python away :) (if there is a two-fold increase in speed I expect A. Cangiano article rewrited)

c) What about duby and groovye?

Duby has become Mirah, and though I have not personally had a lot of time to work on it, it has continued slowly forward. It is basically just Ruby syntax for writing Java, though, so it performs identically to Java.

I don't know the status of Groovy performance in general. I do know that on small numeric benchmarks, JRuby + invokedynamic beats fully-dynamic Groovy, but you can "cheat" and static-type some numeric logic in Groovy, which puts it out in front again.

b) Also Jython is virtually dormant, at some older version of Python, while both JRuby and IronRuby are actively worked on.

c) Groovy hasn't added any support for invokedynamic or even started on it. A month ago they announced they're removing their meta-object protocol rework from the upcoming version 2.0.

SpringSource seem to be repositioning Groovy as a staticly-typed language, this year adding primitive types like in Java. Before everything was an object, and primitives were regarded as leaky abstractions. 2 months ago they employed someone to write a static type checker called "Grumpy" for Groovy, and eventually type inference and optimization, but the work's just started so it might take quite some time depending on how good their new hire is. This year they also tried to rewrite the Antlr 2.7 based parser in Antlr 3.2, using a Google Summer of Code worker, but they didn't get very far. Perhaps the static type checking for Groovy will be more successful, perhaps not.

I imagine this repositioning was instigated by the Grails team wanting a faster language, with dynamicity an optional extra to be used only when required.

I find it a bit of a dodge when a dynamic language has to go to static types for performance. That's not to say I haven't wanted to have that dodge available to JRuby users, but being unable (or unwilling) to unilaterally add optional static types to Ruby, we've been forced to find more creative options. We'll probably never approach Java's raw primitive math performance, but I think we can easily match Java's speed (and in some cases, already do) at manipulating objects.

It also appears that the push toward more static typing in Groovy may come at the cost of dynamicity...or at least incompatible changes in Groovy's dynamic features. Perhaps it's the best move for them, though; they've always pushed Groovy's ability to run (and enhance) plain old Java code, but performance was considerably worse than Java.

It's unfortunate that other JVM dynamic languages did not start exploring invokedynamic sooner. It has been a complete game-changer for JRuby.

Oh, and regarding JRuby versus Python 3...yes, if Ruby 1.9 smokes Python, and JRuby is faster than Ruby 1.9, then JRuby should smoke Python even more. I have not done the comparisons myself, though.

> if Ruby 1.9 smokes Python

A big IF? :-)


I know some of the implementations there use native libraries that are built into python but only available via RubyGems for Ruby, like numpy. That makes it less of a language shootout (at times) and more of a "who ships the best C libs" shootout.

I believe MRI is generally faster than Python now when they're doing roughly equivalent work in Ruby and Python.

> use native libraries that are built into python


> I believe MRI is generally...

Show me the numbers ;-)


Too cryptic!

Except that Python has scipy,numpy and cython in its camp. I have spent the last six months of my life making Python code run faster. You can write slow code very quickly in Python. If your users complain about it being too slow you can implement some critical sections in Cython. (again with very minor changes). That being said there is a overhead to calling functions in Python which I would like addressed. (Dictionary lookups were three times as fast as function calls when I measured them).

> There will be problems in the Computer Language Benchmark.



> Ruby 1.9 smokes Python away!

"I ran a recursive Fibonacci function..." Really.

I wonder how long until the JRuby vs PyPy "benchmarks" start appearing.

I have not got round to playing with JRuby but if you can squeeze 10-30% extra performance then I am keen!

I'm a big fan of JRuby for two notable reasons. First, JRuby runs on the JVM, which is arguably the most mature and tested VM on the market today; second, you can leverage both the Ruby and Java ecosystems without any additional overhead (the Java interop is almost seamless).

Being able to write classes in Java (or Mirah) where speed is crucial, and use them from within JRuby without having to do any additional work, is a nice bonus as well.

The only downside is startup time, but that's been improving a lot recently.

I believe Rails can also run on the JVM via JRuby. I wonder what the benchmark is between Rails/JVM and Grails 2.0 ???

Rails 2 and Rails 3 run beautifully on JRuby.

Github:FI (the locally-installed version of Github) was, for instance, a JRuby application.

Rails runs quite well on JRuby, indeed. I have no information on its performance relative to other frameworks, though.

It is also important to mention that java7 G1 garbage collector gives much better results for certain installations (of course test it before deploying it on production). The pauses are much more predictable and, in my case, much shorter.

Here is how to get Oracle Java7 on Ubuntu:

  wget http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz
  tar -xvf jdk-7-linux-x64.tar.gz 
  sudo mkdir -p /usr/lib/jvm/      
  sudo mv jdk1.7.0/ /usr/lib/jvm/
  sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.7.0/jre/bin/java 0
  sudo update-alternatives --config java

G1 is definitely an interesting piece of work. It's geared toward very large heaps where you need more predictable pause times, and is intended to replace the concurrent mark/sweep GC.

To enable G1 on Java 7: -XX:+UseG1GC. Prepend -J if passing to the 'jruby' command.

maybe I'm a weirdo, but I care a lot more about memory consumption than processing performance. Starting up irb with jruby-head and java 7u2 uses around 80MB. 80MB for vanilla irb!

MRI uses around 8MB

Edit: I get down voted for saying that I care about memory consumption? My use case may not be everyones, but it's a legitimate issue is it not?

Memory is cheap?

JRuby largely uses more memory because we have a "real" garbage collector. The JVM, unlike MRI, allocates much more memory than the app needs. That gives it freedom to delay GC runs and push old objects into rarely-GCed sections of memory. It does also mean JRuby uses more memory on startup.

JRuby also has the JVM's subsystems booted and considerably more complex caching and optimization logic. This does increase the base size of a JRuby runtime.

It's possible to get JRuby to start in under 30MB if you explicitly force the JVM to use less memory with -J-Xmx30M or lower. But is it that big a deal? Let the JVM breathe, and your code runs better as a result.

First, I'd like to say that I use JRuby a lot, and you guys have done an amazing job on it. I complain because I like JRuby, and wish I could use it more :)

I did set the Xmx and it still uses around 80MB. I'm not sure what in the world the JVM is doing there, but the permgen is another 20MB or so and then other native stuff, thread overhead, etc.

Is memory cheap? Yes and no. At work we have a rails site with two REST api backends both ruby. I also love RubyMine. Now here's my problem, I can't run all of those on the JVM on my macbook air. 4 gigs is not enough if I also have a browser open!

This is not a problem at all if I switch the apps to MRI and switch my editor to a non JVM based editor. I think the JVM people need to recognize this problem.

I understand that most of the performance boost comes from the the invokedynamic optimization, not from anything special inside JRuby.

So, does this mean we can expect the same goodness for other dynamic JVM languages? Clojure maybe?

there is the need for the implementation to actually make use of invokedynamic, otherwise there would be no gains. As for clojure: http://news.ycombinator.com/item?id=2928285

FYI: If you want to use JRuby 1.7.0-dev (snapshot) with rbenv/ruby-build see https://github.com/sstephenson/ruby-build/issues/105

I just update to java7 in ubuntu 11.04 then using

sudo update-java-alternatives

there is an error: no alternatives for -javaplugin.so

Your updated Java 7 is probably a 64-bit version. There's currently no Java applet browser plugin for 64-bit (and I'm not sure if there's plans to create one). If you don't mind missing out on Java applets, you'll be fine.

A silly question: Can I call jruby or jirb with something like classpath?

I don't want to lose my java applet for firefox,(ubuntu 11.04 64 bits,four-cores).

I would also like to see some comparison, for example like in the computer benchmark game between jruby and other ruby implementations.

What about clojure and scala? will they get a speed up with the new java 7 update? , Will it be three-fold like jruby (in certain cases?). Thanks for the great work on jruby. Being more dynamic is great!

   $ man update-java-alternatives 
    Limit the actions to alternatives belong to a  runtime  environment, not a development kit.
    Limit the actions to alternatives belong to the headless part of a runtime environment.
    Limit the actions to alternatives providing browser plugins.

You can certainly just call JRuby directly with the 'java' command, or just set your environment to point at the Java 7 install only when you need it, rather than forcing your whole environment to use Java 7 by default.

I imagine the computer benchmarks game will update when we have a release of JRuby 1.7 out, sometime early 2012. If someone else wants to do some benchmark runs, I have no objection.

As for Clojure and Scala...they may see modest improvements from Java 7, but neither of them use invokedynamic at all. The biggest boost for JRuby comes from invokedynamic.

Is this the case for Java 7? It's not the case for Java 6. There is one from Oracle, which I'm using.

  $ file .mozilla/plugins/libnpjp2.so 
  .mozilla/plugins/libnpjp2.so: symbolic link to
There's one from OpenJDK as well.

  $ apt-cache show icedtea-plugin
  Package: icedtea-plugin
  Source: icedtea-web
  Version: 1.1.4-1
  Installed-Size: 276
  Maintainer: OpenJDK Team <openjdk@lists.launchpad.net>
  Architecture: amd64

Perhaps IcedTea (RedHat's fork of OpenJDK) has included a 64-bit plugin. I did not think OpenJDK/OracleJDK had done so yet...but I'd love to be proven wrong.

Like I said, libnpjp2.so is a 64-bit plugin from Sun/Oracle. It's been available from Sun since Java 1.6.0_12 in 2008. It's still available in Java 1.6.0_29; I just downloaded and installed it from Oracle last week. I have not looked into support for Java 7 or operating systems besides linux.


  $ update-java-alternatives --jre-headless

  $ update-java-alternatives --jre
this will not update the plugin-alternative which is not available (yet), see headius' comment

  => https://gist.github.com/1501186

(UPD: sorry, the following only applies to the standard OpenJDK7 on Ubuntu, not the u2 release )

also on i386 and armel see https://bugs.launchpad.net/ubuntu/+source/openjdk-7/+bug/850...

workaround (change armel/arm to i386 if that's your arch):

  $ cd /usr/lib/jvm/java-7-openjdk-armel/jre/lib/arm/

  $ sudo ln -s jamvm/libjvm.so .

That applies to ARM. I'm not sure of the equivalent workaround for x86_64 outside of installing a 32-bit Java 7 and setting that up as the source of the Java plugin.

Fair enough :)

Slightly off topic but does anyone know if Java 7 would allow fork to work in JRuby?

how does eventmachine run on jruby and does anyone actually use jruby and eventmachine in production?

EM does work, but it's not commonly used on JRuby. I think the fact that JRuby has real parallel-executing threads makes eventing frameworks like EM less interesting...especially since EM only runs a single event loop.

I'd love to hear about people actually using EM on JRuby, if there's such folks out there.

10-30% faster is “massive”? Ruby standard is 5-30 times slower than all other popular language implementations, I'd expect at least a 100% increase to be considered “massive.” I'd call this just “a speed boost.” Anyway, keep it up.

A quick look at the programming language shootout (http://shootout.alioth.debian.org/) puts Ruby in roughly the same performance league as Python and Perl, and you'd be hard-put to argue that these aren't popular languages.

You must have missed this part:

"Compared to JRuby on Java 6, JRuby on Java 7 without invokedynamic is around 25% faster, and JRuby with invokedynamic is nearly 3 times faster."

Three times faster == 300% speed increase. In your terms that would mean a _really_ massive upgrade.

No, it's a 200% speed increase. If the speed before was 1 unit, it's now 3 units. That's 200% more.

So if I say it's one time faster, isn't it a 100% increase?

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact