2

インターネットでは、並列プログラミングでのキーワードの使用について多くの議論がありvolatile、時には矛盾した議論があります。

このトピックに関するより信頼できる議論の1つは、ArchRobisonによるこの記事のようです。彼が使用している例は、あるスレッドから別のスレッドに値を渡すタスクです。

スレッド1は行列積を計算し、それをスレッド2に渡します。スレッド2はそれを使って他のことを行います。マトリックスは可変Mであり、フラグはvolatileポインターRです。

  1. スレッド1は、行列積Mを乗算して計算し、RをMを指すようにアトミックに設定します。
  2. スレッド2は、R!= NULLになるまで待機してから、Mを係数として使用して別の行列積を計算します。

つまり、Mはメッセージであり、Rは準備完了フラグです。

著者は、Rを揮発性として宣言することで、スレッド1からスレッド2への変更を伝播する際の問題を解決できるが、これが発生したときにMの値がどうなるかについては保証しないと主張しています。そして、への割り当てRM並べ替えることができます。したがって、両方MR揮発性にするか、pthreadなどのライブラリで同期メカニズムを使用する必要があります。

私の質問は、Cで次のことを行う方法です

1)2つのスレッド間で単一のフラグを共有する方法-フラグをアトミックに割り当てる方法。他のスレッドが変更を確認し、他のスレッドの変更をテストすることを確認します。この場合、揮発性物質の使用は合法ですか?または、一部のライブラリは、おそらくメモリバリアを含む、概念的に優れた、またはより高速な方法を提供できますか?

2)Robisonの例を正しく実行する方法、つまり、マトリックスMをあるスレッドから別のスレッドに送信して安全に実行する方法(できればpthreadを使用して移植可能)

4

4 に答える 4

1

「volatile」は、コンパイラがメモリアクセスを最適化しないようにするためのヒントです。つまり、メモリ内の値が最後の(ローカル)書き込み以降変更されていないと想定しないでください。このヒントがないと、コンパイラーは、変数のコピー元であるレジスターの値がまだ有効であると想定する可能性があります。したがって、行列がレジスタ内に保持される可能性はかなり低いですが、一般に、両方の変数は、受信側にとって揮発性であるか、より正確には揮発性である必要があります。

実際のマルチスレッドでは、受信機でのビジーウェイトを回避するために、シグナリングにセマフォなどを使用する方がよいでしょう。

于 2012-02-28T12:41:16.623 に答える
1

x86のようなアーキテクチャでは、ポインタのような適切に配置された(およびサイズ設定された)変数は、デフォルトでアトミックに読み書きされますが、CPUパイプラインでの並べ替えを防ぐために、メモリの読み取り/書き込みをシリアル化する必要があります(使用による)明示的なメモリフェンスまたはバスロック操作の)、およびvolatileコンパイラが生成するコードを並べ替えるのを防ぐための使用。

これを行う最も簡単な方法は、CASを使用することです。ほとんどのCAS組み込み関数は、コンパイラおよびCPUメモリバスレベルで完全なメモリバリアを提供します。MSVCでは、機能を使用できます。BTS Interlock*、BTR、Inc、Dec、Exchange、およびAddはすべてフラグに対して機能し、GCCの場合は__sync_*ベースのバリアントを使用します。

pthread_mutexよりポータブルなオプションについては、またはを使用できますpthread_condC11を使用できる場合は、_Atomicキーワードを調べることもできます。

于 2012-02-28T12:56:55.880 に答える
1

volatileゼロオーダー保証を提供します。コンパイル時(および順序の弱いISAでの実行時)は、と同様_Atomicですmemory_order_relaxed。(変数が十分に小さく、自然にアトミックになるように整列されていると仮定します。

もちろん、 1バイトだけで変更されるため、またはbool以外のものを表示することは不可能です。01

強く順序付けられたx86での実行時に、asmロード/ストアにはacq / rel順序が設定されているため、volatile再順序付けが行われない場合は、そのビルドに対して「安全」です。

マルチスレッドでvolatileを使用するのはいつですか? (絶対に:必要に応じて、memory_order_relaxedでatomicを使用してください。)


「データ準備完了」フラグの場合、実際にはリリース/取得セマンティクスが必要です。 https://preshing.com/20120913/acquire-and-release-semantics/

2つのスレッド間で単一のフラグを共有する方法-それにアトミックに割り当てる方法、他のスレッドが変更を確認し、他のスレッドでの変更をテストすることを確認します。

#include <stdatomic.h>
// shared:
_Atomic bool data_ready = false;
float shared_matrix[N][N];

プロデューサー:

   write_matrix( &shared_matrix );  // loop that fills a buffer
   atomic_store_explicit(&data_ready, true, memory_order_release);
   // data_ready = true  but with only release, not seq_cst for efficiency

消費者の場合:

#include <immintrin.h>   // ifdef __x86__

void consumer() {
   while(!atomic_load_explicit(&data_ready, memory_order_acquire)) {
       _mm_pause();   // for x86 spin loops
   }
   // now safe to read matrix
}
于 2020-01-29T00:56:08.180 に答える
0

「古典的な」方法は、スレッド1が動的に割り当てられたマトリックスへのポインターをスレッド2が待機している生産者/消費者キューにプッシュすることです。プッシュされると、スレッド1は別のMを割り当てて、必要に応じて作業を開始できます。

全体的なパフォーマンスが大きな行列の操作によって支配されている場合、最適化として揮発性フラグなどをいじるのは時期尚早かもしれません。

于 2012-02-28T13:00:22.370 に答える