4

私はメモリバリア/フェンスを使用したプログラミングにかなり慣れていません。セットアップの書き込みが、後で他のCPUで実行されるワーカー関数に表示されることをどのように保証できるのか疑問に思いました。たとえば、次のことを考慮してください。

int setup, sheep;

void SetupSheep():    // Run once
    CPU 1: setup = 0;
    ... much later
    CPU 1: sheep = 9;
    CPU 1: std::atomic_thread_fence(std::memory_order_release);
    CPU 1: setup = 1;

その後(同時にではなく)、何度も何度も実行します。

void ManipulateSheep():
    CPU 2: int mySetup = setup;
    CPU 2: std::atomic_thread_fence(std::memory_order_acquire);
    CPU 2: // Use sheep...

CPU 2では、mySetupが1の場合、 sheep9であることが保証されますがmySetup、0ではないことをどのように保証できますか?

これまでのところ、CPU 2で1になるまでスピンウェイトすることしか考えられません。しかし、スピンウェイトが最初に呼び出されたsetupときに待機するだけでよいことを考えると、これは非常に醜いようです。ManipulateSheep()確かにもっと良い方法があるに違いありませんか?

初期化解除コードには対称的な問題もあることに注意してください。たとえば、存続期間中にメモリを割り当てるロックフリーのデータ構造を作成しているとします。デストラクタでは(すべてのスレッドがメソッドの呼び出しを終了したと仮定して)、すべてのメモリの割り当てを解除する必要があります。つまり、デストラクタを実行しているCPUが最新の変数値を持つ必要があります。デストラクタはそれをチェックするために「最新の」状態が何であるかを知る方法がないため、そのシナリオでスピン待機することさえできません。

編集:私が求めているのは、「すべてのストアが他のCPUに伝播するのを待つ」(初期化の場合)および「すべてのストアがCPUに伝播するのを待つ」(非初期化の場合)と言う方法はありますか?

4

2 に答える 2

1

それが#StoreLoadこの状況にとってまさに正しい障壁であることがわかりました。ジェフ・プレシングが簡単に説明したように

StoreLoadバリアは、バリアの前に実行されたすべてのストアが他のプロセッサに表示されること、およびバリアの後に実行されたすべてのロードがバリア時に表示される最新の値を受け取ることを保証します。

C ++ 11では、std::atomic_thread_fence(std::memory_order_seq_cst)明らかに#StoreLoadバリアとして機能します(他の3つ:、、、#StoreStoreおよび#LoadLoad#LoadStoreこのC++11ドラフトペーパーを参照してください。

補足:x86では、mfence命令#StoreLoadは;として機能します。これは通常_mm_fence()、必要に応じてコンパイラ組み込み関数で発行できます。

したがって、ロックフリーコードのパターンは次のようになります。

Initialize:
    CPU 1: setupStuff();
    CPU 1: std::atomic_thread_fence(std::memory_order_seq_cst);

Run parallel stuff

Uninitialize:
    CPU 2: std::atomic_thread_fence(std::memory_order_seq_cst);
    CPU 2: teardownStuff();
于 2012-11-05T03:24:40.587 に答える
0

実際、メモリバリアは、条件が真になるのを待つ方法を提供しません。ほぼ確実に、オペレーティングシステムが提供する機能(pthread条件変数など)や、Linuxのfutex呼び出しなどの低レベルのプリミティブを使用する必要があります。

ManipulateSheepただし、例で示した障壁は、少なくとも羊の準備ができているかどうかを判断するのに十分です。

(バー)

于 2012-11-03T05:30:08.293 に答える