9

次のコードを使用して操作をプロファイリングし、関数で使用される CPU サイクルを最適化しています。

static __inline__ unsigned long GetCC(void)
{
  unsigned a, d; 
  asm volatile("rdtsc" : "=a" (a), "=d" (d)); 
  return ((unsigned long)a) | (((unsigned long)d) << 32); 
}

2連呼でも「33」の差が出るのでベストとは思いません。助言がありますか ?

4

7 に答える 7

7

個人的には、rdtsc 命令は素晴らしく、さまざまなタスクに使用できると思います。rdtsc の準備に cpuid を使用する必要はないと思います。これが私がrdtscをどのように推論するかです:

  1. 私は Watcom コンパイラを使用しているので、「#pragma aux」を使用して rdtsc を実装しました。これは、C コンパイラがインラインで命令を生成し、edx:eax で結果を期待し、eax と edx の内容が変更されたことをオプティマイザに通知することを意味します。変更されました。これは、オプティマイザーが _asm の近くで最適化を行わないようにしていた従来の _asm 実装からの大幅な改善です。また、"#pragma aux" を使用して、divide_U8_by_U4 を実装したので、clock_cycles を us または ms に変換するときに lib 関数を呼び出す必要はありません。
  2. rdtsc を実行するたびにオーバーヘッドが発生します (作成者の例のようにカプセル化されている場合はさらに多くなります)。これは、測定するシーケンスが短いほど考慮に入れる必要があります。通常、私は内部クロック周波数の 1/30 よりも短いシーケンスの時間を測定しません。これは通常 1/10^8 秒 (3 GHz 内部クロック) になります。私はそのような測定値を事実ではなく指標として使用します. これを知っていれば、cpuid を省略できます。測定回数が多ければ多いほど、事実に近づきます。
  3. 確実に測定するには、1/100 ~ 1/300 の範囲、つまり 0.03 ~ 0.1 us を使用します。この範囲では、cpuid を使用した追加の精度は実質的に重要ではありません。この範囲は、短いシーケンスのタイミングに使用します。これは、CPU の内部クロック周波数に依存するため、私の「非標準」ユニットです。たとえば、1 GHz のマシンでは、0.03 us を使用しません。これは、1/100 の制限を超えてしまい、測定値が指標になってしまうからです。ここでは、最短時間測定単位として 0.1 us を使用します。1/300 は 1 us に近すぎて (以下を参照)、有意な差が生じないため、使用されません。
  4. さらに長い処理シーケンスの場合、2 つの rdtsc 読み取り値の差をたとえば 3000 (3 GHz の場合) で割り、経過したクロック サイクルを換算します。実際には、(diff+1500)/3000 を使用します。ここで、1500 は 3000 の半分です。I/O 待機には、ミリ秒 => (diff+1500000)/3000000 を使用します。これらは私の「標準」ユニットです。私はめったに秒を使用しません。
  5. ときどき予期せぬ遅い結果が得られることがありますが、これは割り込みによるものなのか、それともコードによるものなのかを自問する必要があります。本当に割り込みかどうかを確認するために、さらに数回測定します。その場合...まあ、現実世界では常に割り込みが発生します。シーケンスが短い場合、次の測定が中断されない可能性が高くなります。シーケンスが長い場合、割り込みがより頻繁に発生し、それについてできることはあまりありません。
  6. 長い経過時間を非常に正確に測定すると (時間とそれよりも長い ET が us またはそれ以下)、divide_U8_by_U4 で除算例外が発生するリスクが高くなるため、いつ us を使用し、いつ ms を使用するかを考えます。
  7. 基本的な統計のコードもあります。これを使用して、最小値と最大値を記録し、平均と標準偏差を計算できます。このコードは自明ではないため、測定された ET から独自の ET を差し引く必要があります。
  8. コンパイラが広範な最適化を行っており、読み取り値がローカル変数に格納されている場合、コンパイラはコードを省略できると (「正しく」) 判断する場合があります。これを回避する 1 つの方法は、結果をパブリック (非静的、非スタックベース) 変数に格納することです。
  9. 現実世界の条件で実行されているプログラムは、現実世界の条件で測定する必要があります。それを回避する方法はありません。

タイムスタンプカウンターが正確であるかどうかの問題については、異なるコアの tsc が同期されていると仮定すると (これが標準です)、エネルギー消費を削減するためにアクティビティが少ない期間に CPU スロットリングの問題があると言えます。テスト時に機能を禁止することは常に可能です。同じプロセッサで 1 GHz または 10 Mhz で命令を実行している場合、前者が後者と比較して 1% の時間で完了したとしても、経過サイクル数は同じになります。

于 2010-12-07T10:25:33.160 に答える
2

関数の個々の実行のサイクルを数えようとすることは、実際には正しい方法ではありません。キャッシュミスやブランチの予測ミスによる遅延に加えて、プロセスがいつでも中断される可能性があるという事実は、呼び出しから呼び出しまでにかかるサイクル数にかなりの偏差がある可能性があることを意味します。

正しい方法は次のいずれかです。

  • clock()関数への多数の呼び出しにかかったサイクル数またはCPU時間(を使用)をカウントし、それらを平均します。また
  • Callgrind/kcachegrindのようなサイクルレベルのエミュレートプロファイラーを使用します。

ちなみに、の前にシリアル化命令を実行する必要がありますRDTSC。通常CPUIDは使用されます。

于 2010-09-30T13:48:47.990 に答える
2

心配する必要があるもう 1 つのことは、マルチコア マシンで実行している場合、プログラムが別のコアに移動され、別の rdtsc カウンターを持つ可能性があることです。ただし、システムコールを介してプロセスを 1 つのコアに固定できる場合があります。

このようなものを測定しようとしている場合、おそらくタイムスタンプを配列に記録し、ベンチマーク対象のコードが完了した後に戻ってこの配列を調べます。タイムスタンプの配列に記録されたデータを調べるときは、この配列が CPU キャッシュに依存することに注意する必要があります (配列が大きい場合はページングも可能です)。データ。タイム スタンプ間に非常に規則的な時間差が見られるはずですが、いくつかのスパイクと、場合によってはいくつかのディップがあります (おそらく別のコアに移動したため)。外部イベントがこれらの測定に影響を与えなかったことを示唆しているため、定期的な時間差はおそらく最良の測定値です。

そうは言っても、ベンチマークしているコードのメモリ アクセス パターンや実行時間が不規則であったり、システム コール (特に IO 関連のもの) に依存している場合は、関心のあるデータからノイズを分離するのが難しくなります。

于 2010-09-30T14:53:36.687 に答える
2

あなたは正しい軌道に乗っています1が、次の 2 つのことを行う必要があります。

  1. CPU パイプラインをフラッシュするcpuid前に命令を実行します (測定の信頼性を高めます)。rdtsc私が思い出す限り、 から までのレジスタを破壊eaxedxます。
  2. リアルタイムで測定します。実行時間には、CPU サイクル (ロックの競合、コンテキストの切り替え、および制御できないその他のオーバーヘッド) だけでなく、さらに多くの要素があります。リアルタイムで TSC ティックを調整します。gettimeofdayたとえば、 (Linux、プラットフォームについて言及していないため)呼び出しとrdtsc出力の測定値の違いを取る単純なループで実行できます。次に、各 TSC ティックにかかる時間を知ることができます。別の考慮事項は、CPU 間での TSC の同期です。これは、各コアが独自のカウンターを持っている可能性があるためです。Linux では/proc/cpuinfo、CPU にconstant_tscフラグが設定されている必要があります。私が見たほとんどの新しい Intel CPU には、このフラグがあります。

1個人的には、細粒度測定のrdtscようなシステム コールよりも正確であることがわかりました。gettimeofday()

于 2010-09-30T14:03:11.737 に答える
1

TSC は時間の適切な尺度ではありません。CPU が TSC について行う唯一の保証は、TSC が単調に上昇すること (つまり、一度実行してからもう一度実行すると、2 番目の結果が最初の結果よりも高い結果を返すこと) であり、非常にRDTSC時間がかかることです。ラップアラウンドするのに長い時間。

于 2010-09-30T13:54:30.340 に答える
0

これを行う理由は、他のコードを括弧で囲み、他のコードにかかる時間を測定できるようにするためだと正しく理解していますか?

もう 1 つの良い方法は、他のコードを 10^6 回ループし、ストップウォッチして、マイクロ秒と呼ぶことです。

他のコードを測定したら、時間を短縮するために最適化する価値のある行を知りたいと思いますか?

もしそうなら、あなたはよく踏まれた地面にいます. ZoomLTProfなどのツールを使用できます。以下は私のお気に入りの方法です。

于 2010-09-30T16:52:24.977 に答える