Ruby Array concat vs + speed?

sno*_*ard 21 ruby arrays performance

我对Ruby的数组concat()+操作进行了小的性能测试,但concat()速度太快了.

但是我不明白为什么concat()这么快?

有人可以帮忙吗?

这是我使用的代码:

t = Time.now
ar = []
for i in 1..10000
ar = ar + [4,5]
end
puts "Time for + " + (Time.now - t).to_s 


t = Time.now
ar = []
for i in 1..10000
ar.concat([4,5])
end
puts "Time for concat " + (Time.now - t).to_s 
Run Code Online (Sandbox Code Playgroud)

zwi*_*pie 40

根据Ruby文档,区别在于:

数组#+:

连接 - 返回通过将两个数组连接在一起以生成第三个数组而构建的新数组.

数组#concat:

Array#concat:将other_ary的元素追加到self.

因此,+操作员每次调用时都会创建一个新数组(这很昂贵),而concat只附加新元素.


Chr*_*ell 14

答案在于Ruby的+运算符和concat方法的底层C实现.

Array#+

rb_ary_plus(VALUE x, VALUE y)
{
    VALUE z;
    long len, xlen, ylen;

    y = to_ary(y);
    xlen = RARRAY_LEN(x);
    ylen = RARRAY_LEN(y);
    len = xlen + ylen;
    z = rb_ary_new2(len);

    ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR(x));
    ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR(y));
    ARY_SET_LEN(z, len);
    return z;
}
Run Code Online (Sandbox Code Playgroud)

Array#concat

rb_ary_concat(VALUE x, VALUE y)
{
    rb_ary_modify_check(x);
    y = to_ary(y);
    if (RARRAY_LEN(y) > 0) {
        rb_ary_splice(x, RARRAY_LEN(x), 0, y);
    }
    return x;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,+操作员正在从每个阵列复制内存,然后创建并返回包含两者内容的第三个数组.该concat方法只是将新阵列拼接成原始阵列.

  • @NoICE 在 Ruby 中,您可以通过几种方式编写操作数表达式。这里最相关的是`x = x + y`,它等价于`x += y`。除非一个类专门覆盖加号运算符以委托给 `concat`,`+=` 的性能不会与 `concat` 完全相同,因为它使用 `rb_ary_plus` 而不是 `rb_ary_concat`。 (2认同)

the*_*Man 8

如果您要运行基准测试,请利用预构建工具并将测试降低到测试您想要知道的最低要求.

Fruity开始,它为其基准测试提供了大量智能:

require 'fruity'

compare do
  plus { [] + [4, 5] }
  concat { [].concat([4, 5]) }
end
# >> Running each test 32768 times. Test will take about 1 second.
# >> plus is similar to concat
Run Code Online (Sandbox Code Playgroud)

当事情足够接近而不是真的担心时,Fruity会告诉我们他们"相似".

那时Ruby的内置Benchmark类可以帮助:

require 'benchmark'

N = 10_000_000
3.times do
  Benchmark.bm do |b|
    b.report('plus')  { N.times { [] + [4, 5] }}
    b.report('concat') { N.times { [].concat([4,5]) }}
  end
end
# >>        user     system      total        real
# >> plus  1.610000   0.000000   1.610000 (  1.604636)
# >> concat  1.660000   0.000000   1.660000 (  1.668227)
# >>        user     system      total        real
# >> plus  1.600000   0.000000   1.600000 (  1.598551)
# >> concat  1.690000   0.000000   1.690000 (  1.682336)
# >>        user     system      total        real
# >> plus  1.590000   0.000000   1.590000 (  1.593757)
# >> concat  1.680000   0.000000   1.680000 (  1.684128)
Run Code Online (Sandbox Code Playgroud)

注意不同的时间.运行一次测试可能会导致误导结果,因此请多次运行它们.此外,请确保您的循环导致的时间不会被过程启动引起的背景噪音所掩盖.