Ruby has gained a lot of popularity in the programming community for its natural syntax and the flexibility it provides through meta-programming and monkey patching. Most of Ruby's appeal centers around developer experience, and while the freedom it provides can lead to some hairy situations, this appeal has stood the test of time. Ruby is not considered to be a fast. It is often "fast enough" for its typical applications, but optimization of Ruby in certain cases can be a major pain.
This is where Chris Seaton comes in. He and his team at Oracle Labs are working on an implementation of Ruby called JRuby+Truffle as an alternative to current suite of Ruby implementations that offers some massive performance upgrades. In a recent update, Chris announced that this implementation was benchmarked to be over 31x faster than MRI. The Practical Developer got in touch with Chris to learn more about the project and what we can expect over the next year.
In some ways JRuby+Truffle is conceptually the simplest implementation of Ruby. As in other implementations, the first thing we do is parse the Ruby source code to an abstract syntax tree (AST). Unlike other implementations that's as far as we go - there is no bytecode or other representations of your program in the implementation of JRuby+Truffle. We then execute your program by walking the AST. For example if you have a
Fixnum#+ node we execute the two nodes for the left and right values and then perform the result. It's like the visitor pattern, if people are familiar with that.
We do one new trick with the AST though - we specialise it as the program runs. For example we have a 'call' node, and the first time it runs we see what method is called and then specialise that call node to expect to call that same method the next time it runs. Truffle is the name of the Java framework and DSL that helps us write AST interpreters like this.
The huge performance gains that we get comes from what Truffle then does with this simple AST interpreter. We have written a new JIT compiler for Java called Graal. It's written in Java itself, and that means it can be used as a Java library. Truffle uses Graal to compile our AST interpreter to machine code, which does the equivalent of all the phases in the other implementations automatically for us.
Graal is advancing the state-of-the-art for JITs across all languages and includes some extremely powerful optimisations that make a big difference to Ruby code, such as partial evaluation, which allows us to run parts of your Ruby program at compile time, and partial escape analysis, which allows us to virtually create Ruby objects but never actually
For example, if you write a max method as
[a, b].sort, the partial escape analysis will see that although you create an array and sort it to create another, neither of these objects are ever visible to anyone else so they needn't actually be allocated, and the partial evaluation will run the sort and index at compile time as far as it can without actually knowing the values. The result is that this method will be compiled to something like
if a > b; a; else b.
These kind of optimisations are very effective at improving the performance of idiotmatic Ruby code, which often uses lots of temporary objects and abstractions. We combine them with a technique called dynamic deoptimisation, which can reverse the optimisations if for example a method is monkey patched, or someone uses
We're starting to be at the stage where simple gems and applications that don't have native code and don't use some parts of the standard library like openssl may be able to run. Gems like Webrick, Sinatra and Active Support already work. During 2016 we'll be getting the rest of the Rails stack working more or less, and we'll be testing other important gems.
So the people who can benefit earliest are those with the simplest set of dependencies and those who don't use native code. Small Sinatra apps for example. And they'll see the biggest impact if they do lots of processing inside Ruby. We're benchmarking apps that manipulate images with Chunky PNG for example and seeing huge gains.
I believe the idea in Crystal is to make a new language that is simpler than Ruby and does not have features that are difficult to optimise statically. For example it doesn't support methods like
Crystal is one valid approach and it looks like it's working for what they want to do, but it probably doesn't help people with existing Ruby applications and people who want to use existing Ruby gems because real Ruby code uses those features a lot. It's hard to imagine something like Rails without those methods. It's really a new language that looks and feel a lot like Ruby, but not enough to run existing Ruby code.
I think it would be a shame if we had to give up so much of what makes Ruby what it is in order to achieve performance. If we can make Ruby fast without removing the features then I think that's surely the better option for Ruby programmers.
Crystal does have other interesting benefits such as macros and low footprint.
I was actually handed the project idea as an intern task. I had never written or read a line of Ruby before I started so I read a couple of Ruby books on the flight from the United Kingdom to San Francisco and then my first day using Ruby was my first day starting to implement it.
But after starting to work with the Ruby language and community I've really grown to love all aspects of it. It's definitely a productive language, I have fun writing Ruby programs, and I think making it fast is one of the last pieces of the puzzle so I'm proud to be working on that.
You see a lot of language implementers making fun of the languages they're trying to implement - saying how crazy they are in places and how bad the code can be. I've tried to take a philosophical approach of not having an opinion on how Ruby is designed. I'll try to implement whatever MRI does and I'm happy to try to get anyone's code working fast, no matter what language features it uses or how inefficiently it may be designed. For that reason I don't want to offer any ideas on how the language itself could be improved - I see that as a separate job for someone else.
Ultimately of course I hope that JRuby+Truffle will one day soon be the implementation that people want to use because it's faster and has better tooling. I think the impact it will make along the way is to show that the ceiling for Ruby performance is much higher than was previously believed. Already people like Evan Phoenix are talking about what JRuby+Truffle can do and saying that this the bar that MRI should be aiming for.
Open sourcing our work on Ruby and integrating into the existing JRuby project was the obvious way to make the code useful as quickly as possible. The JRuby team have been really welcoming and being part of a community like that makes this work fun. We've even hired several of the people from the community to work with us full time.
We've used the same licenses as JRuby rather than impose our own for maximum compatibility, and Truffle and Graal are also open source and work on OpenJDK, so the full stack that you need to run JRuby+Truffle is open.