コードが十分に高速に実行されているかどうかを確認するにはどうすればよいですか?コードの速度とパフォーマンスをテストする測定可能な方法はありますか?
たとえば、Numpyを使用して統計情報を計算しながら、CSVファイルを読み取り、新しいCSVファイルを書き込むスクリプトがあります。以下では、PythonスクリプトにcProfilerを使用していますが、結果の統計を確認した後、次に何をしますか?この場合、メソッドが意味する、astype、numpyから減少する、 csvからのメソッドwriterowとpythonリストのメソッドアペンドは、時間のかなりの部分を占めています。
私のコードが改善できるかどうかはどうすればわかりますか?
python -m cProfile -s cumulative OBSparser.py
176657699 function calls (176651606 primitive calls) in 528.419 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.003 0.003 528.421 528.421 OBSparser.py:1(<module>)
1 0.000 0.000 526.874 526.874 OBSparser.py:45(start)
1 165.767 165.767 526.874 526.874 OBSparser.py:48(parse)
7638018 6.895 0.000 179.890 0.000 {method 'mean' of 'numpy.ndarray' objects}
7638018 56.780 0.000 172.995 0.000 _methods.py:53(_mean)
7628171 57.232 0.000 57.232 0.000 {method 'writerow' of '_csv.writer' objects}
7700878 52.580 0.000 52.580 0.000 {method 'reduce' of 'numpy.ufunc' objects}
7615219 50.640 0.000 50.640 0.000 {method 'astype' of 'numpy.ndarray' objects}
7668436 28.595 0.000 36.853 0.000 _methods.py:43(_count_reduce_items)
15323753 31.503 0.000 31.503 0.000 {numpy.core.multiarray.array}
45751805 13.439 0.000 13.439 0.000 {method 'append' of 'list' objects}
誰かがベストプラクティスを説明できますか?
コードが十分に高速に実行されているかどうかはどうすればわかりますか?
それはあなたのユースケースに大きく依存します-あなたのプログラムは1.4時間実行されますが、これは十分に速くても遅くてもかまいません。これが1回限りのプロセスである場合、1.4時間はそれほど多くありません。最適化に時間を費やしても、投資する価値はほとんどありません。一方、これが実行する必要があるプロセスである場合、 1時間に1回、明らかに時間のかかるアプローチを見つける価値があります
コードの速度とパフォーマンスをテストする測定可能な方法はありますか?
はい、プロファイリング-あなたはすでにそれを行っています。それは良いスタートです。
次に何をしますか?
ベストプラクティスは次のとおりです。
すでに1を実行しているので、2に移動しましょう。
分析
あなたのケースでは、プログラムはほとんどの時間をOBSparser.py:48行で費やし、その3分の1は平均7638018回の計算に費やされています。
プロファイラーの出力が示すように、これはndarrayにあります。つまり、numpyを使用しており、呼び出しごとに多くの時間を費やしているようには見えません。簡単な計算により、次のことが確認されます。
179 '/ 7.638.018 =通話あたり23.6マイクロ秒
これはすでにCコード(numpy)で実装されているため、実際のmean
コードを変更する(または別のライブラリを使用する)ことでコールごとのパフォーマンスを向上させるためにできることはほとんどありません。
ただし、いくつかの質問を自問してください。
.mean()
の呼び出し回数を減らすにはどうすればよいですか?.mean()
の呼び出しをより効率的に実装できますか?注目に値するその他の呼び出しは.astype() and reduce
への呼び出しです。説明のために.mean()
simplyに焦点を当てました。
複雑さの軽減
あなたのコードが実際に何をしているのか分からないので、とにかく、具体的な私の5セントです:
2.では、私のi7コアを簡単にチェックすると、ndarray.mean()
が20奇数マイクロ秒かかるのに、約50の値がかかることがわかりました。だから私はあなたが値をグループ化していて、すべてのグループで.mean()
を呼び出していると思います。より効率的な方法があるかもしれません-numpy group aggregate performanceの検索またはそのいくつかのバリアントは、いくつかの有用なポインタを見つける可能性があります。
並列計算
オン3.計算はほとんどCPUに依存しているように思われ、個別のタスクを起動してデータを交換するオーバーヘッドがおそらくメリットを上回るため、ここではマルチプロセッシングが解決策になる可能性は低いと思います。
ただし、SIMDアプローチの一部の使用、つまりベクトル化がある場合があります。繰り返しますが、ただの予感です。
ベースラインパフォーマンスと比較します
再プロファイルにかかる時間を短縮するには、パフォーマンスの動作がまだ見えるようにデータをサブセット化することを検討してください(つまり、.mean()
の呼び出しごとに23 us)。ただし、総実行時間はおそらく1〜2未満です。分、またはそれ以下。これは、プログラムに完全に適用する前に、いくつかのアプローチを評価するのに役立ちます。小さな最適化をテストするためだけに、プロセス全体を何度も繰り返し実行しても意味がありません。
最も基本的な質問を忘れました:
速度はユースケースに満足できますか?
しかし、正直に言って、ほとんどすべての時間がOBSparser.py:48(parse)に費やされているため、それほど時間はかかりません。その方法をいくつかの別々の方法にリファクタリングすることをお勧めします。結果を視覚化するためにビジュアライザーを使用する場合があります。pycharmはそのユースケースを適切にサポートしています。
これが、パフォーマンスの 非機能要件 の目的です。
十分に速いという概念は、それ自体技術的なものは何もありません。それはあなたの製品に対するユーザーの認識に依存し、は要件を通して翻訳されるべきです。これは、実際の実装が十分に高速かどうかを判断するためのonly客観的な方法です。
これらの要件がない場合、それ以外のことは推測であり、建設的ではありません。
ユーザーは、アプリの速度が遅いと感じていますが、いつでも、誰がどのハードウェアでどの機能についてミリ秒単位で何を意味するかを指定していますか?非構築的:これに基づいてコードを改善することはできません。基本的に、以前のリビジョン、コードが許容できないほど遅く、現在は十分に高速であることがわかりません。
あなたは、特定の機能が現在よりも速く実行できると思いますか?これは時期尚早の最適化であり、この機能の速度をまったく気にせず、特定のバグを優先したり、新しい機能が必要になったり、より高速になるために何か他のものが必要になる可能性があるユーザーに反します。
私のコードが改善できるかどうかはどうすればわかりますか?
それは常にできると仮定します。いくつかの手法は次のとおりです。
より多くのメモリを使用してCPUを少なくするか、CPUを多く使用してメモリを少なくするようにコードを書き換えます。これにより、コードの読み取り、理解、維持が非常に困難になることがよくあります。これが、時期尚早の最適化を避けるべき理由の1つです。
さまざまなデータ構造の使用。
キャッシング、事前計算、またはOLAPキューブの使用。
アセンブラーを含む、低レベルに移動します。
タスクを実行していません。全然。これがN秒からゼロまでの究極の最適化です。
テストについてではなく、チューニングについてです。大量のI/Oを実行しているため、あらゆる種類の「CPUプロファイラ」は、ではありません。私がいつも使う方法は this です。
私があなただったら、次のようにします。できるだけ速くなるまでプログラムを調整します。次に、満足できるほど高速でない場合は、より高速なハードウェアを入手します。
私が行う方法は、多数のサンプルを手動で取得することです。それらの一部は、I/Oを実行中です。それらがほとんどI/Oにある場合、そのI/Oの一部を回避する方法があるかどうかを尋ねます。 (事前に、実行中のすべてのI/Oが必要であると想定しないでください。実際に回避できる可能性があることを実行している場合があります。)I/Oの一部を回避できる場合は、それに応じて速度が向上します。
次に、非I/O処理のサンプルの着陸を確認します。サンプルの10%以上がかかるなど、重要ですか?もしそうなら、いくつかの作業を回避することによって、それをスピードアップする方法はありますか?
改善すべき点が見つかるたびに、プログラムを修正して、繰り返し実行します。前回の修正以降、以前には見られなかったいくつかの新しいことが修正に現れたことは嬉しいことに驚くかもしれませんが、今はそれが重要です。修正するものがこれ以上見つからない場合は、プログラムを「あなたまたはおそらく誰でもできるように速く」宣言できます。
それでもまだ十分に高速でない場合は、より高速なCPU、ソリッドステートディスクドライブなどのオプションしか利用できません。
他の人が指摘したように、速度が不十分でない限り、最適化しないでください。
次のステップであるプロファイリングに進みました。
可能な最適化候補を探すための時間をプロファイリングしたら、次のようにします。
パフォーマンスを大幅に改善できる場所はありません。多くの作業により、パフォーマンスが10〜20%向上する可能性があります。パフォーマンスを改善する強い理由がない限り、最適化は完了したと考えます。
少なくとも80%の時間を使用するルーチンを識別していない場合、通常、最適化はあまり役に立ちません。このようなルーチンのパフォーマンスが10倍向上すると、時間は30%以下に短縮されます。
そのようなルーチンを見つけた場合は、より良いアルゴリズムを探してください。そうでない場合は、時間を無駄にしないでください。