Apple M1 だと String が速い?
(2023年4月16日に #ruby チャンネルで出た話題の要約版です)
yasulab.icon Ruby 3.2.2 の macOS (Apple M1 MAX) で以下のベンチマークを走らせると「Symbol や Integer より String が速い」という結果になるんですが、何か ARM アーキテクチャ独特の計算があったりするんでしょうか...? 🤔💭 (毎年色んなマシンでやってますが初めてこの結果になってちょっと困惑してます 😂)
code:benchmark.rb
require 'benchmark'
n = 100_000_000
STRING_HASH = { 'foo' => 'bar' }
SYMBOL_HASH = { :foo => 'bar' }
NUMBER_HASH = { 100 => 'bar' }
Benchmark.bmbm do |x|
x.report('String') { n.times { STRING_HASH'foo' } } x.report('Symbol') { n.times { SYMBOL_HASH:foo } } x.report('Number') { n.times { NUMBER_HASH100 } } end
code:console
$ ruby benchmark.rb
user system total real
String 3.428578 0.030301 3.458879 ( 3.568238)
Symbol 5.133289 0.039929 5.173218 ( 5.266821)
Number 5.205668 0.047685 5.253353 ( 5.402222)
k0kubun さんからいただいたフィードバックによると、上記のコードだと STRING_HASH['foo'] のケースだけ opt_aref ではなく opt_aref_with が使われるため、スタックの操作と命令ディスパッチ1つ分は速くなってそう、とのこと。
修正したコードは以下の通り。若干まだ String の方が速いけど、確かに差が小さくなった!
code:benchmark.rb
require 'benchmark'
n = 100_000_000
STRING_HASH = { 'foo' => 'bar' }
SYMBOL_HASH = { :foo => 'bar' }
NUMBER_HASH = { 100 => 'bar' }
str = 'foo'
sym = :foo
num = 100
Benchmark.bmbm do |x|
x.report('String') { n.times { STRING_HASHstr } } x.report('Symbol') { n.times { SYMBOL_HASHsym } } x.report('Number') { n.times { NUMBER_HASHnum } } end
code:console
$ ruby --version
$ ruby benchmark.rb
Rehearsal ------------------------------------------
String 5.121842 0.019780 5.141622 ( 5.142070)
Symbol 5.345363 0.027225 5.372588 ( 5.379260)
Number 5.341885 0.034184 5.376069 ( 5.385101)
-------------------------------- total: 15.890279sec
user system total real
String 5.099321 0.034504 5.133825 ( 5.135626)
Symbol 5.235539 0.030885 5.266424 ( 5.268760)
Number 5.268744 0.028759 5.297503 ( 5.301803)
その後 sue445 さんが、Intel/M1/M2 * YJIT有り/無し の計6パターンで試してくれた。結果は以下の通り。バージョンはいずれも Ruby 3.2.2 で、M1 Mac + YJIT無し の場合のみ String が速い。
code:M2 Mac + YJIT無し
Rehearsal ------------------------------------------
String 2.915968 0.012202 2.928170 ( 2.928223)
Symbol 2.508056 0.014032 2.522088 ( 2.522447)
Number 2.509988 0.009465 2.519453 ( 2.519530)
--------------------------------- total: 7.969711sec
user system total real
String 2.917110 0.011756 2.928866 ( 2.928908)
Symbol 2.510606 0.007054 2.517660 ( 2.518006)
Number 2.515694 0.030624 2.546318 ( 2.551478)
code:M2 Mac + YJIT有り
Rehearsal ------------------------------------------
String 3.025511 0.007270 3.032781 ( 3.032853)
Symbol 2.432758 0.007260 2.440018 ( 2.440215)
Number 2.360472 0.005625 2.366097 ( 2.366156)
--------------------------------- total: 7.838896sec
user system total real
String 3.073160 0.005369 3.078529 ( 3.078621)
Symbol 2.421471 0.004636 2.426107 ( 2.426145)
Number 2.365711 0.003838 2.369549 ( 2.369582)
code:M1 Mac + YJIT無し(String が速い)
Rehearsal ------------------------------------------
String 3.508231 0.000918 3.509149 ( 3.509973)
Symbol 3.859834 0.003388 3.863222 ( 3.878068)
Number 3.785045 0.001296 3.786341 ( 3.788582)
-------------------------------- total: 11.158712sec
user system total real
String 3.415936 0.001131 3.417067 ( 3.420269)
Symbol 3.845968 0.000865 3.846833 ( 3.848751)
Number 3.813097 0.000602 3.813699 ( 3.813613)
code:M1 Mac + YJIT有り
Rehearsal ------------------------------------------
String 3.508843 0.000618 3.509461 ( 3.509411)
Symbol 2.626205 0.001354 2.627559 ( 2.630926)
Number 2.530694 0.000506 2.531200 ( 2.531401)
--------------------------------- total: 8.668220sec
user system total real
String 3.582557 0.002643 3.585200 ( 3.608832)
Symbol 2.623694 0.000471 2.624165 ( 2.624278)
Number 2.539243 0.000737 2.539980 ( 2.540937)
code:Intel Mac + YJIT無し
Rehearsal ------------------------------------------
String 8.883364 0.038110 8.921474 ( 9.245778)
Symbol 7.199444 0.042050 7.241494 ( 7.424929)
Number 7.180403 0.050524 7.230927 ( 7.414303)
-------------------------------- total: 23.393895sec
user system total real
String 9.218174 0.101927 9.320101 ( 10.480423)
Symbol 7.450466 0.079163 7.529629 ( 8.490973)
Number 7.465826 0.068703 7.534529 ( 8.027627)
code:Intel Mac + YJIT有り
Rehearsal ------------------------------------------
String 8.787461 0.056361 8.843822 ( 9.093942)
Symbol 6.312564 0.034961 6.347525 ( 6.464068)
Number 6.527769 0.050138 6.577907 ( 6.870727)
-------------------------------- total: 21.769254sec
user system total real
String 8.830003 0.049355 8.879358 ( 9.051500)
Symbol 6.376301 0.032048 6.408349 ( 6.519433)
Number 6.397982 0.033032 6.431014 ( 6.549404)
考察:Apple M1 だと String が速い?
Apple M1 特有の挙動っぽい (Thx! k0kubun さん && sue445 さん!! 🙏✨)
Apple M2 だと、通常のベンチマークと同じで String が遅い
k0kubun さんの予想:
Apple siliconでのメモリアクセス (のキャッシュ) が速くてそういうことになっている (のでラズパイみたいなlinux-aarch64だと再現しない?)と想像してますが、そのうちちゃんと検証してみたいですね
Linuxだと遅くなる変更で何故かM1では速くなるというのを割と見かけます
トノコト!
ベンチマークを走らせるとき「M1 で測ったら速かったけど M1 以外だとそうでもなかった」というケースがあれば、この記事と同じような状況なのかもしれない 💭