Ruby 2.5のパフォーマンス向上:文字列補間が最大72%高速化!主要な最適化をベンチマークで徹底解説
Rubyはバージョンアップを重ねるごとに着実に進化しており、Ruby 2.5もその例外ではありません。今回のリリースでは、日常的によく使われるメソッドを中心に、複数のパフォーマンス最適化が導入されました。
Ruby 2.5で導入された主な最適化は以下のとおりです。
- 大きな文字列を生成する場合、文字列補間(String Interpolation)が約72%高速化
- 引数が1つだけの場合、
String#prependが約42%高速化 Enumerable#sort_by、Enumerable#min_by、Enumerable#max_byが約50%高速化
それでは、実際のベンチマーク結果を見ていきましょう。
文字列補間(String Interpolation)のパフォーマンス
まずは、この最適化のコミットメッセージに記載されていたサンプルコードを使って検証します。
require 'benchmark/ips'
Benchmark.ips do |x|
x.report "Large string interpolation" do |t|
a = "Hellooooooooooooooooooooooooooooooooooooooooooooooooooo"
b = "Wooooooooooooooooooooooooooooooooooooooooooooooooooorld"
t.times { "#{a}, #{b}!" }
end
x.report "Small string interpolation" do |t|
a = "Hello"
b = "World"
t.times { "#{a}, #{b}!" }
end
x.compare!
end
このベンチマークを実行した結果がこちらです。
Ruby 2.4.1の場合:
Small string interpolation: 3236291.1 i/s Large string interpolation: 1711633.4 i/s - 1.89x slower
Ruby 2.5の場合:
Small string interpolation: 3125175.1 i/s Large string interpolation: 2555782.6 i/s - 1.22x slower
ご覧のとおり、大きな文字列を扱う場合の差は非常に顕著です。従来は小さい文字列と比べて約1.89倍遅かったところが、Ruby 2.5では1.22倍程度まで改善されています。
String#prepend のパフォーマンス
prependメソッドを使うと、文字列の先頭に別のテキストを挿入できます。Ruby 2.5では、最も一般的なユースケースである「1つの文字列を別の文字列の先頭に追加する」処理が最適化されました。
ベンチマーク結果は以下のとおりです。
Ruby 2.4.1の結果:
String#prepend 3.428M (± 3.2%) i/s - 17.159M in 5.011008s
Ruby 2.5の結果:
String#prepend 4.638M (± 3.6%) i/s - 23.276M in 5.025562s
かなりの改善と言えるでしょう。
Enumerableメソッドのパフォーマンス向上
いくつかのEnumerableメソッドにもパフォーマンスの強化が加えられました。
この最適化のポイントは、<=>(宇宙船演算子)メソッドのディスパッチ(メソッド呼び出し)をスキップできることにあります。コミットメッセージには次のように記載されています。
「Fixnum / Float / Stringオブジェクトの比較において、
<=>メソッドのディスパッチの代わりにOPTIMIZED_CMP()を使用してオブジェクトを比較する。」
実際のベンチマーク結果を見てみましょう。
Ruby 2.4.2の場合:
Enumerable#sort_by 2.395k (± 6.7%) i/s - 11.952k in 5.014422s Enumerable#min_by 8.244k (± 6.1%) i/s - 41.405k in 5.042327s Enumerable#max_by 8.053k (± 6.7%) i/s - 40.180k in 5.015375s
Ruby 2.5の場合:
Enumerable#sort_by 5.914k (± 6.7%) i/s - 29.786k in 5.062584s Enumerable#min_by 15.668k (± 3.0%) i/s - 78.888k in 5.039748s Enumerable#max_by 15.544k (± 2.3%) i/s - 78.408k in 5.046709s
おおよそ50%の高速化という結果です 🙂
Range#min と Range#max の高速化
さらに、ボーナスとして2つの最適化をご紹介します。
1つ目は、Range#minおよびRange#maxメソッドに関するものです。
ベンチマーク結果は以下のとおりです。
Ruby 2.4.2の場合:
Range#min 7.976M (± 3.0%) i/s - 39.950M in 5.013242s Range#max 7.996M (± 3.4%) i/s - 40.059M in 5.015984s
Ruby 2.5の場合:
Range#min 13.154M (± 3.0%) i/s - 65.731M in 5.002094s Range#max 13.021M (± 2.6%) i/s - 65.202M in 5.010924s
こちらも約60%以上の大幅な高速化を実現しています。該当するコミットはこちらから確認できます。
String#scan の改善
コミットメッセージによると、この最適化により、文字列パターンの場合は50%、正規表現パターンの場合は10%のパフォーマンス向上が得られるとのことです。
それではベンチマークを見てみましょう。
Ruby 2.4.2の場合:
String#scan - String pattern
1.367M (±19.8%) i/s - 6.458M in 4.982047s
String#scan - Regex pattern
1.228M (±17.0%) i/s - 5.881M in 4.983943s
Ruby 2.5の場合:
String#scan - String pattern
3.944M (±24.4%) i/s - 17.739M in 4.977417s
String#scan - Regex pattern
1.696M (±17.4%) i/s - 8.103M in 4.982614s
特に文字列パターンでの検索が大きく速くなっています。快適なスキャンライフを!
まとめ
この記事では、12月25日にリリースされるRuby 2.5に搭載される新しい最適化について学びました。
対象となったのは、以下の機能です。
- 文字列補間(String Interpolation)
Enumerable系メソッド(sort_by/min_by/max_by)String#prependメソッドString#scanメソッドRange#max/Range#minメソッド
これらの最適化は特別なコード変更なしに恩恵を受けられるものばかりなので、Ruby 2.5へのアップグレードを検討する大きな理由になるでしょう。
最後までお読みいただきありがとうございました。ぜひお気に入りのSNSでこの記事をシェアしてください 🙂
-
Rubyのラムダ徹底解説――基本から実践的な応用テクニックまで
Rubyにおいてブロックは非常に重要な存在であり、ブロックなしにこの言語を語ることはできません。では、ラムダはどうでしょうか?ラムダを愛している人はいるのでしょうか?実際のところ、何年もラムダを使わずに過ごすことだって可能かもしれません。まるで昔の時代の遺物のように見えることもあるでしょう。 しかし、それは完全な真実ではありません。少し掘り下げてみると、ラムダには興味深い技が数多く隠されているのです。 この記事では、まずラムダの基本的な使い方から始め、その後、より高度で面白い応用へと進んでいきます。すでに毎日ラムダを使いこなしている方は、ぜひ後半の応用編までスクロールしてください。 ラムダにつ
-
Ruby 2.6の新機能9選|コード例でわかる注目ポイントを徹底解説
Ruby 2.6には、開発者の生産性を高める新しい機能やパフォーマンス改善が多数盛り込まれています。 本記事では、Ruby 2.6で導入された9つの注目新機能を、実際のコード例とともにわかりやすく紹介します。最新のRuby動向をキャッチアップしたい方は、ぜひ最後までご覧ください。 1. 無限Range(Endless Range) Ruby 2.5以前でもFloat::INFINITYを使えば終端のない範囲を表現できましたが、Ruby 2.6ではさらに直感的な記法が使えるようになりました。 新しい無限Rangeは次のように書きます。 (1..) 通常のRangeが(1..10)のように終端