Tech Racho エンジニアの「?」を「!」に。
  • 開発

Ruby 2.5のパフォーマンス改善点(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Ruby 2.5のパフォーマンス改善点(翻訳)

Rubyは常に改善を繰り返しており、Ruby 2.5でも同様です。

Ruby 2.5でいくつかの最適化が行われました。

  • サイズの大きな文字列を作成したときの式展開が約72%高速化
  • String#prependの引数が1つだけの場合に約42%高速化
  • Enumerable#sort_byEnumerable#min_byEnumerable#max_byが約50%高速化

ベンチマークを見てみましょう。

文字列の式展開パフォーマンス

この最適化のコミットメッセージに含まれていたコード例を使いました。

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.89倍遅い
  • Ruby 2.5:
Small string interpolation:  3125175.1 i/s
Large string interpolation:  2555782.6 i/s - 上より1.22倍遅い

見てのとおり、サイズの大きな文字列でパフォーマンスがかなり向上しました。

String#prependのパフォーマンス

prependメソッドは、arrayの先頭にテキストを挿入します。

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#minRange#max

他にも2つのパフォーマンス改善があります。

1つはRange#minRange#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

コミットはこちら

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

高速化したscanに乾杯!

まとめ

12/25にリリース予定のRuby 2.5で導入される新しい最適化について解説しました。

最適化は、文字列の式展開、EnumerableメソッドString#prependメソッド、String#scanメソッド、そしてRange#max / Range#mixで行われました。

本記事がお役に立てば幸いです。

ぜひ、好みのSNSで本記事をシェアしてください(元記事からどうぞ)🙂

関連記事

ベンチマークの詳しい理解と修正のコツ(翻訳)

RailsConf 2017のパフォーマンス関連の話題(1)BootsnapやPumaなど(翻訳)


CONTACT

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