2

その行のコピーを保持しているコア/キャッシュが多い場合、キャッシュの書き込みが完了するまでに時間がかかるかどうかを調べるのを手伝ってもらえますか? また、実際にかかる時間を測定/定量化したいと考えています。

Google で有用なものを見つけることができませんでした。最新のプロセッサでは多くのことが発生する可能性があるため、自分で測定するのに加えて、測定したものを解釈するのに苦労しています。(並べ替え、プリフェッチ、バッファリング、そして神は何を知っているか)

詳細:

それを測定する私の基本的なプロセスは、おおよそ次のとおりです。

write soemthing to the cacheline on processor 0
read it on processors 1 to n.

rdtsc
write it on process 0
rdtsc

最終的な時間測定の前に書き込み/無効化が完了していることを確認するために、プロセス 0 での読み取り/書き込みに実際にどの命令を使用するかさえわかりません。

現時点では、アトミック交換(__sync_fetch_and_add())をいじっていますが、この操作の長さにとってスレッドの数自体が重要であるようです(無効にするスレッドの数ではありません)-これはおそらく私が望んでいるものではありません測定する?!。

また、読み取り、書き込み、メモリ バリア (__sync_synchronize()) も試しました。これは私が期待するものに似ていますが、最後の rdtsc が行われたときに書き込みが終了したかどうかもわかりません。

ご想像のとおり、CPU 内部に関する私の知識はやや限られています。

どんな助けでも大歓迎です!

ps: * 測定には Linux、gcc、および pthreads を使用しています。* 私の並列アルゴリズムをモデル化するためにこれを知りたいです。

編集:

1 週間ほどで (明日は休暇を取ります)、さらに調査を行い、コードとメモを投稿して、ここにリンクします (誰かが興味を持っている場合)。

4

2 に答える 2

4

私は非常に長い回答を書き始め、これがどのように機能するかを正確に説明しましたが、正確な詳細についてはおそらく十分に知らないことに気づきました。だから私はより短い答えをします....

したがって、あるプロセッサに何かを書き込むとき、そのプロセッサのキャッシュにまだない場合は、フェッチする必要があり、プロセッサがデータを読み取った後、実際の書き込みが実行されます。そうすることで、システム内の他のすべてのプロセッサにキャッシュ無効化メッセージが送信されます。これらはその後、コンテンツを破棄します。別のプロセッサが「ダーティ」コンテンツを持っている場合、それ自体がデータを書き出し、無効化を要求します。この場合、最初のプロセッサは書き込みを完了する前にデータをリロードする必要があります (そうでない場合、同じキャッシュライン内の他の要素壊れる可能性があります)。

そのキャッシュラインに関心のある他のすべてのプロセッサで、キャッシュに読み戻す必要があります。

__sync_fetch_and_add() は "ロック" プレフィックスを使用します [x86 では、他のプロセッサは異なる場合がありますが、"命令ごと" のロックをサポートするプロセッサの一般的な考え方はほぼ同じです] - これにより、"I want this cacheline EXCLUSIVELY,他の人はあきらめて無効にしてください。」最初のケースと同様に、プロセッサは、別のプロセッサがダーティにした可能性のあるものをすべて再読み取りする必要があります。

メモリ バリアは、データが「安全に」更新されることを保証するものではありません。「このインストラクションが終了するまでに、これまでに (メモリに対して) 発生したことがすべてのプロセッサに表示される」ことを確認するだけです。

プロセッサの使用を最適化する最善の方法は、共有をできるだけ少なくすることです。特に、「誤った共有」を避けることです。何年も前のベンチマークでは、[単純化された] 次のような構造がありました。

struct stuff {
    int x[2];
    ... other data ... total data a few cachelines. 
} data;

void thread1()
{
    for( ... big number ...)
        data.x[0]++;
}

void thread2()
{
    for( ... big number ...)
        data.x[1]++;
}

int main()
{
    start = timenow();

    create(thread1);
    create(thread2);

    end = timenow() - start;   
}

スレッド 1 が x[0] に書き込むたびに、スレッド 2 のプロセッサは x[1] のコピーを削除する必要があり、その逆も同様でした。その結果、SMP テスト [対スレッド 1 の実行のみ] は約 15 回実行されました。もっとゆっくり。次のように構造体を変更します。

struct stuff {
    int x;
    ... other data ... 
} data[2];

void thread1()
{
    for( ... big number ...)
        data[0].x++;
}

1 つのスレッド バリアントの 200% を取得しました [ギブまたはテイク数パーセント]

そうです、プロセッサには、プロセッサがメモリに書き込んでいるときに書き込み操作が格納されるバッファのキューがあります。メモリ バリア (mfence、sfence、または lfence) 命令は、プロセッサが次の命令に進む前に未処理の読み取り/書き込み、書き込み、または読み取りタイプの操作が完全に終了していることを確認するために存在します。通常、プロセッサは後続の命令を順調に実行し続け、最終的にメモリ操作は何らかの方法で実行されます。最新のプロセッサには多くの並列操作とバッファがいたるところにあるため、実際に何かが最終的に到達する場所にたどり着くまでにはかなりの時間がかかる場合があります。そのため、続行する前に実際に何かが行われたことを確認することが重要な場合 (たとえば、ビデオ メモリに一連の命令を書き込んで、それらの命令の実行を開始したい場合は、「命令」の書き込みが実際に終了し、プロセッサの他の部分が終了していることを確認する必要があります。 t はまだそれを仕上げる作業を行っています。だから使用するsfence書き込みが実際に行われたことを確認するためです。これはあまり現実的な例ではないかもしれませんが、おわかりいただけると思います。)

于 2012-12-26T18:29:16.270 に答える
4

キャッシュ書き込みは、キャッシュ ラインをダーティにする前にライン所有権を取得する必要があります。プロセッサ アーキテクチャに実装されているキャッシュ コヒーレンス モデルによって、この手順にかかる時間は異なります。私が知っている最も一般的なコヒーレンス プロトコルは次のとおりです。

  • スヌーピング コヒーレンス プロトコル: すべてのキャッシュが、キャッシュされたメモリ ラインのアドレス ラインを監視します。つまり、すべてのメモリ要求をすべての CPU にブロードキャストする必要があります。つまり、CPU の増加に応じて拡張できません。
  • ディレクトリベースのコヒーレンス プロトコル: 多くの CPU 間で共有されるすべてのキャッシュ ラインは、ディレクトリに保持されます。そのため、所有権の無効化/取得は、ブロードキャストではなくポイントツーポイントのCPUリクエストです。つまり、よりスケーラブルですが、ディレクトリが単一の競合点であるため、遅延が発生します。

ほとんどの CPU アーキテクチャは、PMU (perf monitoring unit) と呼ばれるものをサポートしています。このユニットは、キャッシュ ヒット、ミス、キャッシュ書き込みレイテンシー、読み取りレイテンシー、tlb ヒットなど、多くのカウンターをエクスポートします。この情報が利用可能かどうかを確認するには、CPU のマニュアルを参照してください。

于 2012-12-27T00:07:08.117 に答える