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

Rubyコードのベンチマーク

Rubyの標準ライブラリには、コードのパフォーマンスを測定するのに役立つベンチマークツールがあります。 2つの実装を比較して、どちらが最速かを判断するのに最も役立ちます。

この例では、文字列キー({"foo" => "bar"}など)を使用してハッシュを変換する必要があります。 記号付きのもの({:foo => "bar"}など) )。例全体を通して、英語のアルファベットの各文字にキーと値を含むハッシュを使用します。

入力せずにこのハッシュをすばやく生成するために、文字の範囲をテストハッシュに変換します。 inputに入れます 後で使用する変数。

input = ("a".."z").map {|letter| [letter, letter]}.to_h
# => {"a"=>"a", "b"=>"b", "c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f", "g"=>"g", "h"=>"h", "i"=>"i", "j"=>"j", "k"=>"k", "l"=>"l", "m"=>"m", "n"=>"n", "o"=>"o", "p"=>"p", "q"=>"q", "r"=>"r", "s"=>"s", "t"=>"t", "u"=>"u", "v"=>"v", "w"=>"w", "x"=>"x", "y"=>"y", "z"=>"z"}

これで、inputができました。 実装をテストするための変数を作成し、そのパフォーマンスを確認します。入力ハッシュのすべてのキーを文字列ではなく記号に変換するための優れたワンライナーは、次のようになります。

input.map { |key, value| [key.to_sym, value] }.to_h
# => {:a=>"a", :b=>"b", :c=>"c", :d=>"d", :e=>"e", :f=>"f", :g=>"g", :h=>"h", :i=>"i", :j=>"j", :k=>"k", :l=>"l", :m=>"m", :n=>"n", :o=>"o", :p=>"p", :q=>"q", :r=>"r", :s=>"s", :t=>"t", :u=>"u", :v=>"v", :w=>"w", :x=>"x", :y=>"y", :z=>"z"}

この実装では、mapを使用します ハッシュをループして、キーと値のペアごとにブロックを実行するメソッド。ブロック内で、キーをシンボルに変換し、新しく作成されたシンボルキーと、変更されていない値を含む2要素の配列を返します。

mapからの結果 コマンドは、26個のKey-Value配列を持つ配列です。ハッシュが必要なので、#to_hを使用します 新しい配列をハッシュに変換し直します。

Benchmark.measure

実装が機能するようになったので、Rubyのベンチマークモジュールを使用して、そのパフォーマンスを確認できます。

require 'benchmark'
 
input = ('a'..'z').map { |letter| [letter, letter] }.to_h
 
puts Benchmark.measure {
  50_000.times do
    input.map { |key, value| [key.to_sym, value] }.to_h
  end
}

Benchmark.measure 実行にかかった時間を追跡しながら実行されるブロックを取ります。レポート文字列を返します。この文字列は、putsを使用してコンソールに出力されます。 。

これは簡単なコードなので、目に見える結果が得られるように50.000回実行します。

$ ruby bench.rb
  0.810000   0.000000   0.810000 (  0.816964)

レポート文字列には、ユーザーCPU時間を表す4つの数値が表示されます。 (コードの実行に費やされた時間)、システムCPU時間 (カーネルで費やされた時間)、ユーザーとシステムの両方のCPU時間、およびブロックが括弧で囲まれて実行されるのにかかった実際の時間(または実時間)。

実時間は、800ミリ秒強で50.000回を超えるコードのブロックを実行できることを示しています。これは印象的な数字ですが、コードの別の実装と比較しない限り、それが何を意味するのかわかりません。

Benchmark.bm

Benchmark.measureの他に 、RubyはBenchmark.bmを提供します 、複数のコードサンプルを実行し、その結果を出力できます。サンプルごとに、Benchmark#reportと呼びます。 名前と実行するブロックを指定します。

require 'benchmark'
 
input = ("a".."z").map { |letter| [letter, letter] }.to_h
n = 50_000
 
Benchmark.bm do |benchmark|
  benchmark.report("Hash[]") do
    n.times do
      input.map { |key, value| [key.to_sym, value] }.to_h
    end
  end
 
  benchmark.report("{}.tap") do
    n.times do
      {}.tap do |new_hash|
        input.each do |key, value|
          new_hash[key.to_sym] = value
        end
      end
    end
  end
end

このベンチマークでは、Benchmark.bmを使用します それぞれ50.000回実行して、2つの実装をテストします。最初の測定ブロックは前の例と同じです。

2番目の測定ブロックでは、より長い実装を使用します。これにより、新しいハッシュが事前に作成されます。文字列キーハッシュをループし、すべてのアイテムの新しいハッシュに要素を追加します。このように、ハッシュを配列に変換し、終了時にハッシュに戻す必要はありません。

ベンチマークを再度実行すると、この実装は25%以上高速であることがわかりますが、コードは以前に試したワンライナーよりも長くなります(そして少し賢くなりません)。

$ ruby bench.rb
       user     system      total        real
Hash[]  0.850000   0.000000   0.850000 (  0.851106)
{}.tap  0.610000   0.020000   0.630000 (  0.637070)

その他のベンチマーク

コードベース内の重要なコードで作業する場合、ベンチマークを実行してさまざまな実装を比較すると、実行速度についてより多くの洞察を得ることができます。さまざまな実装を比較して、それらがパフォーマンスにどのように影響するかを理解することで、アンチパターンを回避し、より高速なRubyを作成できます。

ヒント :多くの一般的なイディオムは事前にベンチマークされており、その結果はfast-rubyとして公開されています。例を読むことで、将来のベンチマークを節約できます。

この例でテストできるオプションは他にもあります。Rubyのベンチマークライブラリには、試すことができるより高度な機能がありますが、これにより、Rubyでベンチマークがどのように機能するかを知ることができます。ベンチマークについて詳しく知りたい場合、または質問や提案がある場合は、@AppSignalまでお知らせください。


  1. Ruby2.6の9つの新機能

    Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,

  2. Rubyでの静的分析

    ソースコードを解析して、すべてのメソッド、それらが定義されている場所、およびそれらが取る引数を見つけたいとします。 どうすればこれができますか? あなたの最初のアイデアはそれのために正規表現を書くことかもしれません… しかし、もっと良い方法はありますか? はい! 静的分析 は、ソースコード自体から情報を抽出する必要がある場合に使用できる手法です。 これは、ソースコードをトークンに変換する(解析する)ことによって行われます。 さっそく始めましょう! パーサージェムの使用 Rubyには標準ライブラリで利用可能なパーサーがあります。名前はRipperです。出力を操作するのは難し