00:00:04.520
My name is Thomas Enebo, and I work for Red Hat.
00:00:19.199
I've been using Java basically since it started and have been using Ruby since 2002. That's also when I started working on JRuby.
00:00:25.329
This is the roadmap from last September at the last JRuby Kaigi. I'm just going to go over what has happened since then.
00:00:38.230
We were going to retire JRuby 1.7, and we did. A couple of people asked us to keep it going, but most were happy to move to JRuby 1.9.4.
00:00:44.140
Two weeks ago, we put out JRuby 9.1.17. I think we've put out four point releases since the last year, and last week we released JRuby 9.2.
00:01:00.400
It has Ruby 2.5 support. You'll notice in our roadmap that we didn't put out a 2.4 release; we can say that this was due to procrastination.
00:01:06.280
We started our 2.4 branch in October of 2016, a couple of months before CRuby released 2.4. By May, we were about 90% complete, but by September at Ruby Kaigi, we were still about 90% complete. We realized we needed to get moving.
00:01:21.250
Then by December, we were nearly ready, but we realized that CRuby was releasing 2.5, and we were only putting out 2.4, which seemed kind of weird. So, we decided to push on and support Ruby 2.5 instead, skipping 2.4.
00:01:39.130
There are really only two big bullet points for JRuby 9.2. The first is the support for Ruby 2.5, and the second is non-ASCII identifiers.
00:01:46.810
When we try to add a new language level of support, we'll create a new issue on GitHub, and it typically generates lots of checkboxes. We usually build this list from MRI's news file, but we noticed something strange: we get a lot of new contributors whenever we make one of these lists.
00:02:05.170
It seems they look through and adopt the tasks they want to work on. However, if we created many individual issues for each feature, we wouldn't garner as many contributors. This is somewhat of a mystery.
00:02:16.900
We've also noticed that fewer people ask us for commitments; they're generally happy just to submit pull requests. We're continually thinking about how to attract new contributors.
00:02:24.700
Another major feature of JRuby 9.2 is the full support for any encoding of identifiers, including local variables and constants. We partially supported this in previous releases, but there were various quirky issues.
00:02:32.920
Here, we define a method called table_flip that prints out a message. Then we call this method. Using reflective calls to get a list of all the methods on the type should include table_flip.
00:02:48.890
However, it just crashes because those bytes coming from the table_flip method are garbage. In JRuby 9.2, we've fixed this issue.
00:02:56.200
Another issue users encountered was calling a method with multi-byte characters. Sometimes the method wouldn't exist due to a typo, which led to undefined local variable errors.
00:03:07.770
In JRuby 9.2, we handle these discrepancies; however, the user experience has been quite frustrating.
00:03:14.540
Allow me to explain why it has been challenging to tackle this issue for the past ten years. The short answer is that we store and key off all methods and constants using Java Strings for performance reasons.
00:03:30.640
Unfortunately, Java strings can represent different encodings, yet they have no way of reporting what encoding they're storing. Fixing this issue required approximately 5,000 lines of code changes.
00:03:42.230
Previously, we took identifiers like table_flip, parsed them into bytes, created a Java string from these bytes, and used that as a key to store our method definition.
00:03:57.220
When we called methods later, we would ask the type for all the keys. The problem arose when some keys had an unknown encoding, leading to the mistakes seen earlier.
00:04:04.300
The solution was to construct a Ruby symbol from the bytes. The string we made is either a raw or binary string, ensuring that every byte in the Ruby string is a single character in the Java string.
00:04:12.420
We save our symbols to the symbol table using this key and then use that raw string to store our methods. Now, when we retrieve our keys, we can look up something properly encoded, and we can print everything out.
00:04:26.460
Although everything is almost perfect, CRuby allows for two symbols with the same byte sequence but differing encodings, which is one area where we aren't quite perfect.
00:04:33.300
However, this situation is relatively rare, particularly if you have two symbols that fit into ASCII; in that case, we just change their encoding to US-ASCII.
00:04:47.030
Now, let's switch gears and discuss performance.
00:04:52.290
The first topic is Graal, a new JIT compiler. Although it has been in development for several years, it is now starting to make its mark. Graal is written in Java and is bundled with OpenJDK 9 and higher, but it isn't enabled by default and requires a specific option to activate.
00:05:00.840
In the bytecode interpretation process, if the code is deemed hot and needs compilation, it first goes to the client compiler (C1), which performs fast optimizations. If it continues to run longer, it escalates to the server compiler (C2), which has more type information and can generate better code.
00:05:07.840
By using the JVM compiler interface, we can direct the system to use Graal instead of C2. So far, our experience indicates that benchmarks are sometimes much faster with Graal than C2, but usually, performance is similar or slightly slower.
00:05:19.750
The faster results are attributed to partial escape analysis, where Graal excels compared to prior methods.
00:05:29.950
We face a math problem in JRuby. Whenever we execute something like '12 + 2', we must construct Ruby Fixnum objects for each number, which leads to performance overhead.
00:05:37.800
Graal's escape analysis helps here, as it can determine an object's existence within a particular scope and optimize the instantiation of Ruby Fixnum objects.
00:05:48.480
For example, if we proved the identity of an object and that it is used only for reading a specific field, the compiler may choose to directly operate on the extracted value, thus allowing faster execution.
00:06:03.820
This is evident in benchmarks, as we strive to ensure JRuby performs comparably to CRuby. In default mode, JRuby tends to perform slightly better than CRuby, especially with invokedynamic support enabled.
00:06:20.510
Using Graal improves performance as it handles the instantiation of Ruby Fixnum objects more efficiently. Disabling the cache for common Ruby Fixnum objects led to noticeable speed gains.
00:06:31.200
Currently, JRuby matches or slightly underperforms compared to competing implementations, which is promising.
00:06:39.190
Moving on to optimizations, we have recently implemented call frame splitting, where we store additional information when invoking methods or blocks.
00:06:49.140
Previously, if we were dealing with regular expressions, we would store that data, allowing special methods to determine if a block was passed into the call, enhancing performance.
00:07:03.490
The cost of saving these additional fields isn't free and becomes apparent when calling trivial methods.
00:07:10.370
In benchmarks, we can see significant differences in performance, particularly for operations like array indexing.
00:07:22.220
An additional optimization we tackled is storing keyword arguments directly into a Ruby hash, instead of an ordinary Ruby hash. This has led to performance improvements, reducing the overhead when using keyword arguments.
00:07:37.380
Going forward, our goal is to convert keyword arguments into special positional arguments, eliminating Ruby hash allocation altogether, and achieving better performance.
00:07:47.320
Now, let's talk about JRuby on Rails. We've been running JRuby on Rails since 2006 and have many users in production.
00:07:55.600
However, with the transition to Rails 5, we fell behind in compatibility with Active Record. This was primarily due to the JDBC library used for database access.
00:08:10.610
Maintaining compatibility is a challenging task. Active Record JDBC must work with multiple JRuby versions (currently 1.9 and 9.2). We also face differences between Ruby and JRuby, especially since we run on several versions of Java.
00:08:25.350
Our last release before adding Rails 5 support was Active Record JDBC 1.3, targeting various JRuby versions. Supporting multiple active record versions complicated our code base, leading to many checks littering the implementation.
00:08:33.680
Back in 2008, we had more contributors on projects like this, but nowadays fewer people contribute, making the integration difficult. As I began the transition work for Rails 5, I frequently encountered lines of code that puzzled me, questioning their relevance.
00:08:49.711
Essentially, to avoid the mistakes we made in JRuby 1.7, where we mixed functionalities for various Ruby versions and ended up with complicated nested conditions, we opted for a strategy where each branch supports a single version of Active Record.
00:09:07.790
Our new structure means active record version 5.0 is supported by one version and 5.1 by another. We're also limiting support to the main three databases Rails supports.
00:09:18.590
When Rails 5.0 reaches the end of life, we will stop supporting that branch. Since this overhaul, we have significantly decreased our codebase complexity, reducing our lines of Ruby from 1,600 down to 280.
00:09:36.190
With the updates, we removed unnecessary version checks and leveraged the core Active Record code more effectively, cutting down outdated checks in Java code, albeit keeping the vital checks intact.
00:09:49.810
We noticed improvements when updating to Active Record 5.1 and capabilities to add lightweight methods, streamlining the code base further.
00:10:05.911
As we proceed, we aim for a more robust testing state with Rails 5.1, which currently has only 36 failures among about 800,000 assertions.
00:10:19.300
Most of these failures appear linked to specific bugs, particularly with precision issues. Recently, we've explored Rails 5.2 support.
00:10:27.090
While testing action cable, it has not bootstrapped adequately. We're looking into it as we prepare presentations on JRuby's capabilities.
00:10:39.900
Currently working with SQLite for testing action cable, we face eight failures. However, aside from minor setbacks, we're able to execute scaffolded applications.
00:10:46.820
The main obstruction is related to configuring the testing environment, particularly concerning the fork feature, which JRuby does not support.
00:11:01.750
To ensure compatibility with Rails 5.2, some tweaks might be necessary, especially in the gem file. We continuously strive for seamless integration and functionality with the latest Rails versions.
00:11:15.610
Lately, I've been focusing on performance benchmarks, analyzing our performance regression through an internal benchmarking suite with comparisons to CRuby.
00:11:29.660
For instance, I benchmarked the performance of creating 5,000 rows with just a string column and observed that lower times are preferred, showing CRuby outperforming various JRuby modes.
00:11:39.000
Although CRuby currently leads, we’ve seen JRuby's performance improve significantly compared to previous years, where the performance gap was notably wider.
00:11:48.400
One key area of improvement lies with the transcoding of database queries between UTF-8 in databases and Ruby, where extra transcoding previously slowed JRuby down.
00:11:58.600
We've managed to optimize this process, with RUBY 2.5 now showing considerable performance improvements due to these optimizations.
00:12:09.800
As we compare performance for operations like selecting data or updates, we find JRuby generally holds its own against CRuby and shows great promise for future enhancements.
00:12:21.750
Looking to the future, we intend to simplify our integration with core Rails to ensure Ruby code alignments, as involvement in core development remains a priority.
00:12:36.200
Particularly as JRuby continues to evolve, we aim to minimize disruption in Rails compatibility and actively work with contributors to improve the development process.
00:12:47.250
We want to embrace projects like Discourse and involve ourselves in their development to showcase *Ruby 3x3* and seamlessly integrate JRuby.
00:13:03.570
Considering native C extensions, there are ups and downs. Occasionally, we face new C extensions that need to be integrated into JRuby, creating a cycle of adaptation and resolution.
00:13:10.970
For certain algorithms like cpp-gi, which lacks a Java equivalent, we can script Java types as Ruby code, making workflow smoother.
00:13:29.190
For issues pertaining to libraries lacking Java counterparts, like *no-co-gumbo*, we can rely on bindings to Lib no-co-gumbo.
00:13:35.497
We also work on porting the JSON library into JRuby, which almost reaches fruition. Being close to completion means we only need a few more refinements.
00:13:47.799
By collaborating with authors and optimizing interactions with native extensions, we aim for better integrations and experiences for developers seeking to use JRuby.
00:14:02.900
Despite my speed during this talk, I appreciate the opportunity to share our advances and performance enhancements with everyone. Thank you all for being here.
00:14:15.060
If anyone has questions, feel free to turn on the mic and ask. I'll do my best to respond before time runs out.
00:14:23.760
One question asked concerns the relationship between JRuby and Truffle Ruby, which is more indirect.
00:14:32.050
At one point, our codebases were unified before they branched out due to not sharing as much code. Ruby Truffle borrows extensively from JRuby's libraries, such as Joani and JCodings.
00:14:45.900
We also use components from Rubinius, connecting our development paths more closely. Moreover, Graal, the new JIT compiler, is under Truffle's umbrella.
00:15:03.000
As a result, we foresee opportunities for future collaboration and integration of technologies among different Ruby dialects.
00:15:17.230
The roadmap for JRuby looks promising, with exciting developments focused on compatibility enhancements with core libraries, especially in light of upcoming Ruby versions.
00:15:31.600
Any changes made towards the JRuby runtime will benefit performance overall, particularly with regard to keyword arguments and method dispatching.
00:15:44.560
Thank you again for your time, and if you have further questions, feel free to reach out.