--nextPart20763057.7iusmhGbMz
Content-Type: multipart/mixed;
  boundary="Boundary-01=_9CMkBOMZk3M6FPy"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--Boundary-01=_9CMkBOMZk3M6FPy
Content-Type: text/plain;
  charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

On Thursday 04 November 2004 07:28, Florian Gross wrote:
> Sean Russell wrote:
> > I was playing around with the SyncEnumerator from Ruby 1.8, and
> > noticed that it was taking a long time to iterate over elements.
=2E..
> > Is there any reason why I shouldn't submit this as an RCR to replace
> > SyncEnumerator?
>
> I wonder how an implementation using .zip with a block would compare? Or
> would that not do the same thing?

#zip ends up being slower in construction (because we have to find the larg=
est=20
array) and faster in iteration.  It is also more elegant, and it is now my=
=20
preferred version.

139 % ruby synctest.rb                                                     =
            =20
~/Work/Sync
Construction
SyncEnumerator1: 0.041632
SyncEnumerator2: 0.138032
Starting SyncEnumerator1
=2E........................................................................=
=2E........................................................................=
=2E.............
26 shold be 26 :: Time: 0.132563
Starting SyncEnumerator2
=2E........................................................................=
=2E........................................................................=
=2E.............
26 shold be 26 :: Time: 0.041874



Here's the test including the original SyncEnumerator; I had to scale down =
the=20
repetitions because the original is about three orders of magnitude slower=
=20
than either of the new versions:

152 % ruby synctest.rb                                                     =
            =20
~/Work/Sync
Construction
SyncEnumerator: 0.245202
SyncEnumerator1: 0.000343
SyncEnumerator2: 0.001459
Starting SyncEnumerator
=2E........................................................................=
=2E......
26 shold be 26 :: Time: 15.135305
Starting SyncEnumerator1
=2E........................................................................=
=2E......
26 shold be 26 :: Time: 0.025891
Starting SyncEnumerator2
=2E........................................................................=
=2E......
26 shold be 26 :: Time: 0.017458



I'm attaching the sources.

* mysync.rb defines SyncEnumerator1 and SyncEnumerator2; #2 is implemented=
=20
with #zip, and is the version I'm proposing replace the original=20
SyncEnumerator.

* mysynctest.rb contains unit tests for both version #1 and #2

* benchmark.rb contains the benchmarks above.

=2D-=20
### SER  =20
### Deutsch|Esperanto|Francaise|Linux|XML|Java|Ruby|Aikido
### http://www.germane-software.com/~ser  jabber.com:ser  ICQ:83578737=20
### GPG: http://www.germane-software.com/~ser/Security/ser_public.gpg

--Boundary-01=_9CMkBOMZk3M6FPy
Content-Type: application/x-ruby;
  name="benchmark.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="benchmark.rb"

require 'generator'
require 'mysync'

I = 100
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"
#classes = [ SyncEnumerator1, SyncEnumerator2 ]
classes = [ SyncEnumerator, SyncEnumerator1, SyncEnumerator2 ]

classes.each { |klass|
  t=Time.now; I.times { klass.new( a,b,c,d ) }; puts "#{klass.name}: #{Time.now-t}"
}
GC.start

for sync in classes.collect { |k| k.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}"
  GC.start
end

--Boundary-01=_9CMkBOMZk3M6FPy
Content-Type: application/x-ruby;
  name="mysynctest.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="mysynctest.rb"

require 'mysync'
require 'test/unit'

class SyncTest < Test::Unit::TestCase
  def test_one
    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 = SyncEnumerator1.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 )

    x = []
    y = []
    z = []
    sync = SyncEnumerator1.new( c, b, a ).each { |m,n,o|
      z << m
      y << n
      x << o
    }
    y.compact!  # Get rid of end nils
    z.compact!
    assert_equal( a, x )
    assert_equal( b, y )
    assert_equal( c, z )
  end

  def test_two
    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 = SyncEnumerator2.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 )

    x = []
    y = []
    z = []
    sync = SyncEnumerator2.new( c, b, a ).each { |m,n,o|
      z << m
      y << n
      x << o
    }
    y.compact!  # Get rid of end nils
    z.compact!
    assert_equal( a, x )
    assert_equal( b, y )
    assert_equal( c, z )
  end
end

--Boundary-01=_9CMkBOMZk3M6FPy
Content-Type: application/x-ruby;
  name="mysync.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="mysync.rb"

class SyncEnumerator1
  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

class SyncEnumerator2
  include Enumerable

  # Creates a new SyncEnumerator which enumerates rows of given
  # Enumerable objects.
  def initialize(*enums)
    @gens = enums
    @biggest = @gens[0]
    @gens.each {|x| @biggest = x if x.size > @biggest.size }
  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
    @biggest.zip( *@gens ) {|a| 
      yield *a[1..-1] 
    }
    self
  end
end

--Boundary-01=_9CMkBOMZk3M6FPy--

--nextPart20763057.7iusmhGbMz
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQBBkMDIP0KxygnleI8RAipvAJ9Cy3+1qPXCJ0bBPbLc0SbU9FVUrgCfX/Ak
hdqPuCBfYufGj+kuA1PR26A=
=ofkU
-----END PGP SIGNATURE-----

--nextPart20763057.7iusmhGbMz--