The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Is Ruby 2.3 Faster? Nested Iterator Performance

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Alexander Dymo

Posts: 24
Nickname: adymo
Registered: Feb, 2004

Alexander Dymo is the author of the Ruby Performance Optimization book
Is Ruby 2.3 Faster? Nested Iterator Performance Posted: Feb 9, 2016 10:04 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Alexander Dymo.
Original Post: Is Ruby 2.3 Faster? Nested Iterator Performance
Feed Title: Ruby and Rails Performance Optimization
Feed URL: http://ruby-performance-book.com/blog/feed/
Feed Description: This is the blog about Ruby and Rails performance optimization techniques, tips, and tricks. Learn how the newest Ruby releases perform in the field, see how to reduce memory and CPU usage, discover the world of profiling, measuring, and performance testing.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Alexander Dymo
Latest Posts From Ruby and Rails Performance Optimization

Advertisement

Ruby 2.3 was released last month with yet another bunch of performance improvements. But is it really faster than 2.2? Let's take a look.

This is the first post in my series about Ruby 2.3 performance. This time we'll look at nested iterator performance.

What Is Wrong with Nested Iterators?

Most people don't know about this problem, but some iterators create extra objects. For example, every call to Array#each_with_index produces two extra Ruby objects. This doesn't sound like a big deal until you do that in the nested loop. Look at this example:

GC.disable
before = ObjectSpace.count_objects
  Array.new(10000).each do |i|
    [0,1].each_with_index do |j, index|
  end
end

after = ObjectSpace.count_objects
puts "# of arrays: %d" % (after[:T_ARRAY] - before[:T_ARRAY])
puts "# of extra Ruby objects: %d" % (after[:T_NODE] - before[:T_NODE])

Download this file: iterator_each_with_index.rb

How many objects does this example create? Of course, it allocates 10001 arrays. But, surprisingly, you will also see 20000 extra objects when you run this with Ruby 2.2 or earlier.

> ruby array_investigation.rb
# of arrays: 10001
# of extra Ruby objects: 20000

It turns out it's each_with_index iterator allocating 2 extra objects every time you call it. See why in the excerpt from my book and in the book itself.

This is bad for performance. Extra objects add more work for the garbage collector. That results in extra GC cycles that, in turn, slow down your application.

I saw this in my code when the seemingly innocent nested loop added 1.5 seconds of execution time because of additional GC cycles.

Which Iterators Are Bad?

You can find the iterator performance comparison in the excerpt from my book and in the book itself. But these are the most commonly used offenders:

Iterator Array Range Hash
all? 3 3 3
any? 2 2 2
each_with_index 2 2 2
find 2 2 2
map 0 1 1

Note, the comparison table in the book has an incorrect header. It's not the comparison between Enum, Array, and Range. I compare Array, Range, and Hash in the book as well as in the example above.

I also have the code that you can run to demonstrate the problem and get the complete iterator performance comparison table for yourself.

Is Ruby 2.3 Faster?

Immediately after Ruby 2.3 was released, my readers told me the each_with_index example above reports 0 additional objects with Ruby 2.3.

Were iterators optimized? It turned out they were not. Ruby 2.3 internally still creates objects. It's just the object type that changed from T_NODE to T_IMEMO. So this code will expose the bad iterator behavior with Ruby 2.3:

GC.disable
before = ObjectSpace.count_objects

MEMO_OBJECT_TYPE = (RUBY_VERSION >= '2.3.0') ? :T_IMEMO : :T_NODE

Array.new(10000).each do |i|
  [0,1].each_with_index do |j, index|
  end
end

after = ObjectSpace.count_objects
puts "# of arrays: %d" % (after[:T_ARRAY] - before[:T_ARRAY])
puts "# of extra Ruby objects: %d" % (after[MEMO_OBJECT_TYPE] - before[MEMO_OBJECT_TYPE])

> ruby array_investigation.rb
# of arrays: 10001
# of extra Ruby objects: 20000

If you're interested in details, look at the commit into Ruby source code that changed it.

Verdict: Not Faster

Some iterators continue to create additional objects. Their use in nested loops will slow down your code.

Did you like this post? Follow me on Twitter or Google+ to stay updated on Ruby performance optimization news.

Read: Is Ruby 2.3 Faster? Nested Iterator Performance

Topic: Is Ruby 2.3 Faster? Date Parsing Performance Previous Topic   Next Topic Topic: Clojure ‘def’, ‘declare’, and ‘concat’

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use