Slow view rendering and absolutely terrible assets:precompile on JRuby


#1

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


#2

Try running the cpu sampler in visual vm (http://visualvm.java.net)
against your app to see what’s using up the most time.


#3

org.jruby.RubyThread.lockInterruptibly is where it’s spending 70% of
time
(cpu time there is 0.000 ms though). Most of the cpu time is spent in
org.jruby.util.io.SelectBlob.doSelect().

Not entirely sure how to read the output… I’m not an expert on
VisualVM
(or the JVM) really. Not seeing haml or anything else showing up at the
top.


#4

John -

Firstly, I just want to say, wow, you’ve been amazingly thorough in
pursuing this problem.

The dramatic differences you’re experiencing between MRI Ruby and JRuby
(especially with rake) make me wonder if there’s something going on with
your Java installation(s). If JRuby were slower at all, I would expect
it to be only a little slower, nowhere near your experience.

Is there anything you can tell us about the machine on which you’re
testing (e.g. OS)?

Is the total memory enough to support the increased settings you tried?
I tried using a ridiculous number, and it didn’t complain:

java -Xmx20000000m -jar sample-rails-app.war

Also, with whatever memory you have to work with, I’d suggest allocating
almost all of it to heap rather than stack.

Is it possible that JRuby and/or Java are installed on volumes that are
slower than the one on which MRI Ruby is installed?

You report similar problems on the production machine; is this the case
for both the rails rendering and the rake task? Does the speed
difference also occur when running other Ruby apps, subtracting the JVM
startup time, in my case, 3.19 seconds:

rvm 1.9
time ruby -e “”
ruby -e “” 0.03s user 0.01s system 8% cpu 0.474 total

rvm jruby
time ruby -e “”
ruby -e “” 3.22s user 0.19s system 182% cpu 1.868 total

Are you using rvm, by the way? Using SELinux or anything similar that
might be inserting itself into the running of the process?

Regarding the Rails app benchmark, I don’t know how significant, but I
wonder if testing the JRuby app on a Java web server would be a fairer
comparison than using Webrick on JRuby. Webrick is known to be slow, so
running the web server code in Ruby instead of Java may unfairly bias
your results. In order to make running a Java web server easy, I tried
running warble executable on your project, but when I ran the resulting
war file I got this error:

java -jar jruby-view-rendering-master.war
Nov 25, 2012 1:34:12 PM winstone.Logger logInternal
INFO: Beginning extraction from war file
Nov 25, 2012 1:34:17 PM winstone.Logger logInternal
WARNING: No webapp classes folder found -
/private/var/folders/ll/nyv2lhd08xl8lf0059bmbrw80000gp/T/warbler5205388911019780837webroot/jruby-view-rendering-master.war/WEB-INF/classes
Nov 25, 2012 1:34:17 PM winstone.Logger logInternal
INFO: Winstone shutdown successfully
Nov 25, 2012 1:34:17 PM winstone.Logger logInternal
SEVERE: Container startup failed
java.lang.ClassCastException: org.jruby.rack.RackFilter cannot be cast
to javax.servlet.Filter

Also, when I tried to run rake assets:precompile in either MRI 1.9.3 or
JRuby 1.7, I got:

time rake assets:precompile
rake aborted!
Don’t know how to build task ‘assets:precompile’

I didn’t get either error when using a ‘rails new’ -ly generated Rails
app. I’m not really a Rails developer (yet) though, so I may be missing
something simple. Hope you don’t mind the stabs in the dark, I’m hoping
something might pay off.

  • Keith

Keith R. Bennett
http://about.me/keithrbennett


#5

Sure. My development machine is a Macbook Pro Retina Quad-Core Intel
Core
i7 with 16 GB Ram - OS X 10.8.2. The production machine is an Ubuntu
12.04
7 GB Ram running on EC2 (so - virtualized). I have the same problem in
dev
as in prod.

JRuby is installed on the same volume as MRI. In dev that is a 256 GB
SSD,
in prod it’s an EBS-backed volume. Heh - funny how my development
machine
is probably faster than my production server.

The test app on github isn’t for testing assets:precompile (sorry, I
might
have been unclear) - it’s ONLY for the view rendering part. As I said -
I’m
testing on webrick, but the original concern came from running on either
Unicorn or Trinidad (Tomcat wrapped in a bit of ruby). In production we
run
on Trinidad. It was simply easier to run on webrick since the results
were
more or less the same as on Trinidad. I’ve also tested on Torquebox with
about the same results - eg. MRI is faster.

I’m not using RVM but rbenv in both dev and prod. The slowness (100x
slower) running assets:precompile seem to be mostly due to therhubyrhino
running completely unoptimized as reported by Charles Nutter here:
https://github.com/cowboyd/therubyrhino/issues/23. Of course, one-off
commands such as rake will be slower than MRI - but 100x slower while
using
10x the ram? That’s simply unacceptable. As I said, using nodejs for
assets
compilation together with turbosprockets-rails (
https://github.com/ndbroadbent/turbo-sprockets-rails3) has made it
almost
bearable… but compared to MRI it’s still like night and day. It’s
still
uses LOTS more memory and is in general… perhaps half as fast,
probably
slower.

So the end result is very mixed. The good stuff is that we get great
java
integration, pretty awesome deployment possibilities, and a somewhat
simpler deployment. The bad stuff is that the app is slower overall (how
it
“feels”), and I’ve got the numbers to back that up, the deployment
(while
simpler) is MUCH slower and uses LOTS more ram. And I’ve found out the
hard
way that Trinidad (and, as I’ve read, other java server containers) leak
memory on redeploy so the zero-downtime we’ve enjoyed on unicorn is a
bit
less “zero” on JRuby (since a full restart of Trinidad is needed every
now
and then).

Unfortunately, so far, JRuby has meant less good than bad which is the
reason we might move back to MRI and Unicorn for the time being… I
really
WANT to run on JRuby because of it’s potential though. I’m not sure if
my
experiences are because of some bad configuration on my part or if
they’re
something other people are seeing - and I guess I’m interested in the
experiences of other people who’ve moved their Rails apps from MRI to
JRuby.

Thanks,
John


#6

Tested on jruby 1.7.0 and jruby 1.6.8, OpenJDK 7 and Oracle JDK 7.
MRI was 1.9.3-p327.
I’ve tested the actual time shown in the rails logs and the time a
request takes using curl. In general it’s slower on JRuby regardless of
JRuby version/JDK version, web server or OS. I see the same thing on
webrick as on Trinidad, same thing on Macbook Pro 15 Retina with SSD/Mac
OS X 10.8.2 as on Linux Ubuntu 12.04 on EC2/EBS.

The “feel” of the app on jruby in both prod/dev is “more sluggish” on
JRuby and the numbers back that up.

29 nov 2012 kl. 05:02 skrev Anthony J. removed_email_address@domain.invalid:


#7

On Sun, Nov 25, 2012 at 6:10 AM, John Axel E. removed_email_address@domain.invalid
wrote:

org.jruby.RubyThread.lockInterruptibly is where it’s spending 70% of time
(cpu time there is 0.000 ms though). Most of the cpu time is spent in
org.jruby.util.io.SelectBlob.doSelect().

Not entirely sure how to read the output… I’m not an expert on VisualVM
(or the JVM) really. Not seeing haml or anything else showing up at the top.

That’s just part of the IO subsystem, so I doubt that’s what’s causing
your
slowdown. I did some profiling locally to see if I could replicate the
behavior you’re seeing, but unfortunately, I can’t. I did isolate the
view
rendering in my tests though, so perhaps the issue is just further up
your
stack.

For comparison, here’s what I’m seeing (MRI is ruby-1.9.3-p194, JRuby is
1.7.0, JVM is Oracle’s 1.7.0_07. I embedded a controller in a benchmark
and ran through repeated iterations of rendering the view. 1000 warm-up
iterations, then 1000 timed iterations, with results broken into 100
iteration samples. You can see that even after 1000 iterations, the JVM
is
still optimizing a bit, but by 2000, seems to have reached a steady
state.
Not sure why it’s taking that long to fully optimize, I haven’t dug into
the numbers further than just doing the sampling, but once it does reach
a
steady state, JRuby is about 30% faster than MRI. Enabling
invokedynamic
didn’t change performance appreciably, nor did moving the LogEntry.all
allocation to either a single-time shared allocation, or a per-render
allocation.

Either you didn’t mention, or I missed it. What JVM are you running on,
both in your test environment and production?

MRI:
[…truncated a bit, since MRI’s performance doesn’t change much…]
Sample mean: 0.044296849999999985; Sample stddev: 0.010034992645606094
Sample mean: 0.043826000000000004; Sample stddev: 0.009419012821220481
Sample mean: 0.043669299999999994; Sample stddev: 0.009377437996539747
Sample mean: 0.043847399999999995; Sample stddev: 0.009612958356192684
Sample mean: 0.044089550000000005; Sample stddev: 0.009703262210395235

JRuby:
Sample mean: 0.05607000000000001; Sample stddev: 0.018613699201926466
Sample mean: 0.030839999999999996; Sample stddev: 0.003719237458502387
Sample mean: 0.028590000000000008; Sample stddev: 0.0014361494534084027
Sample mean: 0.02932; Sample stddev: 0.0053689718369403444
Sample mean: 0.028150000000000026; Sample stddev: 0.001479660075623431
Sample mean: 0.028940000000000014; Sample stddev: 0.0026621932174943123
Sample mean: 0.028880000000000013; Sample stddev: 0.004369961745435038
Sample mean: 0.02925000000000002; Sample stddev: 0.0053207711904014535
Sample mean: 0.02742; Sample stddev: 0.0011562740578987733
Sample mean: 0.027309999999999997; Sample stddev: 0.0010019677609281624


#8

On 24.11.2012 09:51, John Axel E. wrote:

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

This has been a source of pain for me as well. I did some profiling
work, and
found much time being spent dealing with exceptions-as-flow-control.
This
anti-pattern is a performance drain on MRI, but is even more apparent
on JRuby.

I’d been meaning to come up with a better benchmark for sometime now,
and here
it is:

https://gist.github.com/4234608
https://github.com/pmahoney/assets_comp_perf

Still a lot of improvement to be made (though even MRI is painfully
slow).

I admit I don’t quite understand why “assets:precompile” is hardly
faster
than “assets:clean assets:precompile”, so perhaps I don’t know enough
to even make a proper benchmark… I hope to be able to keep digging
into this, but that seems unlikely any time soon.


Patrick M.


#9

Last update on this is that we had to give up. JRuby was simply too slow
for us to justify, switching back to MRI was like getting a new server
with
double everything. It’s just ALOT faster. I know this isn’t what some
benchmarks claim but it’s the truth for us.

Some things on JRuby are so slow that they’re completely, utterly
unusable… like rake assets:precompile for example - MRI sometimes
takes
almost two minutes to compile which to me is unacceptable, JRuby on the
other hand can take anywhere from 15 minutes to infinity (eg. I’m not
gonna
sit around watching it chug away for more than an hour - that process
gets
killed). We did everything we could to speed up the assets:precompile,
for
example, not doing all of it - just the parts we needed, switching to
nodejs as compiler for the assets, using turbosprockets. All those
helped a
bit but still unusable i.m.o and much much slower and eating MUCH more
memory than MRI(that is compared to MRI without any weird patches to
make
things go faster).

So - deployment of the app wasn’t working for us, we must be able to
deploy
a new version in at most 2-3 minutes (preferrably faster than that).

Another issue was the overall sluggishness of the app, it just felt alot
slower on JRuby than on MRI. Views rendered in double the time for
example.

A third issue was the fact that on MRI (just plain old 1.9.3-p125, no
patches) we use quite a bit less memory than Trinidad+JRuby. The app on
Trinidad leaked memory on every redeploy - something that doesn’t happen
on
unicorn. With unicorn we could do real, zero-downtime deploys. On
Trinidad… well, not so much. I didn’t dare turning that option on
since
it leaked ridiculous amounts of memory on “normal” redeploys where
requests
are paused.

The final decision to go back to MRI was made when we tried out JRuby
1.7.1

  • for some reason the assets were ALWAYS missing on 1.7.1 (even though
    we
    could clearly see them sitting there on disk). For some reason
    rails+jruby-1.7.1 decided that the hashes of the assets were different
    from
    what was on disk. Running assets:precompile a few times (on 1.7.1 - even
    the full compile without turbosprockets or any other tricks) was
    useless.
    Removing all assets and starting from scratch was useless. Our app was
    just
    down. We needed to upgrade to 1.7.1 because of
    https://jira.codehaus.org/browse/JRUBY-7008.
    Fortunately our first foray into JRuby on Rails was a management app
    that
    no customers access - we only use it internally. We wanted to try this
    app
    in production first to see what worked and what didn’t since this is a
    low
    risk app running on only one server.

Basically, moving one of our Rails apps to JRuby meant:

The bad:
Half the performance of MRI rendering views
Sluggish “feel” overall
Unusable assets precompilation (takes “too long to infinity” on JRuby
even
with every conceivable tweak or hack to the asset pipeline)
Uses a lot more memory than Rails on Unicorn
Leaks memory (eg. needs complete Trinidad restart now and then - which
means downtime)
Frustratingly high memory usage when running command-line tasks (like
rake
tasks)
Frustratingly slow startup - rake tasks aren’t much fun (they’re slow on
MRI too i.m.o but JRuby is like 5x-20x as slow, except for assets
compilation which is possibly the worst thing I’ve ever experienced)
Incompatibilities/bugs (yeah well - MRI has bugs too)
JRuby 1.7.1 + Rails wouldn’t render assets anymore (claimed they didn’t
exist)

The good:
Java integration (something we may need)
A better concurrency story

I’m really sorry that this was our first experience running Rails on
JRuby

  • we really wanted it to work and work faster, deploy easier, be less
    buggy, have a better concurrency story. None of those (except for maybe
    concurrency) were true - rather the opposite. Before moving the app to
    JRuby I had read a few success stories, some of which claimed that view
    rendering would be faster on JRuby. I did know about slow startup times,
    but it was in production it that got really frustrating for us. And
    again -
    assets:precompile isn’t usable on JRuby at all while on MRI it’s at best
    bearable. I wouldn’t ever accuse the rails asset pipeline compilation of
    being fast.

I don’t want to be too hard on the JRuby devs - I know how much hard
work
is behind JRuby and I really like the whole idea of running on the JVM
(and
we still run a non-rails, non-web app on JRuby with success)… I just
wanted to let you know that our experience moving a Rails app onto JRuby
was far from pleasant.

I don’t understand how we could have such a bad experience when I’ve
heard
so much good about JRuby and JRuby on Rails in talks, on blogs and other
places. I also know of several places running JRuby on Rails in
production.
Either they keep silent about their issues or… I don’t know - I’m
still
open to suggestions and I guess something, somewhere could possibly have
been tweaked in some way - though we’ve been running the app on jruby
for
three weeks or more in production and I’ve been tweaking and hacking
away
quite heavily with mostly nonexistent good results, we’ve tried OpenJDK
6,
OpenJDK 7 and Oracle JDK 7… We were out of ideas and options when we
switched back to MRI.

Thanks,
John


#10

I’m not knowledgeable about the issues raised in this thread, but I do
know that JRuby is used with Rails in production at sites all over. A
week or two after giving a JRuby talk at Bangkok’s Rails/JavaScript
meetup, a European developer told me he switched his business’ site to
use JRuby and it greatly simplified his operation. He seemed happy with
the performance as well.

I’m not at all discounting the experiences recounted on this thread, I’m
just saying that, based on what I’ve seen, it would be inaccurate to say
that JRuby and Rails do not work well together.

It would certainly be nice if one/some of the authors addressed this
thread. I did raise the issue with Charlie Nutter when he spoke at Ruby
Hangout (on YouTube at
https://www.youtube.com/watch?feature=player_embedded&v=SmBDBDRX-jg,
sorry, I don’t know where in the video this is), and he acknowledged
that there are issues.

  • Keith

Keith R. Bennett
http://about.me/keithrbennett


#11

Here’s a baseline time for JRuby and assets:precompile for a Rails
application with a small to medium amount of assets on my local system:

$ time rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile

real 2m22.891s
user 4m30.941s
sys 0m6.728s

There are several options for speeding this up. If your JVM supports it,
run JRuby in 32-bit mode - “-J-d32”. Try turning off all compilation and
running purely interpreted - “-X-C”. Combining these options cuts a
large percentage of the time off in my tests.

$ time JRUBY_OPTS="-J-d32 -X-C" rake RAILS_ENV=production
RAILS_GROUPS=assets assets:precompile

real 1m41.990s
user 1m52.771s
sys 0m2.969s

If you have the ‘node’ binary available on your system, you can tell
Rails to use that instead of therubyrhino for asset compilation and cut
off even more time.

$ time EXECJS_RUNTIME=‘Node’ JRUBY_OPTS="-J-d32 -X-C" rake
RAILS_ENV=production RAILS_GROUPS=assets assets:precompile

real 0m29.981s
user 0m32.636s
sys 0m2.880s

If you don’t have the node binary available then you can still gain a
large boost by switching from uglifier to closure-compiler for
minification by setting config.assets.js_compressor = :closure in
config/application.rb. The size of the result assets is almost
identical, at least in my case.

$ time JRUBY_OPTS="-J-d32 -X-C" rake RAILS_ENV=production
RAILS_GROUPS=assets assets:precompile
real 0m44.692s
user 1m16.724s
sys 0m3.262s

For reference, Ruby 1.9.3 can compile assets for the above application
in 14.314 seconds. With a bit of tweaking I got JRuby from 143 seconds
down to 30 seconds. Still not as fast as Ruby 1.9.3, but much closer.

Ben


#12

Hey guys.

I just want to pick up this thread again. It has been real
interesting to read about the problems people have had with
Jruby+Rails and it’s looking like it’s just not a good fit at all.
So is this the general consensus then? That Jruby is not a good fit
for rails? Are there alternatives to make the view rendering faster?
Has anybody tried using markaby or something similar? Haml? What about
the asset pipeline? Any alternatives? What about other frameworks
like sinatra?

I think for the future googlers we should clean this thread up. Either
conclude that jruby should be avoided with rails or find the magic
incantation that will make it work.

Thanks.


#13

As someone who uses jruby on rails, but without views, this is really an
interesting thread. Could someone put together a simple github repo that
reproduces this, with the speed ups?

I’d love to see that plus a blog article. Given how prevalent rails is,
this seems like an important limitation to make widely known. If only so
the next dev knows it’s not her fault.