From: mpapis@...
Date: 2014-12-01T19:11:15+00:00
Subject: [ruby-core:66628] [ruby-trunk - misc #10560] confusion between x=x+y, x+=y, x.concat(y) and y.each{|z| x<<z}

Issue #10560 has been updated by Michal Papis.


Recursive Madman it's what I said in the ticket (but unwinded)

Chris Seaton I would assume the `doc` categorization means only the first part was approved to update docs. 
The rest is optimization and does not have to be part of the specification ... or would it have to be part of specification that cloning object can be dropped if it's not used anywhere else?

----------------------------------------
misc #10560: confusion between x=x+y, x+=y, x.concat(y) and y.each{|z| x<<z}
https://bugs.ruby-lang.org/issues/10560#change-50243

* Author: Michal Papis
* Status: Assigned
* Priority: Normal
* Assignee: Zachary Scott
* Category: doc
* Target version: current: 2.2.0
----------------------------------------
while discussing a ticket I have noticed that there is no documentation for `+=`

I was expecting `+=` to behave as `concat` but instead it behaves as `x=x+y` which for every operation clones the array and updates the variable with new value so it behaves similarly to `x=x.dup.concat(y)` and is slightly faster, but using plane `x.concat(y)` is a lot faster from both `each<<` and `+=`

I would either like to get:

 - updated docs that describe concept of `+=` and show the difference from `concat`
 - or change `+=` to use `concat` which is faster - and add docs ;) (I would expect `+=` to use `concat` when available)

here is a test:

    require 'benchmark'
    
    rep = 10_000
    
    Benchmark.bmbm do |x|
      {
        1..25 => [],
        "a".."z" => "",
      }.each do |base, storage|
    
        base = base.to_a
        basej = base
        class_name = storage.class.to_s
    
        x.report(class_name+'#concat') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { a.concat(basej) }
        end
    
        x.report(class_name+'#<<') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { base.each { |e| a << e } }
        end
    
        x.report(class_name+'#+=') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { a += basej }
        end
    
        x.report(class_name+'#dup.concat') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { a = a.dup.concat(basej) }
        end
    
      end
    end


and here are results on my machine:

                            user     system      total        real
    Array#concat        0.000000   0.000000   0.000000 (  0.001422)
    Array#<<            0.020000   0.000000   0.020000 (  0.014356)
    Array#+=            1.270000   0.230000   1.500000 (  1.498558)
    Array#dup.concat    2.720000   0.190000   2.910000 (  2.915701)
    String#concat       0.000000   0.000000   0.000000 (  0.001072)
    String#<<           0.030000   0.000000   0.030000 (  0.025828)
    String#+=           0.130000   0.010000   0.140000 (  0.135143)
    String#dup.concat   0.210000   0.020000   0.230000 (  0.227470)




-- 
https://bugs.ruby-lang.org/