Are there still benefits to running JRuby vs. the latest MRI with Puma?
Asked Answered
L

3

7

I'm considering updating our ruby interpreter to JRuby, it's been quite a headache because we've had to remove any 2.x specific syntax from our app and resort to ruby 1.9.3 compatibility. Which isn't the end of the world.

When it came time to run the app, I found out that we cannot use Puma in clustered mode. The question is, given all the fixes and changes to MRI in the past few years, are the benefits of having "real threads" still valid?

update

To make this more objective, the question is, "Does the latest version of MRI negate the need to adopt JRuby to achieve the same benefits that native threads give you?"

Lindane answered 23/9, 2014 at 23:57 Comment(2)
hey... interesting question, but it's likely to hit the "primarily opinion-based" flag... just sayin'Sang
I don't think it is opinion base. We are lacking more evidence, in particular experimental evidence.Kent
K
9

Does the latest version of MRI negate the need to adopt JRuby to achieve the same benefits that native threads give you?

The answer is no. It does not negate the need, and it depends on your application as mentioned in other answers.

Also, JRuby does not allow you to run in cluster mode, but that is not really a problem in regards to your question, because it is multithreaded and parallel. Simply run in Single mode with as many threads as you need. It should be perfectly fine, if not even more lightweight.


Let me give you some references that give more insight and allow you to dig further.

This answer discusses experiments with MRI and JRuby testing concurrent requests using Puma (up to 40 threads). It is quite comprehensive.

The experiments are available on GitHub, MRI and JRuby.

The caveat is that it only tests concurrent requests, but does not have a race condition in the controller. However, I think you could implement the test from this article Removing config.threadsafe! without too much effort.

The difference between JRuby and MRI is that JRuby can execute code in parallel. MRI is limited by the GIL and only one thread at a time can be executed. You can read more information about the GIL in this article Nobody understands the GIL.

The results are quite surprising. MRI is faster than JRuby. Feel free to improve and add race conditions.

Note that both are multi-threaded and not thread safe. The difference really is that MRI cannot execute code in parallel and JRuby can.


You might be tempted to say why I answer "No" if the experiment shows that MRI is faster.

I think we need more experiments and in particular real world applications.

If you believe that JRuby should be faster because it can execute code in parallel then reasons could be:

  • The experiments should be executed in a highly parallel environment to be able leverage the potential of JRuby.
  • It could be the web server itself. Maybe Puma does not leverage the full potential of JRuby. MRI has a GIL, so why is it faster than JRuby in handling requests?
  • Other factors might be relevant that are more in depth and we did not discover yet...
Kent answered 14/5, 2015 at 3:17 Comment(3)
I haven't run your benchmarks yet, but you may want to perform some pre-warming on them; JRuby gets substantially faster once the JIT has a chance to kick in.Belew
@ChrisHeald Can you tell me more about it or do you have some references to introduce me into this "JIT can kick in". Sounds interesting.Kent
@Elyasin Just make 100+ requests to the app before starting your measurements. By default, JIT kicks in after 50 invocations of a method. The first two graphs here illustrate the gap; a warm JRuby VM outperforms a warm MRI VM, though a cold MRI VM trounces a cold JRuby VM. Since Rails apps tend to be long-running, though, it's generally appropriate to simulate how your app would perform over a long period by performing prewarming before benchmarking.Belew
P
4

Really depends on your scenario with the web-server (which you should have the very best understanding) ... case you feel your production is just serving about fine under MRI than you probably do not have that much concurrency around. puma's README pretty much explains what you get under MRI compared to Rubinius/JRuby :

On MRI, there is a Global Interpreter Lock (GIL) that ensures only one thread can be run at a time. But if you're doing a lot of blocking IO (such as HTTP calls to external APIs like Twitter), Puma still improves MRI's throughput by allowing blocking IO to be run concurrently (EventMachine-based servers such as Thin turn off this ability, requiring you to use special libraries). Your mileage may vary. In order to get the best throughput, it is highly recommended that you use a Ruby implementation with real threads like Rubinius or JRuby

... so in one sentence: ** you can have multiple threads under MRI, but you have no parallelism **

Phenocryst answered 29/9, 2014 at 13:13 Comment(2)
I did not want to put that in the edit by surprise and I did not want to create a new answer. Would it be OK for you to add a link to my answer? It is related to this question. In the experiment (about concurrency) MRI beats JRuby using Puma. The link: https://mcmap.net/q/451485/-concurrent-requests-with-mri-rubyKent
thanks but preferably (as it is currently) not ... it's notes such as those that people get an impression that JRuby is of no gain - the example seems very far from "real-world" numbers (not to mention starting JRuby in development mode while MRI goes production) and I do not see how that would make this answer clearer (actually introduces more confusion). the test case using with sleep() is unfortunate as well.Phenocryst
S
3

IMHO It depends on what your application does.

I've tested both MRI/YARV and JRuby on my Rails application. Since most of what the app does is route HTTP requests, fetch from DB, apply simple business logic and write to the DB, parallelism isn't much of an issue. Puma on MRI does handle multi-threading for blocking IO operations (DB, API). Tasks that fall off this scope (image processing, crunching report data, calls to external APIs, etc.) should probably be handled by background jobs anyway (I recommend https://github.com/brandonhilkert/sucker_punch).

Depending on your deployment needs memory consumption might be more of an issue and JRuby is very hungry for memory. Almost 2x memory in my case.

If you're deploying your application on Heroku you might find that you get more bang for the buck by being able to run 2 instances concurrently on 1 dyno.

Soutache answered 17/12, 2014 at 19:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.