- TruffleRuby repository
- Installing Ruby
- Running Ruby applications
- What does TruffleRuby do?
- Features entirely missing
- Features with major differences
- Features with subtle differences
- Features with very low performance
- C Extension Compatibility
- Compatibility with JRuby
- Compatibility with Rubinius
- Features not yet supported in native configuration
- Spec Completeness
- Compatibility with gems
- TruffleRuby Options and Command Line
- Operations Manual
TruffleRuby is an implementation of the Ruby programming language on top of GraalVM that aims to:
- Run idiomatic Ruby code faster
- Run Ruby code in parallel
- Boot Ruby applications in less time
- Execute C extensions in a managed environment
- Provide new tooling such as debuggers and monitoring
- All while maintaining very high compatibility with the standard implementation of Ruby
Warning: The support for Ruby is experimental.Experimental features might never be included in a production version, or mightchange significantly before being considered production-ready.
If you are attempting to experiment with deploying TruffleRuby to production wewould encourage you to contact us so we can help you understand what is possible atthe moment and to help solve any issues for you.
The TruffleRuby project is open source and located on GitHub: oracle/truffleruby. If you encounter any issues or want to leave feedback or feature requests please create an issue in this repository.
TruffleRuby can be installed to a GraalVM build with the
graalvm/bin/gu —help for more information.
Run Ruby applications with the standard
$ ruby [options] program.rb
Other Ruby commands are available, including
TruffleRuby uses thesame options as the standard implementation of Ruby with some additions.
$ gem install chunky_png
$ ruby -r chunky_png -e "puts ChunkyPNG::Color.to_hex(ChunkyPNG::Color('mintcream @ 0.5'))"
TruffleRuby in conjunction with the GraalVM produces very fast machine code.Traditionally, performance-critical Ruby code has had to be extracted out tolanguage extensions implemented in native code. This approach is cumbersome anderror-prone. TruffleRuby on the GraalVM is able to optimize Ruby code. Moreimportantly, it can optimize idiomatic Ruby code.
The following example template rendering benchmark is taken from theMRI 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.
<head><%= title %></head>
<h1><%= title %></h1>
<%= content %>
title ='hello world!'
content ="hello world!\n"*10
erb = ERB.new(data)
$ gem install benchmark-ips
$ ruby benchmark.rb
|MRI 2.5.3||JRuby 188.8.131.52||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)|
TruffleRuby runs threads in parallel, without a global-interpreter-lock. Itincludes sophisticated techniques to automatically make objects thread-safe onlywhen they are going to be used by more than one thread, leaving themunsynchronized when used from only one thread to give you parallelism without anoverhead when you do not want to use it.
A hello-world script in TruffleRuby runs about as fast as MRI, or over ten timesfaster than JRuby. TruffleRuby can run without a heavy-weight JVM so that youstart running your Ruby code almost immediately.
TruffleRuby executes C extensions using a managed C interpreter and just-in-timecompiler, not by running native code.
TruffleRuby includes a built-in debugger that allows you to use Chrome DevToolson your Ruby application.
$ ruby --inspect test.rb
Debugger listening on port 9229.
To start debugging, open the following URL inChrome:
TruffleRuby allows you to write polyglot programs using the other languages fromGraalVM.
TruffleRuby aims to be fully compatible with the standard implementation ofRuby, MRI, version 2.6.2. We test extensively using the MRItest suite and the Ruby Spec project.
encoded = JSON.dump(obj)
puts js_obj[:payload].join(' ')
We use the
—jvm —polyglot option to say we want to run a polyglot program.
$ ruby --jvm --polyglot test.rb
TruffleRuby aims to be fully compatible with the standard implementation ofRuby, MRI, version 2.6.2, revision 67232.
Any incompatibility with MRI is considered a bug, except for rare cases detailed below.If you find an incompatibility with MRI, please report it to us.
Our policy is to match the behaviour of MRI, except where we do not know how todo so with good performance for typical Ruby programs. Some features work butwill have very low performance whenever they are used and we advise againstusing them on TruffleRuby if you can. Some features are missing entirely and maynever be implemented. In a few limited cases, we are deliberately incompatiblewith MRI in order to provide a greater capability.
In general, we are not looking to debate whether Ruby features are good, bad, orif we could design the language better. If we can support a feature, we will do.
In the future, we aim to provide compatibility with extra functionality providedby JRuby, but at the moment we do not.
TruffleReport defines these constants for identification:
RUBY_VERSIONis the compatible MRI version
RUBY_REVISIONis the compatible MRI version revision
RUBY_PATCHLEVELis always zero
RUBY_RELEASE_DATEis the Git commit date
RUBY_ENGINE_VERSIONis the GraalVM version, or
0.0-and the Git commit hash if your build is not part of a GraalVM release.
Additionally, TruffleRuby defines:
TruffleRuby.revisionwhich is the Git commit hash
In the C API, we define a preprocessor macro
callcc are unlikely to ever be implemented in TruffleRuby,as their semantics fundamentally do not match the technology that we are using.
fork the TruffleRuby interpreter. The feature is unlikely to everbe supported when running on the JVM but could be supported in the future inthe native configuration. The correct and portable way to test if
fork isavailable is:
The following standard libraries are unsupported.
debug(could be implemented in the future, use
profile(could be implemented in the future, use
profiler(could be implemented in the future, use
io/console(partially implemented, could be implemented in the future)
io/wait(partially implemented, could be implemented in the future)
pty(could be implemented in the future)
ripper(has a no-op implementation, and could be implemented in the future)
fiddle is not yet implemented - the module and some methods are therebut not enough to run anything serious.
We provide our own included implementation of the interface of the
ffi gem,like JRuby and Rubinius. The implementation should be fairly complete and passesall the specs of the
ffi gem except for some rarely-used corner cases.
0 and no other levels are implemented.Trying to use level
1 will raise a
SecurityError. Other levels will raise
ArgumentError as in standard Ruby. See our security notes formore explanation on this.
RubyVM is not intended for users and is not implemented.
TruffleRuby does not include the Darkfish theme for RDoc.
In MRI, threads are scheduled concurrently but not in parallel. In TruffleRubythreads are scheduled in parallel. As in JRuby and Rubinius, you are responsiblefor correctly synchronising access to your own shared mutable data structures,and we will be responsible for correctly synchronising the state of theinterpreter.
TruffleRuby threads may detect that they have been interrupted at differentpoints in the program to where it would on MRI. In general, TruffleRuby seemsto detect an interrupt sooner than MRI. JRuby and Rubinius are also differentto MRI, the behaviour is not documented in MRI, and it is likely to changebetween MRI versions, so we would not recommend depending on interrupt pointsat all.
Most use cases of fibers rely on them being easy and cheap to start up andhaving low memory overheads. In TruffleRuby we implement fibers using operatingsystem threads, so they have the same performance characteristics as Rubythreads. As with coroutines and continuations, a conventional implementationof fibers fundamentally is not compatible with the execution model we arecurrently using.
MRI provides some classes that are described in the documentation as being onlyavailable on MRI (C Ruby). We implement these classes if it is practical to doso, but this is not always the case. For example
RubyVM is not available.
—debug-frozen-string-literal are ignored witha warning as they are unsupported development tools.
Programs passed in
-e arguments with magic-comments must have an encoding thatis UTF-8 or a subset of UTF-8, as the JVM has already decoded arguments by thetime we get them.
—jit options and the
jit feature are not supported because TruffleRubyuses the GraalVM compiler as a JIT.
Ruby normally provides microsecond (millionths of a second) clock precision,but TruffleRuby is currently limited to millisecond (thousands of a second)precision. This applies to
Setting the process title (via
Process.setproctitle in Ruby) is doneas best-effort. It may not work, or the title you try to set may be truncated.
eval where a custom line number can be specified, line numbers below 1are treated as 1, and line numbers above 1 are implemented by inserting blanklines in front of the source before parsing it.
erb standard library has been modified to not use negative line numbers.
If you use standard IO streams provided by the Polyglot engine, via theexperimental
—polyglot-stdio option, reads and writes to file descriptors 1,2 and 3 will be redirected to these streams. That means that other IOoperations on these file descriptors, such as
isatty may not be relevant forwhere these streams actually end up, and operations like
dup may lose theconnection to the polyglot stream. For example, if you
$stdout.reopen, assome logging frameworks do, you will get the native standard-out, not thepolyglot out.
Also, IO buffer drains, writes on IO objects with
sync set, and
write_nonblock, will not retry the write on
EWOULDBLOCK, as thestreams do not provide a way to detect this.
Error message strings will sometimes differ from MRI, as these are not generallycovered by the Ruby Specification suite or tests.
The set of signals that TruffleRuby can handle is different from MRI. Whenlaunched as a GraalVM Native Image, TruffleRuby allows trapping all the samesignals that MRI does, as well as a few that MRI does not. The only signalsthat cannot be trapped are
VTALRM. Consequently, anysignal handling code that runs on MRI can run on TruffleRuby without modificationin the GraalVM Native Image.
However, when run on the JVM, TruffleRuby is unable to trap
QUIT,as these are reserved by the JVM itself. Any code that relies on being able totrap those signals will need to fallover to another available signal. Additionally,
VTALRM cannot be trapped, but thesesignals are also unavailable on MRI.
When TruffleRuby is run as part of a polyglot application, any signals that arehandled by another language become unavailable for TruffleRuby to trap.
Using most methods on
ObjectSpace will temporarily lower the performance ofyour program. Using them in test cases and other similar ‘offline’ operations isfine, but you probably do not want to use them in the inner loop of yourproduction application.
set_trace_func will temporarily lower the performance of your program.As with
ObjectSpace, we would recommend that you do not use this in the innerloop of your production application.
Throwing exceptions, and other operations which need to create a backtrace, areslower than on MRI. This is because we have to undo optimizations that we haveapplied to run your Ruby code fast in order to recreate the backtrace entries.We would not recommend using exceptions for control flow on any implementation ofRuby anyway.
To help alleviate this problem in some cases backtraces are automaticallydisabled where we dynamically detect that they probably will not be used. See theexperimental
VALUE is a pointer type (
void *) rather than ainteger type (
long). This means that
switch statements cannot bedone using a raw
VALUE as they can with MRI. You can normallyreplace any
switch statement with
if statements with littledifficulty if required.
Identifiers which are normally macros may be functions, functions may be macros,and global variables may be macros. This may cause problems where they are usedin a context which relies on a particular implementation (e.g., taking theaddress of it, assigning to a function pointer variable and using defined() tocheck if a macro exists). These issues should all be considered bugs and befixed, please report these cases.
rb_scan_args only supports up to ten pointers.
mark function of
RTYPEDDATA is not called duringgarbage collection. Instead we simulate this by caching informationabout objects as they are assigned to structs, and periodically runall mark functions when the cache has become full to represent thoseobject relationships in a way that the our garbage collector willunderstand. The process should behave identically to MRI.
TruffleRuby does not support the same interop to Java interface as JRuby does.We provide an alternate polyglot API for interoperating withmultiple languages, including Java, instead.
Calling Ruby code from Java is supported by theGraalVM polyglot API.
Use Java extensions written for JRuby is not supported. We could apply the sametechniques as we have developed to run C extensions to this problem, but it isnot clear if this will ever be a priority.
We do not have any plans at the moment to provide support for Rubinius’extensions to Ruby.
- Java interop
Running TruffleRuby in the native configuration is mostly the same as runningon the JVM. There are differences in resource management, as both VMs usedifferent garbage collectors. But, functionality-wise, they are essentially onpar with one another. The big difference is support for Java interop, whichcurrently relies on reflection. TruffleRuby’s implementation of Java interopdoes not work with the GraalVM Native Image Generator’s limited support forruntime reflection.
‘How many specs are there?’ is not a question with an easy precise answer. Thenumber of specs varies for different versions of the Ruby language, differentplatforms, different versions of the specs, and different configurations ofthe specs. The specs for the standard library and C extension API are alsovery uneven and they so can give misleading results.
For the command line interface, the language, and the core library specs,which covers the bulk of what TruffleRuby reimplements, this is how many specexamples TruffleRuby runs successfully compared to our compatible version ofMRI running the version of specs from TruffleRuby:
- Command line 112 / 136, 82%
- Language 2270 / 2332, 97%
- Core library 19453 / 20644, 94%
You can use the compatibility checker to find whether the gems you are interested in are tested on GraalVM, whether the tests pass successfully and so on. Additionally, you can drop your
Gemfile.lock file into that tool and it will analyze all the gems you are using at once. Note that the processing is done on the client-side, so no information is uploaded to any servers.
TruffleRuby has the same command line interface as our compatible MRI version.
Usage: truffleruby [switches][--][programfile][arguments]
-0[octal] specify record separator (\0,ifno 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]
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 or disable features. see below for available features
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
gems rubygems (default: enabled)
did_you_mean did_you_mean (default: enabled)
rubyopt RUBYOPT environment variable (default: enabled)
freeze all string literals (default: disabled)
TruffleRuby also reads the
RUBYOPT environment variable, as in standardRuby, if run from the Ruby launcher.
MRI has some extra Ruby switches which are are not normally listed in help outputbut 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
the same as--external-encoding=external and optionally --internal-encoding=internal
TruffleRuby options are set via
—option=value, or you can use
—ruby.option=value from any launcher. You can omit
=value to set to
Available options and documentation can be seen with
—help:internal to see those categoriesof options. Warning: All options are experimental and subject to change at any time.
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 viaany other way of setting JVM system properties. Finally, options can be set asGraalVM polyglot API configuration options.
The priority for options is the command line first, then the Graal-SDK polyglotAPI configuration, then system properties last.
TruffleRuby options, as well as conventional Ruby options and VM options, canalso bet set in the
RUBYOPT environment variables, ifrun from the Ruby launcher.
— or the first non-option argument stops processing of TrufflRuby and VMoptions in the same way it stops processing of Ruby arguments.
To set options in the underlying VM, use
—vm., valid for both the nativeconfiguration and the JVM configuration.
To set the classpath, use the
= notation, rather than two separate arguments.For example
Other binaries, such as
gem, and so on, support exactly the sameswitches as in standard Ruby.
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:
- The value of the TruffleRuby
- The home that the Truffle framework reports.
- The parent of the directory containing the Ruby launcher executable.
home option is set, it is used even if it does not appear to be a correcthome location. Other options are tried until one is found that appears to be acorrect home location. If none appears to be correct a warning will be given butthe program will continue and you will not be able to require standardlibraries. You can tell TruffleRuby not to try to find a home at all using the
If you are attempting to experiment with deploying TruffleRuby to production wewould encourage you to contact us so we can help you understand what is possibleat the moment and to help solve any issues for you.
There are two main configurations of TruffleRuby - Native Image and JVM. It isimportant to understand the different configurations of TruffleRuby, as each hasdifferent capabilities and performance characteristics. You should pick theexecution mode that is appropriate for your application.
When distributed as part of GraalVM, TruffleRuby by default runs in the Native Image_configuration. In this configuration, TruffleRuby is ahead-of-time compiled to astandalone native executable. This means that you do not need a JVM installed onyour system to use it. The advantage of the native configuration is that itstarts about as fast as MRI,it may use less memory, and it becomes fast in less time than the _JVM configuration. The disadvantageof the native configuration is that you cannot use Java tools like VisualVM, youyou cannot use Java interoperability, and peak performance may be lower than on theJVM. The Native Image configuration is used by default, but you can also request itusing
—native. To use polyglot programming with the native configuration,you need to use the
TruffleRuby can also be used in the JVM configuration, where it runs as anormal Java application on the JVM, as any other Java application would. Theadvantage of the JVM configuration is that you can use Java interoperability, andpeak performance may be higher than the Native Image configuration. The disadvantageof 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
If you are running a short-running program you probably want the default,Native Image, configuration. If you are running a long-running program and want thehighest possible performance you probably want the JVM configuration, by using
To get the best startup time performance in most cases you want to use thenative configuration, which is the default.
To get the lowest memory footprint you probably initially want to use the nativeconfiguration, but as you get a larger working set of objects you may find thatthe simpler garbage collector and current lack of compressed ordinary objectpointers (OOPS) actually increases your memory footprint and you will be betteroff with the JVM configuration using
—jvm to reduce memory use.
To get the best peak performance from TruffleRuby for longer-runningapplications we would in most cases recommend the JVM configuration with
However to reach this peak performance you need to warm-up TruffleRuby, as youdo with most heavily optimizing virtual machines. This is done by running theapplication under load for a period of time. If you monitor the performance (bymeasuring operation time, or response time) you will see it reduce over time andthen probably stabilize.
To tune TruffleRuby you will need to consider the options of either your JVM orthe Native Image, and then the Truffle framework, and the GraalVM compiler.
TruffleRuby has a large set of options, which you can see with the
Ruby application logging and warning works as in the standard implementation ofRuby.
For logging of TruffleRuby internals, standard Java logging is used. The logginglevel can be set with
=FINEST, or so on.
Warning: The GraalVM implementation of Ruby is experimental and we cannot guarantee it to be bug free. Experimental features mightnever be included in a production version, or might change significantly beforebeing considered production-ready.TruffleRuby uses sophisticated techniques to optimise a Ruby program and its users are strongly encouraged to submit useful bug reports to Truffleruby issues. If you encounter a performance problem, please consider the recommendations in Reporting Performance Problems document .