00:00:00.000
Ready for takeoff.
00:00:16.940
All right, we're going to get started. I have a lot of material to cover. I am Charles Nutter, and I will be speaking about the project I've been working on for the past 16 years: JRuby.
00:00:24.000
I want to give you a little idea of what JRuby is, why it's useful, and how you might get started using it. First of all, I want to thank Yarden Leifenfeld, who could not be here. She was originally set to give a JRuby talk during this slot, but she had other commitments.
00:00:37.140
If you check it out, there are videos online of her talk "Ruby and JVM: A Love Story," where she provides a detailed exploration of how her company has been using JRuby, including building a debugger based on the JVM debugger. There is a lot of cool content, so I encourage you to check that out.
00:00:51.539
I apologize to Yarden, but I was not able to include any of her content since 30 minutes is not long enough. Also, I want to express my thanks to Red Hat and IBM. I am an employee at Red Hat, and they have sponsored our work on JRuby for over 10 years, which allows us to work on JRuby full time.
00:01:10.500
Don't ask why they are so generous with open-source projects. Just buy Red Hat. And of course, cheers to Tom Annabelle, who is still isolating at home, worried about COVID-19. We've been co-leading JRuby for these past 16 years and have traveled a lot, enjoying some beer along the way.
00:01:21.600
He's the silent partner who does a lot of the real work on JRuby behind the scenes.
00:01:39.659
All right, let's get into it. So, what is JRuby? It's not surprising that JRuby is a Ruby on top of the JVM. How many folks here are somewhat familiar with JRuby? Okay, and how many have actually run something on JRuby? Pretty much the same folks.
00:01:51.720
JRuby is a Ruby implementation that emphasizes being a Ruby implementation first and a JVM language second. There are many people on the Java side who enjoy using JRuby to script Java applications, but our primary focus is on the Ruby developer experience. Once you install JRuby, you can run all the same commands from the command line, just like with MRI Ruby. You get all the benefits that come from being on the JVM.
00:02:27.780
The general idea is that Ruby code should just work. You should be able to take any pure Ruby application and many of the extensions that have JRuby versions, plug your application into JRuby, and it should run out of the box. However, we don't support the C extension API that MRI Ruby does; instead, we have our own extension API, so some libraries might need to be ported.
00:02:58.560
We do not support forking because the JVM doesn't support it. Moreover, unlike MRI Ruby, our threads run in parallel, meaning one process can saturate your entire CPU. You don't need to run multiple workers.
00:03:30.000
I've been using this slide for years. Some of the companies listed may no longer use JRuby, but they all did at one point. For example, the BBC has used JRuby to deliver election results, such as the results from the last Scottish referendum on independence.
00:03:44.000
Additionally, I know that JRuby was once used at NASA to script components for one of their SETI projects searching for extraterrestrial life. Exciting stuff!
00:04:03.540
JRuby is indeed a Ruby implementation, and let's discuss Ruby compatibility. Just last week, we managed to release JRuby 9.4. This version jumps from Ruby 2.6 compatibility all the way up to Ruby 3.1, and most of the features in these versions have been implemented.
00:04:17.760
Some notable exceptions are that we do not yet support the Fiber I/O scheduling API and we do not support Reactors, but I am not sure if many people are using those features anyway. We will continue to maintain JRuby 9.3 for the next year or so based on demand.
00:04:42.540
We have a strong focus on compatibility before performance; JRuby 9.4 has had almost no performance tuning during its development. Nevertheless, it still runs very well, and as I will show you later, we expect to see significant improvements now that we are back on track with compatibility.
00:05:08.040
So, why is running Ruby on the JVM interesting? Well, the JVM is ubiquitous; every platform out there has a JVM available. Moreover, most of these are well-supported by companies like Oracle and Red Hat.
00:05:21.000
We also have access to excellent JIT compilers, various garbage collectors with a wide range of tuning options, and great concurrency support right out of the box. Additionally, since we're running on the JVM, we can access all of the libraries available on this platform, whether they are written in Java, Scala, Closure, or any other language.
00:05:51.300
You can import those libraries directly into your Ruby code and call them like any Ruby library. There's also a plethora of tools available with the JVM for monitoring, production monitoring, debugging, profiling, and all that functionality works almost seamlessly with JRuby.
00:06:09.539
Furthermore, because we run on the JVM, your Ruby application can operate on a variety of different and exotic platforms. We were among the first alternative Ruby implementations to have support for Apple's M1, and we also run on older systems like OS/400 and AIX.
00:06:32.340
If you have any legacy application servers you want to transition to Ruby, JRuby provides that flexibility.
00:06:57.900
This slide shows one of the tools we've showcased for years: VisualVM. The right side displays the Visual GC plugin, which provides a live view of the garbage collector heaps, allowing you to see how generations fill up and objects get promoted.
00:07:07.740
You can monitor this via JRuby in production and gather this insight without additional costs.
00:07:13.860
As I mentioned earlier, JRuby can efficiently utilize all CPU cores in your system, so you do not need to manage multiple workers, thereby minimizing memory usage. One process with multiple threads can handle the workload across your entire system.
00:07:30.000
In terms of integration with Java, we have exciting projects, such as the Progen framework, which is a bucket-based plugin for using Ruby to write Minecraft mods. An example is a simple modification to increase the number of chickens that spawn from an egg. Tom Moore did most of the work on this, and there's a fun backstory of him accidentally creating too many chickens.
00:07:55.680
Getting started with JRuby is straightforward; you can check out jruby.org for a wealth of information. The process generally requires that you have a Java JDK installed on your system, which is pretty standard across all Linux distributions and relatively simple to install on Windows and Mac OS.
00:08:13.440
We recommend using Java 11 or higher. We support all versions of Java from 8 and higher, but we will probably drop support for Java 8 and maybe 11 in our next major release.
00:08:37.500
However, for the 9.4 cycle, we will continue to support Java 8. Installing JRuby can be done through Ruby installers like RVM or rbenv, or you can download a tarball or zip file. There is also a Windows installer, as well as a Docker image that we maintain.
00:09:01.320
The only real requirement is having a JDK available. After installation, give it a shot! For instance, here’s how you can switch to JRuby using RVM, and I’ll demonstrate calling some of the Java core libraries.
00:09:19.140
Java Lang runtime can provide information like the number of available processors and free memory. Any JVM library can be treated just like a Ruby class.
00:09:39.840
The biggest use case for Ruby developers using JRuby is JRuby on Rails, which works quite effectively. There are numerous JRuby on Rails applications in production; it’s hard to quantify exactly how many, but it could range from hundreds to thousands.
00:09:54.840
When you can get your application running on JRuby, ensure your tests pass and performance is satisfactory. You can actually run your entire system with just one JRuby instance, avoiding the need to spin up multiple workers.
00:10:12.660
Transitioning an existing application typically requires small configuration adjustments. A few gems might not be supported. For instance, if you're using MiniRacer, which is based on V8, we offer a different JavaScript implementation using either Rhino or Nashorn.
00:10:39.000
When it comes to databases, you would want to specify a host and port for MySQL since we do not support Unix sockets. Adjust the configurations so you can utilize more threads and workers.
00:10:56.760
I am pleased to announce that Rails 7 is working well on JRuby, and I will be using it for some benchmarks later. The main task when supporting a new version of Rails is to incorporate any changes to Active Record.
00:11:16.100
Right now, SQLite is performing well, MySQL works but requires attention, and we are just starting to work with PostgreSQL. In the coming weeks, we will have updates for all three databases.
00:11:36.100
Our Active Record JDBC adapter follows version matching rules; for example, the Rails 6.x versions are supported by our 6.0 Active Record JDBC adapter, and Rails 7 will be using 7.0.
00:11:44.700
As of this morning, SQLite and MySQL adapters for Rails 7 have been released, allowing quick application setup.
00:12:01.200
When it comes to performance, this factor is probably among the key reasons one would try JRuby on Rails. However, performance evaluation is tricky: you should consider what matters to you.
00:12:15.880
Are you focused on straight-line heavy data processing? Is your goal to achieve high concurrency and handle numerous users without spinning up more instances? When it comes to command-line applications, consider whether startup time matters.
00:12:43.340
C Ruby has been optimized for startup for many years. It is difficult for us to compete with that, though we continue to work on improving warm-up time.
00:13:02.540
Consider whether you need an application to reach Peak Performance immediately or if it is acceptable for the application to warm-up gradually.
00:13:24.240
Memory size might be a concern if you're consuming too much memory with multiple workers.
00:13:43.240
Benchmarking is situational and specific. What performs well for a micro-benchmark may not hold in a real-world application. Straight-line performance can be promising, but concurrency can expose weaknesses, especially under heavy loads and memory management.
00:14:22.799
Trust your benchmarks over external claims; therefore, run your own tests with your applications to ascertain their real-world performance.
00:14:39.960
For this talk, I’ll present three benchmark examples: a simple Rails benchmark, an end-to-end blog application running on MySQL, and performance insights regarding Active Record.
00:15:00.480
The Rails benchmark focuses on measuring the overhead of the Rails framework itself. It does not utilize any web server or make socket connections. It executes a single SQLite blog application script as fast as possible.
00:15:17.340
While it’s great for identifying performance bottlenecks within Rails, it might not fully represent a complete application. Therefore, I’ll also run an end-to-end benchmark for a small blog application.
00:15:39.000
As for the Rails bench results collected on my system, I was impressed to find that YJIT is performing admirably. Getting good JIT implementations in C Ruby has been slow, but YJIT is demonstrating substantial promise.
00:15:56.640
Truffle Ruby's performance numbers are reportedly better than what I experienced, so I encourage you to test this out on your own systems.
00:16:14.500
The results show that JRuby is much faster than YJIT right now, especially while we have not engaged in aggressive performance optimization over the last year or two. We expect to significantly boost performance soon.
00:16:34.559
However, benchmarks only reveal straight-line performance. They don't inform you about how concurrency affects overall performance, startup times, or their effect on memory consumption.
00:17:09.960
The memory footprint can be challenging for JVM and similar modern runtimes, as they typically consume more memory than simpler implementations.
00:17:37.800
These complex runtimes require additional memory to allow for efficient garbage collection. They may also need to maintain multiple code copies, further straining memory usage.
00:18:09.780
We face challenges competing with C Ruby, which has focused on optimizing startup time and memory usage over the years. Hence, it’s crucial to look beyond raw performance numbers.
00:18:31.740
Launching a Rails bench example, C Ruby 3.1 consumes about 80-90 MB of memory on my system, while JRuby and Truffle Ruby are significantly more demanding.
00:19:00.420
These numbers can vary based on JVM tuning. The JVM tends to allocate more memory to give its garbage collector room to operate effectively.
00:19:23.520
In practice, this benchmark could run with a smaller JVM heap, but with some tuning, I figured I could make the benchmark run in a reduced footprint.
00:19:43.800
With a minimized heap allocation, I managed to run with JRuby utilizing only about 950 MB of memory, thereby helping lower production costs.
00:20:05.640
Regarding warm-up times, similar trends apply as are observed in the Rails benchmark, where JRuby must initially interpret many components, leading to potentially slower performance.
00:20:31.800
It is important to remember that the Java string implementation and the list implementations start cold and require time to JIT and warm up.
00:20:50.100
Garbage collection mechanisms may also influence how the heap size adjusts as performance stabilizes. We're consistently focused on improving our warm-up time.
00:21:06.800
This graph depicts performance iterations over time for the Rails benchmark across C Ruby and JRuby. While C Ruby stabilized relatively quickly, JRuby took several iterations to reach a steady performance level.
00:21:38.640
When deploying JRuby in production, you may notice that initial requests start slower and gradually improve as the application warms up.
00:21:55.680
Conversely, some developers deploy scripts to warm up their applications post-deployment.
00:22:21.000
Comparing graphs from the performance of C Ruby versus JRuby involved layering them; JRuby tends to outperform C Ruby around the sixth iteration.
00:22:44.180
Truffle Ruby takes longer to stabilize performance and even lags further behind in terms of iterations near my run timings. It will eventually stabilize, but often it requires more attention.
00:23:10.680
These observations underline the importance of real-world checks because many of these figures are situational and specific to each application.
00:23:29.520
Now, let’s transition into more realistic benchmarks involving end-to-end scenarios.
00:23:43.680
I’m running a relatively simple blog application on MySQL hosted on my local Linux system. I deployed C Ruby 3.1 with 16 workers, each with two threads, to ensure optimal CPU usage.
00:24:12.720
The JRuby setup utilized 32 threads in a single process, though I encountered issues using Truffle Ruby which will be discussed briefly.
00:24:31.440
In this scenario, C Ruby achieved around 2000 requests per second, while YJIT performed slightly better with 2361 requests per second.
00:24:53.700
However, when heavily loaded with concurrency, JRuby showed twice the performance of C Ruby with YJIT.
00:25:09.180
When concerning memory footprints, C Ruby averaged around 1.5 GB for 16 instances using Puma.
00:25:27.440
Meanwhile, applications using JRuby operated around 1.4 GB, indicating that JRuby can outdo C Ruby when carefully configured.
00:25:44.180
Once configured correctly, JRuby’s memory footprint aligns more favorably with needs observed in production environments.
00:26:07.560
Performance optimizations in Active Record are an ongoing effort. The complexity inherent in Active Record has posed challenges, but we continue to refine our adapter while JRuby evolves.
00:26:29.040
The improvements seen moving from Java 11 to Java 17 have yielded substantial performance benefits across various benchmarks.
00:26:48.720
When performing select benchmarks during tests, I've observed that JRuby frequently outperformed C Ruby, highlighting the need for extensive situational checks.
00:27:02.480
It’s essential to employ real-world benchmarks tailored for your application to confirm achievable performance gains.
00:27:16.440
For instance, many years ago, a large Rails application running on 40 extra-large EC2 instances was faced with hefty costs. By migrating to JRuby, they managed to bring this down to only 10 instances.
00:27:45.840
They were able to significantly cut AWS costs, achieving better throughput with lower per-request response times.
00:28:06.000
While transitioning to JRuby requires some effort, it is often worth the endeavor for your application.
00:28:31.440
And to summarize, JRuby 9.4 is out, and we've included welcome performance improvements that we hope you will explore.
00:28:54.840
Please report any issues you encounter; it may not be your fault—often, it's our problem. We encourage contributions and PRs, aiming to transfer more code into the Ruby kernel, where missing features could be fixed through Ruby alternatives.
00:29:12.840
We have plans for optimization within JRuby, as we aim to bring our performance closer to competing benchmarks. Additionally, upgrades to the JVM and explorations of new JITs and garbage collectors may enhance our functionalities.
00:29:39.420
For Rails, we have a stable future, especially with Rails 7 proving effective. As your application scales, don't hesitate to consider JRuby as a viable solution for resource optimization.
00:30:08.880
I will be available during the conference, so feel free to approach me to discuss transitioning your applications to JRuby.