Run Ruby with GraalVM Enterprise

TruffleRuby is an implementation of the Ruby programming language on top of GraalVM Enterprise.

Installing TruffleRuby and Running Ruby Programs

TruffleRuby is shipped as a separate installable and should be installed.

  1. Download Oracle GraalVM Enterprise Edition Ruby Language Plugin from the Oracle Technology Network page.
  2. Having downloaded a component jar in consideration of the operating system and the underlying Java SE version, install it with:
    gu -L install component.jar
    

    For more details, check the GraalVM Updater reference.

Run Ruby applications with the standard ruby command:

ruby [options] program.rb

Other Ruby commands are available, including gem, irb, rake, rdoc and ri.

TruffleRuby uses the same options as the standard implementation of Ruby with some additions (see below).

Installing a Ruby gem

gem install chunky_png
ruby -r chunky_png -e "puts ChunkyPNG::Color.to_hex(ChunkyPNG::Color('mintcream @ 0.5'))"
#f5fffa80

Compatibility

TruffleRuby aims to be fully compatible with the standard implementation of Ruby, MRI, version 2.6.5. If you find an incompatibility with MRI, please report an issue.

Identification

TruffleReport defines these constants for identification:

Additionally, TruffleRuby defines:

With TruffleRuby:

Run idiomatic Ruby code faster

TruffleRuby in conjunction with GraalVM Enterprise produces very fast machine code. Traditionally, performance-critical Ruby code has had to be extracted out to language extensions implemented in native code. This approach is cumbersome and error-prone. TruffleRuby on GraalVM Enterprise can optimize idiomatic Ruby code.

The following example template rendering benchmark is taken from the MRI benchmark suite. It has been modified to work with the benchmark-ips benchmarking harness, and to write the result of the render to a file in order to include IO.

require 'benchmark/ips'
require 'erb'

data = <<erb
<html>
<head> <%= title %> </head>
<body>
  <h1> <%= title %> </h1>
  <p>
    <%= content %>
  </p>
</body>
</html>
erb

title = 'hello world!'
content = "hello world!\n" * 10
erb = ERB.new(data)
out = File.open('test.out', 'w')

Benchmark.ips do |x|
  x.iterations = 3
  x.report("erb") { out.write(erb.result(binding)) }
end
gem install benchmark-ips
ruby benchmark.rb
MRI 2.5.3 JRuby 9.2.5.0 TruffleRuby
CE native
TruffleRuby
EE JVM
34 K renders/s 29 K renders/s 542 K renders/s
(16x faster)
1 M renders/s
(31x faster)

Run Ruby code in parallel

TruffleRuby runs threads in parallel, without a global-interpreter-lock. It includes sophisticated techniques to automatically make objects thread-safe only when they are going to be used by more than one thread, leaving them unsynchronized when used from only one thread to give you parallelism without an overhead when you do not want to use it.

Boot Ruby applications in less time

A hello-world script in TruffleRuby runs about as fast as MRI, or over ten times faster than JRuby. TruffleRuby can run without a heavy-weight JVM so that you start running your Ruby code almost immediately.

Execute C extensions in a managed environment

TruffleRuby executes C extensions using a managed C interpreter and just-in-time compiler, not by running native code.

Provide new tooling

TruffleRuby includes a built-in debugger that allows you to use Chrome DevTools on your Ruby application.

ruby --inspect test.rb
Debugger listening on port 9229.
To start debugging, open the following URL in Chrome:
    devtools://devtools/bundled/inspector.html?ws=127.0.0.1:9229/ffffffffffffffff-fffffffffffffffff

High compatibility with the standard implementation of Ruby

TruffleRuby aims to be fully compatible with the standard implementation of Ruby, MRI, version 2.6.5.

Interoperability

TruffleRuby allows you to write polyglot programs using the other languages from GraalVM Enterprise (JavaScript, R, Python and the LLVM languages). TruffleRuby can run code from these languages, and use objects and methods from one language in another.

As an example, a JSON-encoded string is passed to the GraalVM JavaScript engine, decoded into a JavaScript object, and then the decoded object in Ruby is inspected:

require 'json'

obj = {
  time: Time.now,
  msg: 'Hello World',
  payload: (1..10).to_a
}

encoded = JSON.dump(obj)

js_obj = Polyglot.eval('js', 'JSON.parse').call(encoded)

puts js_obj[:time]
puts js_obj[:msg]
puts js_obj[:payload].join(' ')

Use --jvm --polyglot to run a polyglot program:

$ ruby --jvm --polyglot test.rb
2018-04-11 15:25:08 -0700
Hello World
1 2 3 4 5 6 7 8 9 10

TruffleRuby Command Line Options

TruffleRuby has the same command line interface as our compatible MRI version.

Usage: truffleruby [switches] [--] [programfile] [arguments]
  -0[octal]       specify record separator (\0, if no argument)
  -a              autosplit mode with -n or -p (splits $_ into $F)
  -c              check syntax only
  -Cdirectory     cd to directory before executing your script
  -d, --debug     set debugging flags (set $DEBUG to true)
  -e 'command'    one line of script. Several -e's allowed. Omit [programfile]
  -Eex[:in], --encoding=ex[:in]
                  specify the default external and internal character encodings
  -Fpattern       split() pattern for autosplit (-a)
  -i[extension]   edit ARGV files in place (make backup if extension supplied)
  -Idirectory     specify $LOAD_PATH directory (may be used more than once)
  -l              enable line ending processing
  -n              assume 'while gets(); ... end' loop around your script
  -p              assume loop like -n but print line also like sed
  -rlibrary       require the library before executing your script
  -s              enable some switch parsing for switches after script name
  -S              look for the script using PATH environment variable
  -T[level=1]     turn on tainting checks
  -v              print the version number, then turn on verbose mode
  -w              turn warnings on for your script
  -W[level=2]     set warning level; 0=silence, 1=medium, 2=verbose
  -x[directory]   strip off text before #!ruby line and perhaps cd to directory
  --copyright     print the copyright
  --enable={gems|rubyopt|...}[,...], --disable={gems|rubyopt|...}[,...]
                  enable or disable features. see below for available features
  --external-encoding=encoding, --internal-encoding=encoding
                  specify the default external or internal character encoding
  --verbose       turn on verbose mode and disable script from stdin
  --version       print the version number, then exit
  --help          show this message, -h for short message

Features:
  gems            rubygems (default: enabled)
  did_you_mean    did_you_mean (default: enabled)
  rubyopt         RUBYOPT environment variable (default: enabled)
  frozen-string-literal
                  freeze all string literals (default: disabled)

TruffleRuby also reads the RUBYOPT environment variable, as in standard Ruby, if run from the Ruby launcher.

Unlisted Ruby switches

MRI has some extra Ruby switches which are are not normally listed in help output but are documented in the Ruby manual page.

  -Xdirectory     cd to directory before executing your script (same as -C)
  -U              set the internal encoding to UTF-8
  -K[EeSsUuNnAa]  sets the source and external encoding
  --encoding=external[:internal]
                  the same as --external-encoding=external and optionally --internal-encoding=internal

TruffleRuby options

TruffleRuby options are set via --option=value, or you can use --ruby.option=value from any launcher. You can omit =value to set to true.

Available options and documentation can be seen with --help:languages. Additionally, set --help:expert and --help:internal to see those categories of options.

Options can also be set as JVM system properties, where they have a prefix polyglot.ruby.. For example, --vm.Dpolyglot.ruby.cexts.remap=true or via any other way of setting JVM system properties. Finally, options can be set as GraalVM polyglot API configuration options.

The priority for options is the command line first, then the Graal-SDK polyglot API configuration, then system properties last.

TruffleRuby options, as well as conventional Ruby options and VM options, can also bet set in the TRUFFLERUBYOPT and RUBYOPT environment variables, if run from the Ruby launcher.

-- or the first non-option argument stops processing of TrufflRuby and VM options in the same way it stops processing of Ruby arguments.

VM options

To set options in the underlying VM, use --vm., valid for both the native configuration and the JVM configuration. For example, --vm.Dsystem_property=value or --vm.ea.

To set the classpath, use the = notation, rather than two separate arguments. For example, --vm.cp=lib.jar or --vm.classpath=lib.jar.

Other binary switches

Other binaries, such as irb, gem, and so on, support exactly the same switches as in standard Ruby.

Determining the TruffleRuby home

TruffleRuby needs to know where to locate files such as the standard library. These are stored in the TruffleRuby home directory.

The search priority for finding Ruby home is:

If the home option is set, it is used even if it does not appear to be a correct home location. Other options are tried until one is found that appears to be a correct home location. If none appears to be correct a warning will be given but the program will continue and you will not be able to require standard libraries. You can tell TruffleRuby not to try to find a home at all using the no-home-provided option.

TruffleRuby configurations

There are two main configurations of TruffleRuby - Native Image and JVM. It is important to understand the different configurations of TruffleRuby, as each has different capabilities and performance characteristics. You should pick the execution mode that is appropriate for your application.

TruffleRuby by default runs in the Native Image configuration. In this configuration, TruffleRuby is ahead-of-time compiled to a standalone native executable. This means that you do not need a JVM installed on your system to use it. The advantage of the native configuration is that it starts about as fast as MRI, it may use less memory, and it becomes fast in less time than the JVM configuration. The disadvantage of the native configuration is that you cannot use Java tools like VisualVM, you you cannot use the Java interoperability, and peak performance may be lower than on the JVM. The Native Image configuration is used by default, but you can also request it using --native. To use polyglot programming with the native configuration, you need to use the --polyglot flag.

TruffleRuby can also be used in the JVM configuration, where it runs as a normal Java application on the JVM, as any other Java application would. The advantage of the JVM configuration is that you can use Java interoperability, and peak performance may be higher than the Native Image configuration. The disadvantage of the JVM configuration is that it takes much longer to start and to get fast, and may use more memory. The JVM configuration is requested using --jvm.

If you are running a short-running program, use the default native configuration. If you are running a long-running program and want the highest possible performance, run in the JVM configuration.

To tune TruffleRuby you will need to consider the options of either your JVM or the Native Image, and then the Truffle framework, and the GraalVM compiler. TruffleRuby has a large set of options, which you can see with the --help:languages flag.

Logging

Ruby application logging and warning works as in the standard implementation of Ruby. For logging of TruffleRuby internals, standard Java logging is used. The logging level can be set with --log.level=INFO, =FINEST, or so on.