ruby 哪种方法在修改哈希的性能方面更好,是`Hash#merge`还是双splat运算符?

hi3rlvi2  于 2023-05-17  发布在  Ruby
关注(0)|答案(2)|浏览(164)

从下面的例子中,哪种方法在性能方面更好?

h = {a: 1, b: 2}

{**h, c: 3} => {:a=>1, :b=>2, :c=>3}
# or 
h.merge(c: 3) => {:a=>1, :b=>2, :c=>3}
qmelpv7a

qmelpv7a1#

基本基准

require 'benchmark/ips'

Benchmark.ips do |x|
  x.config(:time => 10, :warmup => 2)
  h = {a: 1, b: 2}
  
  x.report("splat") {{**h, c: 3}}
  x.report("merge") {h.merge(c: 3)}

  x.compare!
end

表明合并更快,例如

Warming up --------------------------------------
               splat   243.017k i/100ms
               merge   315.349k i/100ms
Calculating -------------------------------------
               splat      3.388M (±11.8%) i/s -     33.293M in  10.005951s
               merge      4.721M (±12.5%) i/s -     46.356M in  10.037133s

Comparison:
               merge:  4720869.7 i/s
               splat:  3388413.3 i/s - 1.39x  (± 0.00) slower
jq6vz3qz

jq6vz3qz2#

差别很小,除非你在一个紧密的循环中执行数百万个这样的操作。
哪一个更快取决于哈希的“位置”。观察:

require 'benchmark/ips'

Benchmark.ips do |x|
  h = {a: 1, b: 2}.freeze
  
  # Warning: these operations don't all have the same effect due to different ordering
  x.report("splat at start") { {**h, c: 3, d: 4} }
  x.report("splat at end") { {c: 3, d: 4, **h} }
  x.report("splat at middle") { {c: 3, **h, d: 4} }
  x.report("merge literal into named") { h.merge(c: 3, d: 4) }
  x.report("merge named into literal") { {c: 3, d: 4}.merge(h) }

  x.compare!
end
puts RUBY_DESCRIPTION

注意:在ruby 3上,你需要安装benchmark-ips gem。

Warming up --------------------------------------
      splat at start   527.143k i/100ms
        splat at end   578.550k i/100ms
     splat at middle   512.272k i/100ms
merge literal into named
                       415.284k i/100ms
merge named into literal
                       546.939k i/100ms
Calculating -------------------------------------
      splat at start      5.087M (± 3.9%) i/s -     25.830M in   5.085388s
        splat at end      5.794M (± 2.4%) i/s -     29.506M in   5.095522s
     splat at middle      5.062M (± 3.1%) i/s -     25.614M in   5.064780s
merge literal into named
                          4.139M (± 2.3%) i/s -     20.764M in   5.019108s
merge named into literal
                          5.324M (± 3.4%) i/s -     26.800M in   5.039716s

Comparison:
        splat at end:  5794056.5 i/s
merge named into literal:  5324076.5 i/s - 1.09x  slower
      splat at start:  5087349.1 i/s - 1.14x  slower
     splat at middle:  5062140.5 i/s - 1.14x  slower
merge literal into named:  4139241.2 i/s - 1.40x  slower

ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin19]

因此,似乎splats,其中splat的散列位于最后位置,是最快的。
有趣的是,“merge literal into named”是最慢的。
注意:如果你的键被覆盖,你不能总是定位哈希以获得最佳性能,因为你会得到不同的结果。

[13] pry(main)> h = {c: 2}
=> {:c=>2}
[14] pry(main)> {a: 1, **h, c: 3}
=> {:a=>1, :c=>3}
[15] pry(main)> {a: 1, c: 3, **h}
=> {:a=>1, :c=>2}

相关问题