Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby: '&:メソッド名'はブロック変数渡しより若干高速

更新情報

  • 2019/07/12: 初版公開
  • 2022/08/10: 更新

&:メソッド名はブロック変数渡しより若干高速

Use fast ruby idioms by oniofchaos · Pull Request #32337 · rails/railsのしょっぱなに、ブロック変数を用いるブロック渡しより、&:メソッド名渡しの方が高速だと説明されています。

require 'benchmark/ips'

RANGE = (1..100)

def slow
  RANGE.map { |i| i.to_s }
end

def fast
  RANGE.map(&:to_s)
end

Benchmark.ips do |x|
  x.report('Block')          { slow }
  x.report('Symbol#to_proc') { fast }
  x.compare!
end

Ubuntu 18.4.2 LTS(macOS環境のParallels DesktopのVM上)のRuby 2.6.3で実行してみると、誤差の範囲とはいえ、&:メソッド名の方がわずかに高速です。なお、実行にはgem install benchmark-ipsをやっておく必要があります。

ruby block-vs-to_proc.rb
Warming up --------------------------------------
Block 7.317k i/100ms
Symbol#to_proc 8.042k i/100ms
Calculating -------------------------------------
Block 70.244k (± 6.8%) i/s - 351.216k in 5.026038s
Symbol#to_proc 76.526k (± 6.5%) i/s - 386.016k in 5.068287s

Comparison:
Symbol#to_proc: 76525.7 i/s
Block: 70243.9 i/s - same-ish: difference falls within error

なお、macOS Mojave環境のRuby 2.6.3だともう少し差が開きました。いずれも数回回してみましたが、参考値ということで。

ruby block-vs-to_proc.rb
Warming up --------------------------------------
Block 6.854k i/100ms
Symbol#to_proc 6.607k i/100ms
Calculating -------------------------------------
Block 66.699k (± 8.0%) i/s - 335.846k in 5.077837s
Symbol#to_proc 75.243k (± 3.6%) i/s - 376.599k in 5.012293s

Comparison:
Symbol#to_proc: 75242.7 i/s
Block: 66699.0 i/s - 1.13x slower

その後, M1 Macbook Pro 2021とRuby 3.1.2でもやってみました。

$ ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [arm64-darwin21]
$ ruby block-vs-to_proc.rb
Warming up --------------------------------------
               Block    14.991k i/100ms
      Symbol#to_proc    18.080k i/100ms
Calculating -------------------------------------
               Block    150.803k (± 0.5%) i/s -    764.541k in   5.069937s
      Symbol#to_proc    179.763k (± 4.3%) i/s -    904.000k in   5.041927s

Comparison:
      Symbol#to_proc:   179763.2 i/s
               Block:   150803.1 i/s - 1.19x  (± 0.00) slower

おまけ

週刊Railsウォッチ(20190107)より。

トリビアですが、&:の間にはスペースがあっても動作は同じです。

[1, 2, 3].select(&:even?)
[1, 2, 3].select(&     :even?)

RubyのRipperなどでパーサーの動きを確認できます。

require 'ripper'
Ripper.sexp('[1, 2, 3].select(&:even?)')
Ripper.sexp('[1, 2, 3].select(&     :even?)')

おたより発掘

Ruby: `&:メソッド名`はブロック変数渡しより若干高速

これTracePoint( https://docs.ruby-lang.org/ja/latest/class/TracePoint.html )を使って詳細を比較してみると分かる。&:の方が出力されるトレースが少なくてその分速い(って以前纏めた記憶あるけど何処に書いたか忘れた。。

2019/07/15 15:10

関連記事

Ruby: アンパサンドとコロン`&:`記法について調べてみた


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。