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)
RGENGC
refinements
syntax
Decimal Literal
Frozen String Literal
def’s return value
Bignum
128bit
GMP
String#scrub
Socket.getifaddrs
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.

RGenGC

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

Ruby_2.1_preview_time_in_gc

More Ruby GC info can be found in this talk.

ObjectSpace.trace_object_allocations

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)
end

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"
  end
end


module FooExt
  refine Foo do
    def bar
      puts "FooExt#bar"
    end
  end
end

puts Foo.new.bar

module WorkingWithFooExt
  using FooExt

  # only place where the refinement is applied
  puts Foo.new.bar
end

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.

Syntax

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:)
end
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
  end

  # public
  def bar
  end
end

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.

Socket.getifaddrs

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'
# => [#,
       #,
       ...
       #,
       #,
 #]

Conclusion

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.
 

Conversation
  • Bob says:

    Thanks for this. I too was scratching my head about what exactly some of items in the release notes really meant.

    I bet GitHub would have loved to have ObjectSpace.trace_object_allocations when investigating this issue.

    • Shawn Anderson Shawn Anderson says:

      Glad you liked it Bob. Knowing where your objects are created is incredibly powerful.

  • AndrewO says:

    def returns a symbol of method name… I don’t have many practical uses of this in my head.

    How about a Contract Programming library?

    contract pre: -> { some_precondition },
    post: -> { some_postcondition },
    def transfer_money(amount)

    end

    • Shawn Anderson Shawn Anderson says:

      AndrewO,
      Sounds like a fun idea, send me a link when it’s done. ;)
      Seriously though, I’m sure there are a lot of great ideas based off the return value of def, I just couldn’t think of any for the blog post.

      • AndrewO says:

        Any idea why they didn’t return the Method object itself?

        Meant to say before: great write-up! Thanks for doing the leg work. Looking forward to playing with some of these… except refinements. I will be forever leery of them. :)

        • Shawn Anderson Shawn Anderson says:

          That’s what I asked too! My guess is that returning a symbol is lighter weight and probably what you want most of the time.

  • Jon Abrams says:

    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.

    One I can think of is memoization using the memoist gem: https://github.com/matthewrudy/memoist

  • Breno says:

    Nice post!

  • Nice post!

    GC was really needing an improvement, hope someday Ruby will be a reference in Games world

  • Comments are closed.