Kicking the Tires on Ruby 2.1 Preview

A preview of Ruby 2.1 was released a couple of days ago with these release notes:

VM (method cache)
Decimal Literal
Frozen String Literal
def’s return value
new Rubygem

I found myself scratching my head for a minute. I don’t know what most of these are! So let’s dig into them and dive into some of the details.


Ruby 2.1 includes a new generational garbage collector. Prior to 2.1, MRI Ruby used a conservative stop the world while I take out the trash version of mark and sweep for its garbage collector. In the 2.1 preview, it still uses mark and sweep, but with a little generational twist.

The new approach tracks objects in two buckets: young and old. The mark and sweep GC is always run on the young generation and only run on the older generation if there is a need for more memory before resizing the heap. Young objects are promoted to old after surviving a single GC (most objects die young, or at least the good ones).

The new garbage collector is backwards compatible and should not break existing C extensions.

The few benchmarks that I’ve ran show a slight improvement from 2.0 to 2.1. What stood out to me was the 39% less time spent in the GC, which means less time with the whole world being stopped. That should mean less jitter from GC in things like Gamebox games.

Ruby 2.0 vs 2.1-preview performance


More Ruby GC info can be found in this talk.


Tracking allocations in Ruby used to require creative tracing or recompiling Ruby itself. In 2.1 ObjectSpace.trace_object_allocations lets you turn on tracing for a single block of code. This would have been useful when I was debugging some serious Array creation problems in a game I was working on.

require 'objspace'
ObjectSpace.trace_object_allocations do
  a = [2,3,5,7,11,13,17,19,23,29,31]
  puts ObjectSpace.allocation_sourcefile(a)
  puts ObjectSpace.allocation_sourceline(a)

trace_object_allocations will allow for more complex APIs to be built. (such as objectspace-stats).

Refinements (for real this time)

Refinements, marked experimental in 2.0, are now official in Ruby 2.1. They essentially allow for local monkey patching. Like all monkeys, they should be used with extreme caution. Refinements should help reduce a lot of the issues that come with global monkey patching.

class Foo
  def bar
    puts "Foo#bar"
module FooExt
  refine Foo do
    def bar
      puts "FooExt#bar"
puts Foo.new.bar
module WorkingWithFooExt
  using FooExt
  # only place where the refinement is applied
  puts Foo.new.bar
puts Foo.new.bar

klasscache (method caching patch)

The method cache is no longer global. Ruby 2.1 introduces a hierarchical class cache similar to the what Rubinius and JRuby use. I never realized how bad MRI’s method cache was. This post by James Golick does a great job of explaining these changes. The gist is: less clearing out the cache, better cache hit percentages, generally better performance.


Required Keyword Args

If you’ve seen the keyword arguments from Ruby 2.0, then the syntax for required keywords will make sense.

def foo(bar:)
foo(bar: 12) # ok
foo          # ArgumentError: missing keyword: bar

Decimal Literal

Ruby 2.1 added the r and i suffixes as syntactical sugar for creating Rational and Complex numbers respectively:

27r     # => Rational(27/1)
1/2r    # => Rational(1/2)
12+33i  # => Complex(12,33)

Frozen String Literal

You can now create shared frozen strings with the f suffix. This will definitely be helpful for templating libraries.

3.times{ puts 'blah'.object_id }
# => 70287241728160
# => 70287241728080
# => 70287241728020
3.times{ puts 'blah'f.object_id }
# => 70287241709760    same object
# => 70287241709760
# => 70287241709760

def returns a symbol of method name

In previous version of Ruby, the def keyword returned nil. It now returns the symbol of the method name. I don’t have many practical uses of this in my head. The only one I’ve seen is for method access:

class Foo
  # foo will be private
  private def foo
  # public
  def bar

Big Numbers Get a Speed Boost

Ruby 2.1 uses 128bit integers when available and makes use of Gnu Multiple Precision (GMP). These changes will improve performance for anyone using large numbers in Ruby.


This new method on Socket is a wrapper to the getifaddrs function. It returns an Array of Ifaddr instances.

ruby -rpp -rsocket -e 'pp Socket.getifaddrs'
# => [#<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>,
       #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80::1%lo0 netmask=ffff:ffff:ffff:ffff::>,
       #<Socket::Ifaddr en0 UP,BROADCAST,RUNNING,NOTRAILERS,MULTICAST,0x800 fe80::baf6:b1ff:fe1b:4ebb%en0 netmask=ffff:ffff:ffff:ffff::>,
       #<Socket::Ifaddr en0 UP,BROADCAST,RUNNING,NOTRAILERS,MULTICAST,0x800 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=>,
 #<Socket::Ifaddr p2p0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[p2p0 0a:f6:b1:1b:4e:bb]>]


I’m glad to see a shorter release cycle for Ruby. The VM improvements and generational garbage collector make me wish the preview was here already. But like any preview, all of these details are subject to change between now and the release date of Christmas 2013.