【Ruby】each_with_index と each.with_index はどっちが速いのか?
これは、フィヨルドブートキャンプ Part 2 Advent Calendar 2021 - Adventar の 3日目の記事です。
Part1 もあります : フィヨルドブートキャンプ Part 1 Advent Calendar 2021 - Adventar
はじめに
※コメントでご指摘していただいているように、each_with_index と each.with_index のどちらが速いかを結論付けるには不十分な内容になっています。タイトルの結論が出ておらず申し訳ありませんが、1つの参考記録として読んでいただけるとありがたいです。追加で検証をするかは未定です。 (2021/12/20 追記)
Rubyを学び始めると、Enumerable#each_with_index
と Enumerable#with_index
の2つのメソッドについて知る人は多いのではないかと思います。
Enumerable#each_with_index (Ruby 3.0.0 リファレンスマニュアル) Enumerator#with_index (Ruby 3.0.0 リファレンスマニュアル)
with_index
は each.with_index
のように使うことで each_with_index
とは違って offset 引数を受け取れる面で便利ですが、速度はどうなのでしょうか?
調べてみると、古い記事ですが次のようなものが見つかりました。
出典 : difference between each.with_index and each_with_index in Ruby? - Stack Overflow
slightly (わずかに) って実際どれくらいなんだと疑問に思ったので計測してみます。
検証環境
- Ruby 3.0.2
- MacBook Air (13-inch, 2020)
- Intel Core i7
- macOS Big Sur 11.6
処理速度の計測には、benchmark を使います。
(benchmark の使い方については、Ruby でベンチマークを取る方法 - Qiita を参考にさせていただきました。)
検証結果
配列の要素数100件、1000件、1万件それぞれに対して繰り返し処理を実行した結果、
- 100件では
each_with_index
の方が速い - 1000件、1万件では
each.with_index
の方が速い
という結果になりました。
3つのケースしか調べていないため断言はできないですが、差はいずれも0.01秒もないので、どっちを使っても大差はないと思います。
詳細
まず100件。
require 'benchmark' array = Array.new(100) { |i| i } Benchmark.bm 15 do |r| r.report 'each_with_index' do array.each_with_index { |n, i| x = [n, i] } end r.report 'each.with_index' do array.each.with_index { |n, i| x = [n, i] } end end # # 1回目 # => # user system total real # each_with_index 0.000016 0.000003 0.000019 ( 0.000013) # each.with_index 0.000663 0.000001 0.000664 ( 0.000665) # # 2回目 # => # user system total real # each_with_index 0.000017 0.000005 0.000022 ( 0.000014) # each.with_index 0.000772 0.000001 0.000773 ( 0.000775)
まさに slightly (わずかに) each_with_index
のほうが速い。
次は1000件で。
require 'benchmark' array = Array.new(1000) { |i| i } Benchmark.bm 15 do |r| r.report 'each_with_index' do array.each_with_index { |n, i| x = [n, i] } end r.report 'each.with_index' do array.each.with_index { |n, i| x = [n, i] } end end end # # 1回目 # => # user system total real # each_with_index 0.000711 0.000004 0.000715 ( 0.000709) # each.with_index 0.000085 0.000002 0.000087 ( 0.000085) # # 2回目 # => # user system total real # each_with_index 0.000657 0.000004 0.000661 ( 0.000656) # each.with_index 0.000077 0.000001 0.000078 ( 0.000077)
each_with_index
の方が遅くなりました。
しかも、each.with_index
は100件の場合より速くなっている。
更に1万件で。
require 'benchmark' array = Array.new(10_000) { |i| i } Benchmark.bm 15 do |r| r.report 'each_with_index' do array.each_with_index { |n, i| x = [n, i] } end r.report 'each.with_index' do array.each.with_index { |n, i| x = [n, i] } end end # # 1回目 # => # user system total real # each_with_index 0.002912 0.000071 0.002983 ( 0.002979) # each.with_index 0.000889 0.000096 0.000985 ( 0.000985) # # 2回目 # => # user system total real # each_with_index 0.003260 0.000034 0.003294 ( 0.003292) # each.with_index 0.001105 0.000093 0.001198 ( 0.001198)
1万件でも each.with_index
の方が速い結果になりました。
終わりに
検証の結果、この記事が有用なTipsになった感じはしませんが、個人的に気になっていたことをアドベントカレンダーをきっかけに調べてみることができてよかったです。
ということで、フィヨルドブートキャンプ Part 2 Advent Calendar 2021 - Adventar の3日目の記事でした。
(Part1はこちら : フィヨルドブートキャンプ Part 1 Advent Calendar 2021 - Adventar)