ggg
11/4/2004 12:10:00 PM
I was playing around with the SyncEnumerator from Ruby 1.8, and
noticed that it was taking a long time to iterate over elements.
The current implementation may be the "correct", or elegant, way to
implement it, but it is terribly inefficient. I rewrote the class,
and got the following results:
Construction
Old: 0.159291
New: 0.000352
Starting SyncEnumerator
.................................................................................
26 shold be 26 :: Time: 19.686455
Starting SyncEnumerator_
.................................................................................
26 shold be 26 :: Time: 0.028817
The new version is nearly three orders of magnitude faster for
construction, and is three orders of magnitude faster in execution.
Is there any reason why I shouldn't submit this as an RCR to replace
SyncEnumerator?
<code name='Implementation'>
class SyncEnumerator_
include Enumerable
# Creates a new SyncEnumerator which enumerates rows of given
# Enumerable objects.
def initialize(*enums)
@gens = enums
end
# Returns the number of enumerated Enumerable objects, i.e. the size
# of each row.
def size
@gens.size
end
# Returns the number of enumerated Enumerable objects, i.e. the size
# of each row.
def length
@gens.length
end
# Enumerates rows of the Enumerable objects.
def each
count = 0
maxsize = 0
@gens.each { |g| maxsize = g.size > maxsize ? g.size : maxsize }
until count >= maxsize
yield @gens.map { |g| g[count] }
count += 1
end
self
end
end
</code>
<code name='Benchmark'>
require 'generator'
require 'mysync'
N = 80
a = %w{ a b c d e f g h i j k l m n o p q r s t u v w x y z }
b = a.reverse
c = %w{ 1 2 3 4 5 6 7 8 9 10 11 12 }
d = c.reverse
puts "Construction"
t=Time.now; 100.times { SyncEnumerator.new( a,b,c,d ) }; puts "Old:
#{Time.now-t}"
t=Time.now; 100.times { SyncEnumerator_.new( a,b,c,d ) }; puts "New:
#{Time.now-t}"
for sync in [SyncEnumerator.new( a, b, c, d ),
SyncEnumerator_.new( a, b, c, d )]
puts "Starting #{sync.class.name}"
time = Time.now
count = 0
N.times { sync.each { |x,y| count += 1 } ; print "." ; STDOUT.flush
}
time = Time.now - time
puts "\n#{count / N} shold be #{a.size} :: Time: #{time}"
end
# SyncEnumerator_.new( a, b, c, d ).each { |x,y,z,w| puts "#{x} #{y}
#{z} #{w}" }
</code>
<code name='Unit Test'>
require 'mysync'
require 'test/unit'
class SyncTest < Test::Unit::TestCase
def test_all
a = %w{ a b c d e f g h i j k l m n o p q r s t u v w x y z }
b = %w{ 1 2 3 4 5 6 7 8 9 10 11 12 }
c = [ 1,2,3,4,5,6,7,9,9,10,11,12,13,14,15 ]
x = []
y = []
z = []
sync = SyncEnumerator_.new( a, b, c ).each { |m,n,o|
x << m
y << n
z << o
}
y.compact! # Get rid of end nils
z.compact!
assert_equal( a, x )
assert_equal( b, y )
assert_equal( c, z )
end
end
</code>