スクリプトの平均実行時間を測定する方法は?


23

それぞれ数の階乗を計算する2つのスクリプトがあります。どちらが速いか知りたいです。timeコマンドは私にミリ秒を与え、その結果は随時異なります。

piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.052s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.091s
user    0m0.048s
sys 0m0.036s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.040s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.087s
user    0m0.064s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.068s
sys 0m0.016s
piousbox@piousbox-laptop:~/projects/trash$ 

スクリプトの実行にかかる平均時間をどのように取るのですか?100 timeの出力を解析して平均化することはできますが、より良い解決策があると思いますか?


回答:


4

いいえ、平均化の考え方は正しいです。

スクリプトの実行は多くの要因に依存しますが、セットアップ時間(メモリにインタープリターをロードし、セットアップし、場合によってはコードをバイトコードまたはマシンコードにコンパイルする)と実際の実行時間に分割する必要があります。

内側の実行時間により焦点を当てるために、スクリプト自体でループを実行します(つまり、1つの階乗を計算する代わりに、スクリプトの1回の実行で100回計算します。スクリプトは1回セットアップされ、内側のルーチンは100回)。

合計時間に集中するには、スクリプトを100回実行し、結果を平均します。理想的には、システムが毎回「参照状態」(またはスクリプトに関係のない状態)で戻るように、これらの実行を十分に分離する必要があります。たとえば、インタプリタ自体はメモリにキャッシュされるため、スクリプトの最初の実行は後続のスクリプトよりもかなり遅くなります。

アルゴリズムのより良い洞察を得るために、私は最善の方法は次のようなものだと思います(そうでなければアイドル状態のマシンで):

  • アルゴリズムを1つの関数にラップします。
  • 制御アプリケーションで:
    • 一度関数を呼び出す
    • システム(「ウォールクロック」)時間を取得し、10(または妥当なN)秒を追加します
    • ループに入り、反復のカウントを開始します
    • 関数を呼び出すたびに、カウンターをインクリメントします
    • システム時間が保存時間を下回る場合は、別のループを実行します
    • 現在の壁時計時刻から正確なN(おそらく浮動小数点)を取得します
    • カウンタをNで割った値を表示します。これは、1秒あたりの反復回数です。

アプリケーションは1回だけ実行され、すべてのセットアップとプライミングは最初のタイミングのない反復によって行われるため、オーバーヘッドを最小限に抑える必要があります(時間呼び出しを除く)。

関数が入力を受け取る場合、固定値でシードされたPRNGを使用してランダムな入力シーケンスを提供し、テストされる関数の両方のバージョンが同じ値を受け取るようにします。これにより、「ラッキーナンバー」のために1つの関数のパフォーマンスが明らかに向上します(たとえば、ソートするアイテムの数が2 k -1でkが小さい場合に、ヒルソートアルゴリズムのパフォーマンスがかなり向上したことを覚えています)。


そう、ありがとう。後続の呼び出しが短くなることに気付きました。スクリプト内でループを実行すると、1つのアルゴリズムが他のアルゴリズムよりも確実に高速であることがわかりました。
ビクターピウスボックス

39

プログラムの反復をループで実行できます。合計時間を反復回数で除算します。

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s

2
超シンプル、大好きです。また、私は{1..10}前に見たことがないし、それが動作することに困惑している、それはバッシュマニュアルで見つけることができません。悲しいことは、結果の広がり(最小時間と最大時間)がわからないことです。
w00t 14年

@ w00t:man -P 'less +/Brace\ Expansion' bash
user2683246

ありがとう@ user2683246!それから、gnu.org / software / bash / manual / bash.html#Br​​ace - Expansionでそれを見つけました。今、私はこれがbashに登場した時期にも興味があります…
-w00t


2
これがGoogle社員の到着に対して機能しない場合は、実行していない可能性がありますbash/bin/bashこの前に実行してみてください。
コリークライン

14

これを正確に行うマルチタイムと呼ばれるツールがあります。コマンドを数回実行し、その所要時間を測定します(平均、最小/最大、および中央時間が自動的に計算された実/ユーザー/システム)

たとえば、同様のスクリプトを100回測定する場合:

multitime -q -n 100 "fact1.sh"
===> multitime results
1: -q fact1.sh
            Mean        Std.Dev.    Min         Median      Max
real        0.122       0.032       0.086       0.116       0.171       
user        0.148       0.044       0.096       0.137       0.223       
sys         0.023       0.019       0.000       0.014       0.061 

12

これは古いですが、以前使用したコマンドが見つからなかったのを探していたときに、Googleで非常に高くなりました。とにかく、これを行うための私の好ましい方法は:

perf stat -r 10 -B sleep 1

これにより、最後の平均実行時間など、かなりの詳細が得られます。

1.002248382 seconds time elapsed                   ( +-  0.01% )


1

Hyperfineは別のオプションです。

サンプル使用法:

hyperfine --warmup 3 'ruby fac1.rb'

1
ハイパーファインは他の選択肢よりもはるかに優れているため、ばかげています。必要な実行、ウォームアップ、美しい出力、
マークダウン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.