コードの効率を判断するために、どのような種類のツールを使用していますか? 統計的に有意な数のテストを実行する自家製アプリケーションを使用していますか、それとも商用製品を使用していますか? 自分の知識を使ってコードの特定の領域をテストしたり、コードの弱点を分析するツールを使用したりしますか?
9 に答える
挿入: 「統計的有意性」を見てみましょう。
どこかに関数呼び出し命令があるとします。必ずしもそれを確認できるとは限りません - クラス、マクロ、またはコンパイラが、それを取り込んだ可能性があります。近くに同じ関数への他の呼び出しがありますが、この呼び出しはループ内にあるか、その引数が次のようになっています。この呼び出しに時間がかかります。実際には非常に時間がかかるため、この呼び出しにかかる時間をゼロにできれば、合計実行時間はいくらか (たとえば 90%) 短縮されます。(不可能ですか?まったくありません。)タイミングはそれを正確に示しますか?いいえ。コール グラフはそれを正確に示しますか? いいえ、通話回数で特定できますか? いいえ。問題は関数レベルではなく、呼び出し命令レベルにあるためです。
どういうわけかプログラムはある時点でランダムに停止され、その状態が調べられます。命令を「ゼロにする」ことができれば節約される時間の 90% で停止するでしょうか? もちろん - 90% の確率で、命令はその「作業」が完了するのを待ってスタック上で特定されます。
実際、ランダムに 20 回停止すると、その命令は平均 18 回スタックに置かれ、標準偏差は +/- 1.3 回になります。
それは統計的に有意ですか?きっとそうです。
大量のサンプルが必要でしたか? あなたはしなかったに違いない。
パーセンテージが 10% や 5% のように小さいとします。同じ原則が適用されます。
実際、採取されたサンプルがどれほど少なくても、1 サンプルを超える命令は統計的に有意であり、「ホット スポット」、「ボトルネック」、または任意の名前で呼ばれます。それを削除したり、呼び出し回数を減らしたり、何らかの方法で削減したりできれば、かなりの時間を節約できます。(「call _main」のようにできないものもあれば、できるものもあります。それらを見つける必要があるだけです。)
もちろん、私のコードがそれほど馬鹿げたことはないでしょう。では、証明してみよう。
では、話を戻します。. .
元の回答: プロファイラーのことはずっと前に聞いたことがあり、かなりきちんとしているに違いないと思っていましたが、それらにアクセスすることはできませんでした。私は組み込みプロセッサ (8086 Intel チップ) で作業していましたが、ディスプレイ画面に浮動小数点数を描画するのに非常に長い時間がかかっているようです。ハードウェア担当者は、十分に提供することを提案しました-タイマーチップを追加して、処理にかかる時間を確認できるようにしました。しかし、ある週末、インテルの「Blue Box」インサーキット エミュレーターを起動して実行しました。ゆっくりしているうちに「なにやってんの?」と思ってしまいました。だから私はそれを見つけるためにそれを止めました。PC は浮動小数点ライブラリにありました (FP チップはありませんでした)。浮動小数点数を描画していたので、それは当然のことでしたが、もっと知りたいと思いました。だから私は(苦労して)コールスタックをたどるために16進メモリを読みました。何だと思う?ペイントする数値を取得し、それを 10 で割って、整数に変換し、浮動小数点数に戻して、減算などを行っていました。ペイントする次の桁を取得するだけです。言うまでもなく、それを行うためのより良い方法があり、結果として約 10 倍高速化されました。 それは1つのサンプルで見つかりました!
別の機会に、68K チップで、いくらかの速度低下がありました。ここでもプロファイラーは利用できませんでしたが、デバッガー「adb」は利用できたので、遅いうちに数回停止しました。PC は数学ライブラリにあり、実際には 32 ビット整数乗算ルーチンにありました。スタックを調べると、次のコードが見つかりました。
struct {...} a[...];
int i;
for (i = 0; i < ...; ++i){ ... a[i] ... }
そこには乗算する呼び出しはありません - 何が起こっているのですか? の場合、コンパイラは配列要素のサイズa[i]
を乗算する必要があります。i
(そのコンパイラでは)32ビットであるためi
、32ビット乗算ルーチンへの呼び出しが生成され、スタックはその呼び出し命令を特定します。と宣言i
することshort
で、ループの速度が 3 倍になりました。
ポイントは何ですか?プログラムが遅いときにランダムにサンプルを取得すると、PC は何をしているのかを教えてくれますが、コール スタックはその理由を教えてくれ、問題にまっすぐに導きます。ここで、問題が非常に悪い場合を除き、複数のサンプルを取得する必要がある場合があります。1 つ以上のサンプルに現れるすべてのステートメントは、疑わしい可能性があります。関数のようなコードの大きな塊ではなく、ステートメント、命令さえも特定していることに注意してください。このテクニックは「手早く汚い」かもしれませんが、非常に効果的です。
追加: これを繰り返し行うと、同じソフトウェアで次々と問題を解決できます。たとえば、3 倍のスピードアップが得られた場合、以前は重要ではなかった小さなパフォーマンスの問題が残りの 3 倍の時間を消費します。これにより、サンプルをヒットしやすくなります。サンプリングするのに十分長く実行するために、一時的な外部ループを追加する必要がある場合があります。このように、私は40 倍以上の複合スピードアップ ファクターを見てきました。
プロファイラーは、どのコードに最も多くの時間を費やしているかを確認するのに非常に役立ちます。プロファイリング ツールは数多くありますが、通常は使用しているプラットフォーム/開発環境に固有のものです。
小さなケースでは、コードで単純なタイマーを使用しました (アクション終了時のシステム時間 - アクション開始時のシステム時間)。
重要なルールの 1 つ: 実行したばかりのパフォーマンスの最適化が実際により速く実行されると思い込まないでください。いつでも確認!
私は Valgrind とそのツール Callgrind を使用しています。それは素晴らしいツールです。Valgrind は基本的に仮想マシンです。
Valgrind は本質的に、動的再コンパイルを含むジャストインタイム (JIT) コンパイル手法を使用する仮想マシンです。元のプログラムの何もホスト プロセッサで直接実行されることはありません。代わりに、Valgrind は最初にプログラムを中間表現 (IR) と呼ばれる一時的で単純な形式に変換します。これはプロセッサに依存しない SSA ベースの形式です。変換後、Valgrind が IR をマシン コードに変換してホスト プロセッサに実行させる前に、ツール (以下を参照) は IR で必要な変換を自由に行うことができます。動的変換を使用できますが (つまり、ホスト プロセッサとターゲット プロセッサが異なるアーキテクチャからのもの)、使用しません。Valgrind はバイナリ コードを再コンパイルして、同じアーキテクチャのホストおよびターゲット (またはシミュレートされた) CPU で実行します。
Callgrind は、その上に構築されたプロファイラーです。主な利点は、信頼できる結果を得るためにアプリケーションを何時間も実行する必要がないことです。Callgrind はプローブを行わないプロファイラーであるため、数秒で十分です。
Valgrind 上に構築された別のツールは Massif です。これを使用して、ヒープ メモリの使用量をプロファイリングします。それはうまく機能し、メモリ使用量のスナップショットを提供します-何がメモリの何パーセントを保持しているか、誰がそこに置いたかなどの詳細な情報。
もう 1 つの Valgrind ツール -- DRD (および Helgrind)。コード内のデッドロックやデータ競合、スレッド API の悪用を追跡するために使用しています。
私はその仕事のために道具を使います。そうでなければ、自分の手でそれを行うのはかなり難しいと思いました...(まあ、それは私の意見です、実際に試したことはありません)
Linux では、コードをプロファイリングするための便利なツールが付属しているValgrindを使用しています。Valgrind のホームページは次のとおりです。
次のプラットフォームで実行されます: X86/Linux、AMD64/Linux、PPC32/Linux、PPC64/Linux。
.. そしてそれは無料 (無料のビールのように) で、オープン ソースです。
私はそれらをあまり使用していませんが、別のプラットフォームでは、Purify/Quantify (IBM 製品) を使用できます。それらは商用ツールです。Will が報告したように、それらは PurifyPlus パッケージで提供され、Quantify はアプリケーションのプロファイリングに役立ちます。
それが私が使ったすべてです... :-)
正直なところ、私はNUnitを使用しています。コードに時間がかかりすぎたり、スケールしそうにない場合は、アプリのパフォーマンスが低下している部分をシミュレートするテストを作成します。次に、実行時間を短縮するためにコード内で強力な置換を行い、何も壊していないことを確認します。
それでも必要なものが得られない場合は、プロファイラーを見つけて適用する必要がありますが、少なくとも、アプリケーションを開いたりロードしたり、要素への呼び出しを追跡したりせずに、仮定を試すことができるテスト ケースがあります。手元のタスクに影響しないアプリケーションの。
質問があいまいです。ランタイムまたはメモリの観点から効率的ですか? 特定のコードが実行されるコンテキスト、アプリのアーキテクチャ、アプリでスローされる可能性のあるデータもすべて要因となります。
ところで、誰かがPurifyについて言及しました。姉妹製品の Quantify もあります。
.NET の場合はNDependをお勧めします。