2017-02-02 6 views
1

デバイスレジスタの内容を複数のスレッドが読み取る変数にコピーしたいとします。これを行う一般的な方法がありますか?ここでこれを行う2つの方法の例です:あるスレッドのメモリストアを他のスレッドで「即座に」表示させるにはどうすればよいですか?

#include <atomic> 

volatile int * const Device_reg_ptr = reinterpret_cast<int *>(0x666); 

// This variable is read by multiple threads. 
std::atomic<int> device_reg_copy; 

// ... 

// Method 1 
const_cast<volatile std::atomic<int> &>(device_reg_copy) 
    .store(*Device_reg_ptr, std::memory_order_relaxed); 

// Method 2 
device_reg_copy.store(*Device_reg_ptr, std::memory_order_relaxed); 
std::atomic_thread_fence(std::memory_order_release); 

は、より一般的には、可能なプログラム全体の最適化に直面して、どのように1は、正しくメモリのレイテンシは他のスレッドに見えている1つのスレッドで書き込み制御していますか?

EDITは:組込みシステムに

  • コードがCPU上で実行されている:あなたの答えでは、次のシナリオを検討してください。
  • CPU上で1つのアプリケーションが実行されています。
  • アプリケーションのスレッド数は、CPUのプロセッサコア数よりもはるかに少なくなっています。
  • 各コアには大量のレジスタがあります。
  • アプリケーションは、プログラムの最適化が実行可能ファイルを構築するときに十分に使用されるほど十分に小さいです。

あるスレッドのストアがほかのスレッドから無期限に見えないようにするにはどうすればよいですか?

+1

'std :: atomic'を使用すると、可視性の問題(つまり、データ競合やダーティリードがない)がすでに解決されています。しかし、ロード/ストア操作のメモリの順序はまだ懸念されるかもしれませんが、適切な 'std :: memory_order'を' std :: atomic :: load' respに渡すことで簡単に制御できます。 'std :: atomic :: store'を参照してください。 「揮発性」または追加のメモリフェンスの必要はありません。 –

+0

原子負荷を最適化して、公称負荷と実行時実行負荷命令の間に1-1の対応がないようにすることができます。揮発性はこの1-1の対応を保証する。 – WaltK

+1

_ * _の前に_volatile_を置くと、ポインタはvolatileでなくレジスタになります。 @WaitK、右; – WaltK

答えて

2

アトミックな方法でdevice_reg_copyの値を更新する場合は、device_reg_copy.store(*Device_reg_ptr, std::memory_order_relaxed);で十分です。

原子変数にvolatileを適用する必要はありませんが、それは不要です。

std::memory_order_relaxedストアは、同期オーバーヘッドの量が最も少なくなると想定されます。 x86では単なる平凡なmov命令です。もし先行店舗の効果がdevice_reg_copyの新しい値と共に、他のスレッドに見えるようになることを、このような方法でそれを更新したい場合

しかし、その後std::memory_order_releaseストア、すなわちdevice_reg_copy.store(*Device_reg_ptr, std::memory_order_release);を使用します。この場合、読者はdevice_reg_copystd::memory_order_acquireとしてロードする必要があります。再度、x86 std::memory_order_releaseストアでは、平文はmovです。

最も高価なstd::memory_order_seq_cstストアを使用している場合、x86ではメモリバリアが挿入されます。

これは、x86メモリモデルがC++ 11にはあまりにも強すぎると言っているからです。普通のmov命令は、ストアではstd::memory_order_release、ロードではstd::memory_order_acquireです。 が緩和ストアまたはx86にロードされていません。

CPU Cache Flushing Fallacyの記事を十分にお勧めできません。

0

C++標準は

29.3.12 実装妥当な時間内原子ロード原子ストアが見えるようにすべきである..他のスレッドに可視原子格納することについてかなり曖昧です。

詳細については、「合理的」の定義はありません。すぐにする必要はありません。

特定のメモリ順序を強制的にスタンドアロンのフェンスを使用すると、メモリフェンスを使用するに関してあなたの期待が何であるかをあなたがアトミック操作上のものを指定することができますので、必要ではありませんが、質問があり、 ...
フェンス(スレッド間で)メモリ操作の順序を強制するように設計されていますが、タイムリーにの方法での可視性を保証するものではありません。 最も高いメモリ順序(つまり、seq_cst)を持つアトミック変数に値を格納できますが、別のスレッドがstore()より後でload()を実行しても、まだキャッシュから古い値を取得している可能性があります意外にも)それはでないを侵害するの前に起こるの関係。 より強いフェンスを使用するかもしれないは違いを作ります。タイミングと可視性は保証されていますが、保証はありません。

表示の視認性が重要な場合は、読み込み - 変更 - 書き込み(RMW)操作を使用して値を読み込むことを検討します。 これらは、原子的に(すなわち、単一の呼び出しで)読み取りおよび変更し、最新の値で動作することが保証されている追加プロパティを持つアトミック操作です。 しかし、これらの呼び出しはローカルキャッシュよりも少し先に到達する必要があるため、これらの呼び出しは実行するのがよりコストがかかる傾向があります。

Maxim Egorushkinから指摘されているように、デフォルト(seq_cst)よりも弱いメモリ順序を使用できるかどうかは、スレッド間で他のメモリ操作を同期させる必要があるかどうかによって決まります。 これはあなたの質問からはっきりしませんが、通常、デフォルト(逐次整合性)を使用することは安全と考えられます。
パフォーマンスに問題がある場合は、異常に弱いプラットフォーム上にあり、あなたがスレッド間のデータ同期が必要な場合、あなたが取得/解放のセマンティクスを使用して検討することもできた場合:スレッド2が書き込まれた値を見れば

// thread 1 
device_reg_copy.store(*Device_reg_ptr, std::memory_order_release); 


// thread 2 
device_reg_copy.fetch_add(0, std::memory_order_acquire); 

スレッド1では、スレッド1のストアの前のメモリ操作がスレッド2のロード後に見えることが保証されます。 アクワイア/リリース操作はペアを形成し、ストアとロードの実行時関係に基づいて同期します。つまり、スレッド2がスレッド1によって格納された値を参照しない場合、 には順序保証はありません。

原子変数が他のデータに依存しない場合は、std::memory_order_relaxedを使用できます。ストアの順序付けは、常に1つのアトミック変数に対して保証されます。

std::atomicとのスレッド間通信には、他に言及したように、volatileは必要ありません。

関連する問題