Skip to content
eregon edited this page Oct 24, 2014 · 75 revisions

The Truffle runtime of JRuby is an experimental implementation of an interpreter for JRuby using the Truffle AST interpreting framework and the Graal compiler. It’s a potential alternative to the 1.7 AST interpreter, the bytecode backend, and the new 9000 IR. The goal is to be both significantly faster and simpler than other high performance implementations of Ruby.

This wiki page is a collection of notes about the runtime. For more general background information see the announcement blog post, and the FAQ below.

Most of the work for the truffle runtime is in core/src/main/java/org/jruby/truffle. There is some more technical discussion in Chris Seaton's blog posts.

Current Status

At the start of integration, the Truffle backend was not much more than the Oracle Labs implementation of Ruby, hosted within JRuby. The runtimes, object representation and core libraries are still quite separate, but we are now focused on completeness and integrating with the excellent work the JRuby project has done to implement the full Ruby ecosystem.

At the moment the Truffle backed is unlikely to work for arbitrary code without having to implement a few features here and there. Even if it does work, performance is unlikely to be good for arbitrary code without having to implement additional specialisations. However we can run several micro-benchmarks and RubySpec, where we pass about half the language specs.

Running Truffle

Truffle is only available in the JRuby 9000 development builds, or from source control. To enable the Truffle backend use the -X+T option. -X+T also turns off loading the Ruby kernel and implies --disable-gems.

 bin/jruby -X+T

Running With Graal

The Truffle backend will run on any Java 7+ JVM, but it will only compile and optimise Ruby code when running on top of a Graal-enabled build of OpenJDK. You can get both a JRuby 9000 development build and Graal via ruby-build:

rbenv install jruby-9000+graal-dev

This will run using Graal instead of your system JVM.

You can also download Graal builds for 64-bit Linux, Mac and Windows. The binary releases of Graal looks like a normal JVM install. To ask JRuby to use it instead of your system JVM, set JAVACMD to the path of java in your graalvm-jdk1.8.0 directory.

For example

JAVACMD=../graalvm-jdk1.8.0/bin/java bin/jruby -X+T -Xtruffle.printRuntime=true

Compiling

The Truffle backend is integrated into the normal JRuby build system.

mvn package

To test there are phases for running RubySpec for the language and the core library.

mvn -Ptruffle-specs-language
mvn -Ptruffle-specs-core

Building Graal (Optional)

If you don't want to use a pre-built Graal binary follow these instructions:

Graal is a fork of OpenJDK. Compiling it isn’t too complicated, but it isn’t part of the JRuby build system so you’ll have to do it separately.

If you are on Windows, follow the instructions on the Graal Wiki first.

If you are on OS X Mavericks you will need to set these variables:

export COMPILER_WARNINGS_FATAL=false
export USE_PRECOMPILED_HEADER=0
export USE_CLANG=true
export LFLAGS="-Xlinker -lstdc++"

You will need a system and C/C++ compiler toolchain supported by OpenJDK, as well as Mercurial, Ant and Python 2.7.

 hg clone http://hg.openjdk.java.net/graal/graal
 cd graal
 ./mx.sh --vm server --vmbuild product build

This gives you a copy of the JDK in graal/<jvm version>/product containing:

  • the default VM of the bootstrap JDK (launched with java or java -original). Does not contain Graal.
  • a Server VM which is Graal enabled (launched with java -server).

To make -server the default VM, you can edit graal/<jvm version>/product/jre/lib/jvm.cfg (MacOSX) / graal/<jvm version>/product/jre/lib/amd64/jvm.cfg (Other), such that the line with -server is the first uncommented line.

You can use graal/<jvm version>/product as JAVA_HOME, or set JAVACMD, as above.

Compiling Against Latest Graal

To compile against the latest version of Graal, build it as above and then install Truffle jars in your local Maven repository:

./mx.sh maven-install-truffle

You then want JRuby from the truffle-head branch, and build as normal.

Running Benchmarks

We have a couple of micro-benchmarks that we know work in bench/truffle. They are very limited, are focused on peak performance, don’t include time for warmup and don’t have any rigorous statistical methodology behind them, so don’t expect to draw too many conclusions. For example, to run Mandelbrot:

 cd bench/truffle
 ../../bin/jruby -J-server -J-d64 -X+T -Xtruffle.printRuntime=true harness.rb -s 120 mandelbrot.rb
 JAVACMD=path/to/graal/bin/java ../../bin/jruby -J-server -J-d64 -X+T -Xtruffle.printRuntime=true harness.rb -s 120 mandelbrot.rb

Note that this command line uses -server explicitly in order to select the Graal enabled Server VM.

You should see something very roughly like an 10-15x increase in the score compared to invokedynamic - a 10-15x speedup, and more if you compare against JRuby without invokedynamic.

To run a set of benchmarks and compare performance as you makes changes to Truffle, you can use the compare.rb script in bench/truffle:

export GRAAL_DIR=path/to/graal
ruby compare.rb --reference -m 3

This will get the script to set a reference point for performance, giving it a time budget of 3 minutes. You can then make changes to Truffle and see how the performance has changed

ruby compare.rb

Truffelize

An alternative to running your entire program using Truffle is to use Truffle for individual methods. This is done using the truffelize library and method. When using truffelize you don't need the -X+T option, as you're running the rest of the program in normal JRuby.

import 'truffelize'

truffelize :my_method

At the moment we only support passing scalar objects between a truffelized method and normal JRuby execution.

Truffle Options

 -X+T

Use Truffle as the ‘compile mode’. Turns off loading the Ruby kernel and implies --disable-gems.

 -Xtruffle.printRuntime=true

Print the name of the Truffle runtime that you are using. ‘Default’ means Truffle running as a normal Java library - which will be about as slow as the normal JRuby AST interpreter. ‘Graal’ means that you are using Graal VM to compile the Truffle interpreter to native code, and should be significantly faster.

There are more Truffle options - find them listed by running -Xproperties.

FAQ

What is Truffle?

Truffle is a Java framework for writing AST interpreters. To implement a language using Truffle you write an AST for your language and add methods to interpret - perform the action of - each node.

Truffle also has the concept of specialisation. In most AST interpreters the nodes are megamorphic - they handle all possible types and other possible conditions. In Truffle you write several different nodes for the same semantic action but for different types and conditions. As runtime conditions change, you switch which nodes you are using. After the program has warmed up you should end up with an AST that is precisely tailored for the types and conditions that you are actually using. If these conditions change, you can just switch nodes again.

For more information Truffle see the publications, API documentation, and FAQ.

What is Graal?

Graal is a new implementation of a JIT compiler in the OpenJDK Java Virtual Machine. Unlike the current compilers, Graal is written in Java, and exposes a Java API to the running program. This means that instead of emitting bytecode a JVM language can directly control the compiler. However this is complicated, so normally Truffle uses Graal on your behalf.

For more information about Graal see the publications and API documentation.

Why is the Truffle backend slow on a standard JVM?

When running on a standard JVM, Truffle runs about as fast as the JRuby AST interpreter. By default JRuby compiles to bytecode, which is faster than interpreting an AST, so you may not be used to the performance of a simple AST interpreter and it may seem unusually slow.

Eventually the expected way to run Truffle programs will be using the Graal compiler, so the performance on a standard JVM will not be as important.

Why is the Truffle backend faster on Graal?

When running on a VM with the Graal compiler, Truffle can use the API exposed by Graal. Truffle gets the IR representation of all of the AST interpreter methods involved in running your Ruby method, combines them into something like a single Java method, optimises them together, and emits a single machine code function. On Graal, Truffle also provides wrappers for JVM functionality not normally available to Java applications such as code deoptimization. The Truffle backend uses this to provide dramatically simpler and faster implementations of Ruby semantics compared to the current implementation in JRuby.

Where did this code come from?

Chris Seaton wrote an implementation of Ruby on Truffle and Graal as part of an internship at Oracle Labs in the first half of 2013. The code in the org.jruby.truffle package is that open source code merged into JRuby.

Who do I ask about the Truffle backend?

Chris Seaton is the point of contact, or discuss on the JRuby developer’s mailing list. General Graal and Truffle issues can be discussed on the Graal mailing list.

How do I know if I’m using a Graal VM?

Use -Xtruffle.printRuntime=true. This should print the name of the Truffle runtime that you are using. ‘Default’ means Truffle running as a normal Java library - which will be about as slow as the normal JRuby AST interpreter. ‘Graal’ means that you are using Graal VM to compile the Truffle interpreter to native code, and should be significantly faster.

Why doesn’t the Truffle backend work for my application or gem?

The Truffle backend is not yet mature and doesn’t support all of Ruby. It passes about half of the RubySpec language specs, so is unlikely to work for arbitrary code.

Why doesn’t the Truffle backend perform well for my benchmark?

Benchmarks that we haven’t looked at yet are likely to require new code paths to be specialized. Currently we’ve added specialisation for the code paths in the benchmarks and applications that we’ve been using. Adding them is generally not complicated and over time we will have specialisations to cover a broad range of applications.

Also, check that you are using a Graal VM (see above).

How is this related to invokedynamic?

The Truffle backend doesn’t use invokedynamic, as it doesn't emit bytecode. However it does have an optimising method dispatch mechanism that achieves a similar result - see the method dispatch nodes.

How is this related to the new IR?

The Truffle backend and the new IR are not related and take very different approaches. They are mutually exclusive backends - you can’t use both of them at the same time.

Why are there are assertion failures while processing annotations during compilation under IntelliJ IDEA?

The assertion failures are a bug in Truffle - sorry about them. You should be able to pass -J-da to javac in preferences, but that doesn’t seem to fix the problem. Compiling in Maven works fine.

Benchmarks

These benchmarks don't yet have error bars or confidence intervals. This isn't a published paper and we're not making any claims using these graphs. They're just informal runs from nightly builds, they aren't always stable, they aren't all fully optimised yet, they aren't always indicative of your workload, but you can run them yourself if you want to https://github.com/jruby/jruby/tree/master/bench/truffle (@chrisseaton will be happy to explain how).

Clone this wiki locally