私はコードをベンチマークするためにgprofを使用できることを知っています。
しかし、私はこの問題を抱えています-間接レベルが追加されたスマートポインターがあります(プロキシオブジェクトと考えてください)。
その結果、ほとんどすべての機能に影響を与えるこの追加のレイヤーがあり、キャッシングにねじ込みます。
キャッシュミスが原因でCPUが浪費する時間を測定する方法はありますか?
ありがとう!
あなたは cachegrind を試すことができ、それはフロントエンドのkcachegrindです。
CPUパフォーマンスカウンターにアクセスするツールを見つけることができます。おそらく、L1、L2などのミスをカウントするレジスタが各コアにあります。代わりに、Cachegrindはサイクルごとのシミュレーションを実行します。
しかし、それは洞察力があるとは思いません。プロキシオブジェクトは、おそらく独自のメソッドによって変更されています。 従来のプロファイラーは、これらのメソッドにかかる時間を通知します。キャッシュ汚染の原因がなければパフォーマンスがどのように向上するかを通知するプロファイルツールはありません。これは、プログラムのワーキングセットのサイズと構造を小さくすることであり、簡単に推定することはできません。
Googleのクイック検索が表示された boost::intrusive_ptr
興味があるかもしれません。 weak_ptr
のようなものはサポートされていないようですが、プログラムの変換は簡単な場合があるため、非侵入型の参照カウントのコストが確実にわかります。
Linuxは、2.6.31以降 perf
でサポートしています。これにより、次のことが可能になります。
perf record -e LLC-loads,LLC-load-misses yourExecutable
perf report
LLC-load-misses
行、annotate
。行(元のソースコードで囲まれたアセンブリコード内)と、キャッシュミスが発生した行の最後のレベルのキャッシュミスの割合を示す数字が表示されます。@Mike_Dunlaveyの答えに沿って続けます。
まず、お気に入りのツール(VTune、PTU、またはOProf)を使用して、時間ベースのプロファイルを取得します。
次に、キャッシュミスプロファイルを取得します。 L1キャッシュミス、またはL2キャッシュミス、または...
つまり最初のプロファイルは、「費やされた時間」を各プログラムカウンターに関連付けます。 2番目は、「キャッシュミス数」の値を各プログラムカウンターに関連付けます。
注:データを「削減」して、関数ごとに、または(テクノロジーがある場合は)ループごとに合計することがよくあります。または、たとえば64バイトのビンによって。パフォーマンスカウンターが曖昧であるため、個々のプログラムカウンターを比較しても役に立たないことがよくあります。キャッシュミスが報告される場所は、実際に発生した場所とは異なるいくつかの命令であることがよくあります。
では、これらの2つのプロファイルをグラフ化して比較します。ここに私が役立つと思ういくつかのグラフがあります:
"氷山"チャート:X軸はPC、正のY軸は時間、負のYアクセスはキャッシュミスです。上下に行く場所を探します。
(「インターリーブ」グラフも役立ちます:同じ考え、X軸はPC、Y軸に時間とキャッシュミスの両方をプロットしますが、通常は赤と青の異なる色の細い垂直線を使用します。時間とキャッシュの両方が多い場所費やされたミスは、赤と青のラインが細かく交互に配置され、ほとんど紫に見えます。これは、すべて同じグラフ上のL2およびL3キャッシュミスにまで及びます。時間またはキャッシュミス、またはさらに良いことに、最大データポイントの%ageまたはキャッシュミス。スケールが正しくない場合、何も表示されません。)
XYチャート:各サンプリングビン(PC、または関数、またはループ、...)に対して、X座標が正規化された時間で、Y座標が正規化されたポイントをプロットします。キャッシュミス。右上隅に多くのデータポイントが表示される場合-大きな%age時間と大きな%ageキャッシュミス-これは興味深い証拠です。または、ポイントの数を忘れます-上隅のすべてのパーセンテージの合計が大きい場合...
残念ながら、自分でこれらの分析をロールバックしなければならないことがよくあります。最後に、VTuneがそれを実行しないことを確認しました。私はgnuplotとExcelを使用しました。 (警告:Excelは64千を超えるデータポイントで停止します。)
その他のアドバイス:
スマートポインターがインライン化されている場合、場所全体でカウントが取得される可能性があります。理想的な世界では、PCを元のソースコードの行までたどることができます。この場合、削減を少し延期することをお勧めします。個々のすべてのPCを確認してください。それらをソースコードの行にマッピングします。そしてそれらを元の関数にマッピングします。多くのコンパイラ、例えばGCCには、これを可能にするシンボルテーブルオプションがあります。
ちなみに、あなたの問題は、スマートポインタがキャッシュスラッシングを引き起こしていないことではないかと思います。場所を問わずsmart_ptr <int>を実行している場合を除きます。 smart_ptr <Obj>を実行していて、sizeof(Obj)+が4 * sizeof(Obj *)よりも大きい場合(およびsmart_ptr自体が巨大でない場合)、それほど多くありません。
より可能性が高いのは、スマートポインターが行う間接レベルの追加により、問題が発生することです。
偶然にも、私は昼食時にハンドルを使用している参照カウントされたスマートポインターを持っている人と話していました。
template<typename T> class refcntptr {
refcnt_handle<T> handle;
public:
refcntptr(T*obj) {
this->handle = new refcnt_handle<T>();
this->handle->ptr = obj;
this->handle->count = 1;
}
};
template<typename T> class refcnt_handle {
T* ptr;
int count;
friend refcnt_ptr<T>;
};
(私はこの方法でコードを記述しませんが、説明のために役立ちます。)
二重間接this-> handle-> ptrは、大きなパフォーマンスの問題になる可能性があります。あるいは、トリプルインダイレクションでさえ、this-> handle-> ptr-> fieldです。少なくとも、5サイクルのL1キャッシュヒットがあるマシンでは、this-> handle-> ptr-> fieldはそれぞれ10サイクルかかります。また、単一のポインターチェイスよりもオーバーラップがはるかに困難になります。ただし、さらに悪いことに、それぞれがL1キャッシュミスの場合、たとえL2に対して20サイクルしかなかったとしても、1回のL1ミスよりも2 * 20 = 40サイクルのキャッシュミスレイテンシを隠すのははるかに困難です。
一般に、スマートポインターで間接参照のレベルを回避することをお勧めします。すべてのスマートポインターが指すハンドル(それ自体がオブジェクトを指す)の代わりに、ハンドルだけでなくオブジェクトも指すようにして、スマートポインターを大きくすることができます。 (これは、一般にハンドルと呼ばれるものではなくなりましたが、情報オブジェクトに似ています。)
例えば。
template<typename T> class refcntptr {
refcnt_info<T> info;
T* ptr;
public:
refcntptr(T*obj) {
this->ptr = obj;
this->info = new refcnt_handle<T>();
this->info->count = 1;
}
};
template<typename T> class refcnt_info {
T* ptr; // perhaps not necessary, but useful.
int count;
friend refcnt_ptr<T>;
};
とにかく-時間プロファイルはあなたの親友です。
ええ、そうです-Intel EMONハードウェアは、PCで待機したサイクル数も通知します。これにより、多数のL1ミスと少数のL2ミスを区別できます。
AMDプロセッサを実行している場合は、ビールのように無料で CodeAnalyst を取得できます。
CPUパフォーマンスカウンターベースのプロファイリングのもう1つのツールは oprofile です。結果はkcachegrindを使用して表示できます。
私のアドバイスは、Intelの [〜#〜] ptu [〜#〜] (パフォーマンスチューニングユーティリティ)を使用することです。
このユーティリティはVTuneの直接の子孫であり、利用可能な最高のサンプリングプロファイラーを提供します。 (利用可能なハードウェアイベントの助けを借りて)CPUが費やしている時間を浪費している場所を追跡できます。これにより、アプリケーションの速度低下やプロファイルの混乱は発生しません。そしてもちろん、あなたが探しているすべてのキャッシュラインミスイベントを集めることができます。
これは一種の 一般的な答え です。
たとえば、プログラムが時間の50%をキャッシュミスに費やしている場合、一時停止した時間の50%は、プログラムカウンターが、原因となっているメモリフェッチを待機している正確な場所にあります。キャッシュミス。