What's new in Ruby 3.3.0
With the Ruby 3.3.0 release around the corner, we wanted to share a summary of the changes that are coming in this new version. There are many exciting new features and improvements!
Prism
Prism is a new parser for Ruby code developed by Kevin Newton. The objective is to provide a portable and error tolerant parser that can be used by the different Ruby implementations, solving a major pain point all maintainers have to solve when new syntax is added to the language: update all parsers.
It also improves the experience of manipulating Ruby code programmatically, with a more clear API and representation of the parsed code.
The new parser is ready for production use and is already being used by other Ruby implementations, as well as tools and gems that deal with manipulating Ruby code.
During the month of December, Kevin has been writing about how Prism works in the Advent of Prism series .
YJIT
YJIT is a Just-In-Time compiler created by Shopify. It is already available in Ruby 3.2, and Ruby 3.3 comes with performance improvements in many areas and also reduces memory utilization along with other changes.
37signals has reported an 18% speed improvement for Basecamp when they migrated to YJIT, and also reported that, with Ruby 3.3, 98% of their code is now executed by YJIT instead of the Ruby interpreter.
Performance
Apart from the performance benefits from YJIT, there are other areas with improvements in Ruby 3.3:
Garbage Collector
- Handling of young objects was improved to reduce the number of GC collections.
- A new
REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO
environment variable is now available to configure the ratio of unprotected objects that would trigger a GC collection. This allows for better fine-tuning of the GC to reduce the number of GC collections. - More classes use an optimized mechanism to make them faster to allocate and free memory along with more benefits.
- Finally, more classes now have Write Barriers implemented, also making them faster to collect and reducing the number of collections.
It’s important to remember that when the Garbage Collector is performing a garbage collection, our application code is stopped. Therefore, the faster and less frequent the collection is, the better for our app’s performance.
performance
Warning Category
A new category of warning is added in Ruby 3.3 to help us identify code that is known to not be performant. Currently, the only performance
warning in the code can be found here and it helps us identify when new instance variables are added to objects with a shape to warn us that it will impact the speed of accessing them.
You can read more about Ruby warnings in my article Exploring Ruby Warnings .
Other Changes
- When using YJIT, a new
RubyVM::YJIT.enable
method is introduced. This allows us to disable it during boot (with the--disable-yjit --disable-rjit
command line flags), and then enable it once the application starts to speed up the boot process. defined?(@ivar)
is optimized with Object Shapes.
Use Lrama Instead of Bison
Starting with Ruby 3.3, the Lrama parser generator is used instead of Bison . This makes it easier to maintain consistency to avoid having to deal with different versions of Bison in different systems.
RJIT
RJIT is an experimental Just-In-Time compiler implemented in pure Ruby code. The benefit of this JIT compiler is that it removes the need of a C compiler when working with gems that make use of native extensions.
This is not meant for production usage yet, it only supports the x86_64 architecture in Unix platforms.
M:N Thread Scheduler
A new thread scheduler is introduced so applications using Ractors can use more threads in all available cores more efficiently. You can read more about this new feature in the issues tracker .
Changes in Environment Variables
RUBY_GC_HEAP_INIT_SLOTS
has been deprecated and is a no-op. The RUBY_GC_HEAP_{0,1,2,3,4}_INIT_SLOTS
environment variables should be used instead.
A new RUBY_CRASH_REPORT
environment variable was introduced to allow redirecting Ruby crash reports to a file or subcommand.
The RUBY_MN_THREADS
and RUBY_MAX_CPU
environment variables can be used to configure the M:N scheduler.
REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO
is added to fine-tune the garbage collector as previously mentioned.
IRB
Many improvements are coming with the update of the irb
gem, including commands showing more information, better debugging experience, and experimental autocompletion of code using type information provided by Sorbet or RBS.
Deprecations
Process::Status#&
andProcess::Status#>>
are deprecated. The deprecation message will include a suggested method in each case to use as a replacement, since there’s not just one option.Refinement#refined_class
is deprecated in favor ofRefinement#target
, and will be removed in Ruby 3.4.- Subprocess creation/forking via the following file open methods is deprecated is favor of
IO.popen
:Kernel#open
URI.open
IO.binread
IO.foreach
IO.readlines
IO.read
IO.write
it
calls without arguments in a block with no ordinary parameters are deprecated with no replacement.it
will be a reference to the first block parameter in Ruby 3.4.- While not technically a deprecation,
ext/readline
is retired in favor of the new pure-Ruby implementation calledreline
. This simplifies the installation of Ruby by not requiring system libraries likelibreadline
orlibedit
.
Removal of deprecated behavior
We always recommend addressing all deprecated code before jumping to the next version of Ruby. So be sure to run your app with Ruby 3.2 and
deprecated
warnings turned on (check Exploring Ruby Warnings ).
Encoding#replicate
has been removed. The replacement is to use the original encoding class instead.- When given a non-lambda, non-literal block,
Kernel#lambda
now raisesArgumentError
instead of returning it unmodified. - MJIT is removed. Also the
--disable-jit-support
command line flag is removed. Consider using--disable-yjit --disable-rjit
instead.
Default and Bundled Gems
Like with every Ruby release, we have a long list of default and bundled gems that are updated to new versions. The complete list can be found here .
One notable change is that if an application depends on the racc
gem , it now has to be added to the Gemfile.
Conclusion
Ruby 3.3 brings performance improvements in many areas: faster and less frequent garbage collection, memory optimizations, faster code, and YJIT improvements. And the best part is that, in most applications, these performance improvements will be noticeable without any modification of the code!
Apart from that, many new features are added:
- Prism is one of the most exciting additions, not just for MRI but for the whole Ruby community.
- IRB’s autocompletion based on types can help improve the development experience.
- M:N thread scheduler for applications using Ractor means more efficient use of the CPU.
- The new
RUBY_CRASH_REPORT
env variable is great for monitoring.
We cannot list every single change here, so we recommend reading the following list of resources to go into the details:
- Release notes: https://www.ruby-lang.org/en/news/2023/12/11/ruby-3-3-0-rc1-released/
- NEWS.md document: https://github.dev/ruby/ruby/blob/v3_3_0_rc1/NEWS.md
Are you ready to upgrade to Ruby 3.3? We can help you get there!