JRuby is a Java implementation of Ruby that runs atop the Java Virtual Machine. In a previous post, we covered how to install JRuby on Windows. We also saw the commands for running and installing gems, bundler, irb and so on. Take a look at it again for the basics, if you need a refresher.
For this post, I want to start with this message that I tweeted) a couple of days after installing JRuby:
The clickbait headline would be:
"How I made my 900 second Ruby script 7% faster by only adding a single character"
The answer: "j" to run it as jruby x.rb rather than ruby x.rb :)
Of course, there's more to it – it took 3.6x memory
Not bad for near-0 effort!
jruby</a> <a href="https://twitter.com/headius?ref_src=twsrc%5Etfw">headius
— Mohit Sindhwani (@onghu) February 21, 2020
Ok, so it’s Day 1 – the day we see what changes if we want to run on JRuby.
Just run it
Most Ruby scripts that do not have dependencies on native extensions will run unchanged. Keep in mind that your basic installation of JRuby will probably give you gems for things like csv, json, rdoc, fileutils and a couple of other things. If something seems missing, try to do
jruby -S bundle install if you have a Gemfile or just do
jruby -S gem install [gem_name] directly.
Then, instead of doing
ruby [script] just add 1 letter at the start and do
jruby [script]. There is a good chance that it should work!
For reference, the script that I referred to above did the following:
- Create a JSON POST body
- Set up the request
- Make an HTTP POST request and get the JSON response
- Parse the JSON
- Store in memory to dump to a file later
The way it was run, it called an external web service 1352 times. So, of course, the time it takes is greatly affected by the external web service. The script itself is not very long but runs repeatedly – actually, this is usually something that will benefit from JRuby compiling it.
Let’s look at the statistics that we get.
We got the following results:
- Ruby 2.2: 897 seconds
- JRuby 220.127.116.11: 834 seconds
- Savings: 64 seconds (7%)
Now, I should clarify that these are unscientific tests but I ran it again for a different case and got similar results (in fact, in that case, I saved approximnately 10%). Keep in mind that this script depends on 1350 external HTTP calls which does not really depend on JRuby vs Ruby. If you ran it for something that was not going out for HTTP calls, you might have seen an even better improvement.
The caveat: the memory consumption for this script was roughly 3.6 times when using JRuby. JRuby starts out at a higher watermark and kept increasing. The script does not do much to manage memory effectively and this is expected.
In any case, the point here is that if you have a script that is likely to run for a while, you will likely see a benefit by running on JRuby – just start it as
jruby [script] and see how it goes. You might be surprised.
Running Lint for JRuby
There is a tool that helps to find problems that you might encounter when trying to run a Ruby script with JRuby – it’s called jruby-lint and you can install it as a gem. Go to the command prompt and check that you have JRuby set up properly.
e:\blog> jruby -S gem install jruby-lint Successfully installed jruby-lint-0.9.0 Parsing documentation for jruby-lint-0.9.0 Done installing documentation for jruby-lint after 1 seconds 1 gem installed
Once done, you can do the following (from a JRuby presentation)
e:\blog> cd my-app jrlint
If you’re lucky, you will get something like this, which I got on a relatively simple, small, self-contained project.
e:\blog\my_app>jrlint JRuby-Lint version 0.9.0 Processed 13 files in 1.08 seconds OK
Often you’re not as lucky and you get notes and messages from
jrlint that require a closer look. I went to a directory that had an old Rails app in it and tried
e:\blog\rails_app> jrlint JRuby-Lint version 0.9.0 ./Gemfile:: [gems, info] For more on gem compatibility see http://wiki.jruby.org/C-Extension-Alternatives ./Gemfile:14: [gems, warning] Found gem 'pg' which is reported to have some issues: Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).| ./Gemfile:17: [gems, warning] Found gem 'thin' which is reported to have some issues: Use Puma.| Processed 72 files in 1.05 seconds
As you can see,
jrlint is helpful and will point you to the page that you must read about “gem compatibility”: but will also give you clear ideas about what you need to do, in this case:
- Switch from “pg” to “activerecord-jdbcpostgresql-adapter” instead or “pg_jruby”
- Switch from “thin” to “Puma”
With these changes done, you should be able to make progress.
Starting a new Rails app
It’s always exciting to start a new Rails app. Currently, Rails 6.0.0 will work but 6.0.1 and newer rely on a native gem that isn’t yet bundled/ available for JRuby. Stay tuned since the situation is constantly changing.
OK, so that brings us to the end of Day 1 – for a lot of the work that you might want to do, JRuby should just work. You might find that for some scripts that run longer than a few minutes, you might find it quite easy to get some benefits and savings. If you’re running very short scripts (e.g. 2 – 3 seconds long), JRuby is not yet the best solution in terms of the time it takes to start and run. Things are being done to make the startup faster, but let’s wait and see when that happens.
So, give it a spin and if you run into problems, just ask – the JRuby team is very helpful and are genuinely keen to help the community. Keep in mind that JRuby 18.104.22.168 (the current version) is expected to be the last in the 9.2 series. The 9.2 series is compatible with Ruby 2.5 – work will now continue towards Ruby 2.6 and 2.7 compatibility. So, watch out for Ruby gems or code that specifically requires/ relies on Ruby 2.6 or 2.7 features.