ArrayTransformTest

John (@john_drivenupthewall ) asked for this test in another topic.

It compares applying a offset, a transform and zip-map-sum to a coordinate array.

ArrayTransformTest.rb (1.9 KB)

module ArrayTransformTest

  extend self

  @loaded = false unless defined?(@loaded)
  @pt   ||= [50, 65, 0]
  @a    ||= [5, 5, - 5]
  @time ||= {}
  @n    ||= 50000

  def offset
    t = Time.now # start time
    @n.times {|i| @pt.offset( @a ) }
    s = Time.now # stop time
    e = s.to_f - t.to_f # elapsed
    @time[__method__]= e
  end

  def add_coords
    t = Time.now # start time
    @n.times {|i| [@pt.x + @a.x, @pt.y + @a.y, @pt.z + @a.z] }
    s = Time.now # stop time
    e = s.to_f - t.to_f # elapsed
    @time[__method__]= e
  end

  def transform
    t = Time.now # start time
    @n.times {|i| @pt.transform( @a ) }
    s = Time.now # stop time
    e = s.to_f - t.to_f # elapsed
    @time[__method__]= e
  end

  def zip
    if @a.respond_to?(:sum)
      t = Time.now # start time
      @n.times {|i| @pt.zip(@a).map(&:sum) }
      s = Time.now # stop time
    else # Ruby 2.2.4
      t = Time.now # start time
      @n.times {|i| @pt.zip(@a).map {|pair| pair.reduce(:+) } }
      s = Time.now # stop time
    end
    e = s.to_f - t.to_f # elapsed
    @time[__method__]= e
  end

  def go
    result = UI.inputbox(['Iteration Size'],[@n],'Testing ...')
    return unless result
    @n = result.first.to_i
    @time = {}
    GC.start; GC.disable
    add_coords()
    GC.enable; GC.start; GC.disable
    offset()
    GC.enable; GC.start; GC.disable
    transform()
    GC.enable; GC.start; GC.disable
    zip()
    GC.enable
    results()
    GC.start
  end

  def results
    puts
    puts "Results of Array Transform Test : #{@n} iterations"
    puts "  add_coords: #{@time[:add_coords]}"
    puts "  offset    : #{@time[:offset]}"
    puts "  transform : #{@time[:transform]}"
    puts "  zip       : #{@time[:zip]}"
    puts
  end

  unless @loaded
    UI.add_context_menu_handler do |menu|
      menu.add_item('Test Array Transform') { go() } 
    end
    @loaded = true
  end

end

REF:

cheers @DanRathbun, what were your results?

I added forced GC and split out :reduce from :zip

Results of Array Transform Test : 50000 iterations
  transform : 0.03217601776123047
  offset    : 0.03361201286315918
  zip       : 0.05661892890930176
  reduce    : 0.07591795921325684

EDITED: to remove school boy error
john

cool!

okay, I was wonderin’ about that.

I actually made an error there.
I cannot run sum() on v2018 as it’s not in Ruby 2.2.4.
BUT I meant to do this …

  def zip
    if @a.respond_to?(:sum)
      t = Time.now # start time
      @n.times {|i| @pt.zip(@a).map(&:sum) }
      s = Time.now # stop time
    else # Ruby 2.2.4
      t = Time.now # start time
      @n.times {|i| @pt.zip(@a).map {|pair| pair.reduce(:+) } }
      s = Time.now # stop time
    end
    e = s.to_f - t.to_f # elapsed
    @time[__method__]= e
  end

… so as to remove the decision from the elapsed time. But I forgot.


Same basically. offset and transform nearly the same.
With zip-reduce consistently double the former 2.

I see your zip-sum results are midway between.

yeh, I was wondering why :+ was so much faster, then the penny dropped, luckily, before I refactored my code…

I added GC.start ; GC.disable before each, as changing the order changed the results, now it doesn’t…

I my code :offset is probably the best descriptive name, as it deals with screen pixel positions…

I’ll reserve :transform for actual Geom::Transformation actions in this one…

john

1 Like

I added in …

  def add_coords
    t = Time.now # start time
    @n.times {|i| [@pt.x + @a.x, @pt.y + @a.y, @pt.z + @a.z] }
    s = Time.now # stop time
    e = s.to_f - t.to_f # elapsed
    @time[__method__]= e
  end

… add it is consistently the fastest. Which surprised me. I’d have thought transform or offset would be faster.

Results of Array Transform Test : 50000 iterations
  add_coords: 0.02094435691833496
  offset    : 0.022938013076782227
  transform : 0.024932861328125
  zip       : 0.054852962493896484


Results of Array Transform Test : 50000 iterations
  add_coords: 0.02100682258605957
  offset    : 0.022938013076782227
  transform : 0.024934053421020508
  zip       : 0.05385589599609375


Results of Array Transform Test : 50000 iterations
  add_coords: 0.021004915237426758
  offset    : 0.022877931594848633
  transform : 0.023936033248901367
  zip       : 0.05385589599609375

(Note: “zip” above is the zip-reduce loop.)


Updated original code post with GC.disabling and the add_coords method.

I get different results. I wonder why? (using Dan’s last version)

Results of Array Transform Test : 50000 iterations
  add_coords: 0.022701025009155273
  offset    : 0.026060819625854492
  transform : 0.021374940872192383
  zip       : 0.03788399696350098


Results of Array Transform Test : 50000 iterations
  add_coords: 0.022461891174316406
  offset    : 0.025912046432495117
  transform : 0.02156686782836914
  zip       : 0.02959418296813965


Results of Array Transform Test : 50000 iterations
  add_coords: 0.022505998611450195
  offset    : 0.02625894546508789
  transform : 0.021229028701782227
  zip       : 0.029644012451171875

Could be the weather? The other day when I ran it transform was a bit faster than offset (like your results.)

Your zip is likely zip-sum if you ran on SU2019.
(Add: I’m not surprised that zip-map-&sum is faster than zip-map-block-with-reduce-&+.)

But your showing the same basic conclusion that adding individual x, y, z coords is faster.

Likely passing an array parameter into an API method is slower than passing integers (3 times) into the numeric +() method.

?? My results showed transform to be fastest.

# using with latest code...
  def results
    puts
    puts "Results of Array Transform Test : #{@n} iterations"
    @time.sort_by{|a,b| b}.map{|a,b| puts "#{a.to_s.ljust(12,' ')}: #{b}"}
    puts
  end
# returns
Results of Array Transform Test : 50000 iterations
transform   : 0.032013893127441406
add_coords  : 0.03473091125488281
offset      : 0.03669404983520508
zip         : 0.04516196250915527

john

AH, okay time for new :eyeglasses:

The difference might be optimization in Ruby 2.5.5 ?
But a few milliseconds difference over 50000 iterations is not enough to go looking up the code.

Something I noticed: if I increase the number of iterations, most of the ways scale more or less linearly but zip increases much more. Do you see similar?

the first three swap order, and vary speed a bit…

but, I also see :zip suffering the most…

Results of Array Transform Test : 500 iterations
offset       each: 6.537437438964844e-07
add_coords   each: 6.756782531738281e-07
transform    each: 7.538795471191406e-07
zip          each: 8.325576782226562e-07


Results of Array Transform Test : 5000 iterations
add_coords   each: 6.52456283569336e-07
transform    each: 6.715774536132813e-07
offset       each: 8.189678192138672e-07
zip          each: 8.663654327392578e-07


Results of Array Transform Test : 50000 iterations
transform    each: 5.664205551147461e-07
offset       each: 5.955171585083008e-07
add_coords   each: 6.681203842163085e-07
zip          each: 1.1087369918823243e-06


Results of Array Transform Test : 500000 iterations
transform    each: 6.110520362854004e-07
offset       each: 6.357402801513672e-07
add_coords   each: 7.099719047546387e-07
zip          each: 1.2490396499633788e-06


Results of Array Transform Test : 5000000 iterations
transform    each: 6.666581630706787e-07
offset       each: 6.858943939208984e-07
add_coords   each: 8.27260160446167e-07
zip          each: 1.7224379539489746e-06

john