10

ほとんどの条件下で、ローカル読み取りで問題なく動作するロックフリー コードをいくつか書きました。

メモリ読み取りでのローカル スピンは、スピン読み取りの前に常にメモリ バリアを挿入する必要があることを意味しますか?

(これを検証するために、特定の非常に特定の条件下で、リーダーが書き込まれた値をまったく見ないという結果になるリーダー/ライターの組み合わせを作成することに成功しました。ループ内で実行されるため、矢印はその方向を指していますが、メモリバリアを通過するコストについては完全にはわかりません.)

キャッシュのストア バッファにフラッシュするものが何もない場合、メモリ バリアを介してスピンするコストはいくらですか? つまり、すべてのプロセスが (C で) 行っているのは、

while ( 1 ) {
    __sync_synchronize();
    v = value;
    if ( v != 0 ) {
        ... something ...
    }
}

それは無料であり、メモリバスにトラフィックを邪魔しないと仮定するのは正しいですか?

別の言い方をすれば、次のように質問することです: メモリ バリアは、ストア バッファーをフラッシュし、無効化を適用し、コンパイラーがその場所全体で読み取り/書き込みを並べ替えないようにする以上のことを行いますか?


逆アセンブルすると、__sync_synchronize() は次のように変換されます。

lock orl

Intelのマニュアルから(同様に、初心者にとっては漠然としています):

Volume 3A: System Programming Guide, Part 1 --   8.1.2

Bus Locking

Intel 64 and IA-32 processors provide a LOCK# signal that
is asserted automatically during certain critical memory
operations to lock the system bus or equivalent link.
While this output signal is asserted, requests from other
processors or bus agents for control of the bus are
blocked.

[...]

For the P6 and more recent processor families, if the
memory area being accessed is cached internally in the
processor, the LOCK# signal is generally not asserted;
instead, locking is only applied to the processor’s caches
(see Section 8.1.4, “Effects of a LOCK Operation on
Internal Processor Caches”).

私の翻訳: 「ロックと言うと、これはコストがかかりますが、必要な場合にのみ行っています。」


@BlankXavier:

ライターがストア バッファーから書き込みを明示的にプッシュアウトせず、それがその CPU で実行されている唯一のプロセスである場合、リーダーはライターの効果を確認できない可能性があることをテストしました (テスト プログラムで再現できますが、上で述べたように、特定のコンパイルオプションと専用のコア割り当てを使用した特定のテストでのみ発生します-私のアルゴリズムは正常に機能します。将来の問題)。

デフォルトでは、単純な書き込みはWB書き込み(ライトバック)であると思います。つまり、すぐにはフラッシュされませんが、読み取りは最新の値になります(「ストア転送」と呼ばれると思います)。そこで、ライタには CAS 命令を使用します。Intelのマニュアルで、これらすべての異なるタイプの書き込み実装(UC、WC、WT、WB、WP)、Intel vol 3A chap 11-10を発見し、まだそれらについて学んでいます。

私の不確実性は読者の側にあります.McKenneyの論文から、バスからキャッシュへの受信無効化のキューである無効化キューもあることがわかりました。この部分がどのように機能するかわかりません。特に、通常の読み取りをループする(つまり、ロックされていない、バリアなしで、揮発性を使用して、コンパイル後にオプティマイザーが読み取りを確実に残すようにする)と、毎回「無効化キュー」にチェックインすることを暗示しているようです。 (そのようなものが存在する場合)。単純な読み取りでは不十分な場合 (つまり、キューに入れられた無効化が保留されている間はまだ有効に見える古いキャッシュ ラインを読み取ることができます (これは私にも少し矛盾しているように聞こえますが、無効化キューはどのように機能するのでしょうか?))、アトミック読み取りは次のようになります。私の質問は次のとおりです。この場合、これはバスに影響を与えますか? (多分無いと思います。)

私はまだ Intel のマニュアルを読んでいますが、ストア フォワーディングについては素晴らしい議論が見られますが、無効化キューについては適切な議論が見つかりませんでした。C コードを ASM に変換して実験することにしました。これがどのように機能するかを実際に理解するには、これが最善の方法だと思います。

4

3 に答える 3

4

「xchg reg,[mem]」命令は、コアの LOCK ピンを介してロックの意図を通知します。この信号は、他のコアやキャッシュを通り過ぎてバス マスター バス (PCI バリアントなど) に到達し、バス マスタリング バス (PCI バリアントなど) に到達します。これにより、実行中の処理が終了し、最終的に LOCKA (確認応答) ピンが CPU に xchg が完了する可能性があることを通知します。その後、LOCK 信号が遮断されます。このシーケンスは、完了するまでに長い時間 (数百の CPU サイクルまたはそれ以上) を要する場合があります。その後、他のコアの適切なキャッシュ ラインが無効になり、既知の状態、つまりコア間で同期された状態になります。

アトミックロックを実装するために必要なのは xchg 命令だけです。ロック自体が成功した場合は、アクセスを制御するためにロックを定義したリソースにアクセスできます。そのようなリソースは、メモリ領域、ファイル、デバイス、関数、またはあなたが持っているものである可能性があります. それでも、ロックされているときにこのリソースを使用し、ロックされていないときに使用しないコードを作成するのは、常にプログラマーの責任です。通常、ロックが成功した後のコード シーケンスはできるだけ短くして、他のコードがリソースへのアクセスを取得するのをできるだけ妨げないようにする必要があります。

ロックが成功しなかった場合は、新しい xchg を発行して再試行する必要があることに注意してください。

「ロックフリー」は魅力的なコンセプトですが、共有リソースを排除する必要があります。アプリケーションに 2 つ以上のコアが同時にある場合、共通メモリ アドレスから読み書きする「ロック フリー」はオプションではありません。

于 2011-08-17T09:25:26.763 に答える
2

質問の意味がよくわからないかもしれませんが...

スピンしている場合、問題の 1 つはコンパイラがスピンを最適化することです。揮発性はこれを解決します。

メモリ バリアがある場合は、リーダーではなく、ライターによってスピン ロックに対して発行されます。ライターは実際にこれを使用する必要はありません-そうすることで、書き込みがすぐにプッシュされることが保証されますが、とにかくすぐに消えてしまいます。

バリアは、そのコードを実行するスレッドがその場所全体で再注文することを防ぎます。これは、その他のコストです。

于 2011-07-26T03:39:47.827 に答える
0

バリアは通常、メモリ アクセスのセットを順序付けるために使用されるため、コードの他の場所にもバリアが必要になる可能性が非常に高いことに注意してください。たとえば、バリア要件が代わりに次のようになることは珍しくありません。

while ( 1 ) {

    v = pShared->value;
    __acquire_barrier() ;

    if ( v != 0 ) {
        foo( pShared->something ) ;
    }
}

このバリアは、ロードが完了pShared->somethingする前にif ブロック (つまり: ) 内のロードとストアが実行されるのを防ぎます。value典型的な例は、次のように、ストアを使用しv != 0て、他のメモリ ( pShared->something) が他の予想される状態にあることを示す「プロデューサー」がある場合です。

pShared->something = 1 ;  // was 0
__release_barrier() ;
pShared->value = 1 ;  // was 0

この典型的なプロデューサとコンシューマのシナリオでは、ほとんどの場合、ペアのバリアが必要になります。1 つは補助メモリが可視であることを示すストア用であり、もう 1 つは値ストアの効果が何かストアの前に表示されないようにするためです。コンシューマー向け (値のロードが完了する前に何かのロードが開始されないようにするため)。

これらの障壁もプラットフォーム固有です。たとえば、powerpc (xlC コンパイラを使用) では、消費者と生産者にそれぞれ__isync()andを使用します。__lwsync()必要なバリアは、 のストアとロードに使用するメカニズムによっても異なりますvalue。intel LOCK(おそらく暗黙的) になるアトミック組み込み関数を使用した場合、これにより暗黙的なバリアが導入されるため、何も必要ない場合があります。さらに、コンパイラーに必要なことをさせるために、volatile を賢明に使用する (またはできれば、内部でそうするアトミック実装を使用する) 必要もあります。

于 2013-07-31T19:12:19.953 に答える