Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Ruby 2.5のパフォーマンス向上:文字列補間が最大72%高速化!主要な最適化をベンチマークで徹底解説

Rubyはバージョンアップを重ねるごとに着実に進化しており、Ruby 2.5もその例外ではありません。今回のリリースでは、日常的によく使われるメソッドを中心に、複数のパフォーマンス最適化が導入されました。

Ruby 2.5で導入された主な最適化は以下のとおりです。

  • 大きな文字列を生成する場合、文字列補間(String Interpolation)が約72%高速化
  • 引数が1つだけの場合、String#prependが約42%高速化
  • Enumerable#sort_byEnumerable#min_byEnumerable#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でこの記事をシェアしてください 🙂

  1. Rubyのラムダ徹底解説――基本から実践的な応用テクニックまで

    Rubyにおいてブロックは非常に重要な存在であり、ブロックなしにこの言語を語ることはできません。では、ラムダはどうでしょうか?ラムダを愛している人はいるのでしょうか?実際のところ、何年もラムダを使わずに過ごすことだって可能かもしれません。まるで昔の時代の遺物のように見えることもあるでしょう。 しかし、それは完全な真実ではありません。少し掘り下げてみると、ラムダには興味深い技が数多く隠されているのです。 この記事では、まずラムダの基本的な使い方から始め、その後、より高度で面白い応用へと進んでいきます。すでに毎日ラムダを使いこなしている方は、ぜひ後半の応用編までスクロールしてください。 ラムダにつ

  2. 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)のように終端