Hi,
I’m new to the list though I’ve been using JRuby for around a year - not
for web apps though. We’re very happy with the jruby java integration
and
that was the reason for us to move one of our server apps to jruby
(which
was previously a java app).
Now, after a year we decided we’d try moving one of our rails apps to
JRuby
as well for easier deployment, better speed and better integration with
java (for the future). Unfortunately we’ve hit some seemingly large
obstacles. The first is that view rendering is slower on JRuby, the
second
is rake assets:precompile.
First, the view rendering - these are some results I’ve come up with
trying
to replicate a controller/view we have in our real app. The results here
are generally slightly better than the reality for us and the view
rendering is alot faster in both MRI and JRuby in this test app - but
the
difference between JRuby and MRI is about the same as in our real app.
I’ve also only run this on webrick (our real apps run on either unicorn
or
trinidad depending on ruby vm). The test app can be found here (no need
for
active_record or any persistence layer to run it):
These are my results when testing in MRI and JRuby:
– 1.9.3p327 env production –
after 1 request (for “some” warmup - eg. loading code etc)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:01:15 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (19.0ms)
Rendered log_entries/index.html.haml within layouts/application
(19.4ms)
Completed 200 OK in 21ms (Views: 20.3ms)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:01:16 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (19.5ms)
Rendered log_entries/index.html.haml within layouts/application
(19.8ms)
Completed 200 OK in 21ms (Views: 20.7ms)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:01:17 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (19.6ms)
Rendered log_entries/index.html.haml within layouts/application
(20.1ms)
Completed 200 OK in 21ms (Views: 21.1ms)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:01:18 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (28.4ms)
Rendered log_entries/index.html.haml within layouts/application
(28.7ms)
Completed 200 OK in 30ms (Views: 29.6ms)
so, MRI completes the request in 20-30 ms.
after perhaps 40 requests (MRI shouldn’t really need much warmup - but
still)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:03:28 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (17.0ms)
Rendered log_entries/index.html.haml within layouts/application
(17.3ms)
Completed 200 OK in 18ms (Views: 18.2ms)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:03:29 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (19.5ms)
Rendered log_entries/index.html.haml within layouts/application
(19.9ms)
Completed 200 OK in 21ms (Views: 20.7ms)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:03:31 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (17.5ms)
Rendered log_entries/index.html.haml within layouts/application
(17.9ms)
Completed 200 OK in 19ms (Views: 18.7ms)
Started GET “/log_entries” for 127.0.0.1 at 2012-11-24 16:03:36 +0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (20.9ms)
Rendered log_entries/index.html.haml within layouts/application
(21.3ms)
Completed 200 OK in 22ms (Views: 22.2ms)
so, let’s say MRI can complete this request in under 30 ms and usually
in
around 20 ms.
– jruby 1.7.0 (1.9 mode) env production –
after 1 request (eg. no warmup really, but should still have loaded some
stuff)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:54:37
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (90.0ms)
Rendered log_entries/index.html.haml within layouts/application
(91.0ms)
Completed 200 OK in 97ms (Views: 96.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:54:38
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (87.0ms)
Rendered log_entries/index.html.haml within layouts/application
(88.0ms)
Completed 200 OK in 95ms (Views: 94.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:54:40
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (74.0ms)
Rendered log_entries/index.html.haml within layouts/application
(76.0ms)
Completed 200 OK in 82ms (Views: 81.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:54:56
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (78.0ms)
Rendered log_entries/index.html.haml within layouts/application
(80.0ms)
Completed 200 OK in 90ms (Views: 89.0ms)
so, here it’s around 4 times slower than MRI - jruby can complete the
request in 82-100 ms
after 100 requests (let the jvm warmup some)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:56:11
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (37.0ms)
Rendered log_entries/index.html.haml within layouts/application
(38.0ms)
Completed 200 OK in 40ms (Views: 39.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:56:13
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (43.0ms)
Rendered log_entries/index.html.haml within layouts/application
(44.0ms)
Completed 200 OK in 46ms (Views: 46.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:56:14
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (32.0ms)
Rendered log_entries/index.html.haml within layouts/application
(33.0ms)
Completed 200 OK in 36ms (Views: 35.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:56:15
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (44.0ms)
Rendered log_entries/index.html.haml within layouts/application
(45.0ms)
Completed 200 OK in 48ms (Views: 47.0ms)
ok, better but still slower than MRI - jruby can now complete the
request
in around 40-50 ms
after 1000 requests (should have warmed up by now)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:58:25
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (32.0ms)
Rendered log_entries/index.html.haml within layouts/application
(32.0ms)
Completed 200 OK in 34ms (Views: 34.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:58:26
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (36.0ms)
Rendered log_entries/index.html.haml within layouts/application
(36.0ms)
Completed 200 OK in 38ms (Views: 38.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:58:27
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (32.0ms)
Rendered log_entries/index.html.haml within layouts/application
(33.0ms)
Completed 200 OK in 35ms (Views: 34.0ms)
Started GET “/log_entries” for 0:0:0:0:0:0:0:1 at 2012-11-24 15:58:28
+0100
Processing by LogEntriesController#index as /
Rendered log_entries/_table.html.haml (34.0ms)
Rendered log_entries/index.html.haml within layouts/application
(34.0ms)
Completed 200 OK in 37ms (Views: 36.0ms)
ok, even better but still slower than MRI, jruby can now complete the
request in around 30-40 ms
Now, I know these aren’t super-scientific - I’ve tried to replicate some
of
the behavior of one of our real apps. The problem is that we see slower
view rendering on jruby overall. In general view rendering has been
around
half as fast in jruby for us and the feel of the app is that it’s a bit
sluggish compared to before. In development it’s slightly worse but
doesn’t
matter as much.
My conclusion here is that MRI is faster at completing a request, eg.
latency is lower. Best case for JRuby is that it can reach around 58 %
of
the speed of MRI. The view rendering of the real app is slower than the
above (since it’s doing more). We see MRI completing requests to the
controller/view I’ve tried to replicate here in around 50-90 ms while
JRuby
completes them in around 120-180 ms(after warmup). So it seems as if the
58
% of the speed of MRI still holds true.
I’ve really tried everything on JRuby. I tried enabling invokedynamic,
tried increasing memory (even tried 2GB where I set xms and xmx both to
2GB). Tried increasing code cache, tried upping permgen. Tried
TieredCompilation, tried UseCompressedOops, tried other garbage
collectors…
Afaik I’ve exhausted most of the things you CAN do(tried both client and
server modes of course - in production we only run the jvm in server
mode).
I’ve tried openjdk 7, tried the Oracle JDK 7 (latest as of a week ago).
Tried downgrading from jruby 1.7 to 1.6.8, tried upgrading haml to a
beta
version… even tried this test app in ERB which should be faster (I
still
see jruby being slower using ERB).
So, kind of disappointing results for us. Perhaps I’m missing something
but
so far JRuby hasn’t really been that great for us when running our web
apps. The above tests were done on webrick on both MRI and JRuby but in
production we run on Trinidad (see the same results there).
The other BIG problem we’ve had is running rake tasks, some of which
complete much
slower on jruby but they at least do complete… while rake
assets:precompile has been… let’s say around 100 times slower than MRI
if it completes at all.
We were hoping to use less memory on JRuby than on MRI since we run two
apps on the same machine(on JRuby they both run in the same JVM) - the
end
result has been the opposite, especially because of assets:precompile,
we
sometimes run out of memory running that rake task… something that
NEVER
happened on MRI.
I think we’ve got ourselves out of the worst of assets:precompile by not
using therhubyrhino (which seems to be completely unusably slow) and
just
using nodejs for assets compilation. It’s still much slower than MRI
though, and still uses lots more memory than MRI.
I really really want to run our web apps on JRuby, but if I can’t
resolve
the above issues I can’t really justify it.
We still really like the possibilities of JRuby and we’ve been running
our
not-a-webapp but a server-app on JRuby since we must have the java
integration there + we’ve been really happy with it’s performance.
If you have any suggestions - please let me know. Thanks!
/John