16

x86/x86_64 で順次整合性を確保するための 4 つの方法を次に示します。

  1. LOAD(フェンスなし)とSTORE+MFENCE
  2. LOAD(フェンスなし)とLOCK XCHG
  3. MFENCE+LOAD と STORE (フェンスなし)
  4. LOCK XADD(0) と STORE (フェンスなし)

ここに書かれているとおり: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

C/C++11 オペレーション x86 実装

  • Load Seq_Cst: MOV (メモリから)
  • Store Seq Cst: (LOCK) XCHG // 代替: MOV (メモリへ),MFENCE

注: C/C++11 から x86 への代替マッピングがあり、Seq Cst ストアをロック (またはフェンシング) する代わりに、Seq Cst ロードをロック/フェンシングします。

  • Load Seq_Cst: LOCK XADD(0) // 代替: MFENCE,MOV (メモリから)
  • Store Seq Cst: MOV (メモリに)

GCC 4.8.2 (x86_64 の GDB ) は、 C++11-std::memory_order_seq_cstに対して first(1) アプローチ、つまり LOAD (フェンスなし) および STORE+MFENCE を使用します。

std::atomic<int> a;
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
0x4613e8  <+0x0058>         mov    0x38(%rsp),%eax
0x4613ec  <+0x005c>         mov    %eax,0x20(%rsp)
0x4613f0  <+0x0060>         mfence

ご存知のように、MFENCE = LFENCE + SFENCE です。次に、このコードを次のように書き換えます。LOAD(without fence) and STORE+LFENCE+SFENCE

質問:

  1. ここで LOAD の前に LFENCE を使用する必要がなく、STORE の後に LFENCE を使用する必要があるのはなぜですか (LFENCE は LOAD の前にのみ意味があるためです!)。
  2. GCCがアプローチを使用しないのはなぜですか:std::memory_order_seq_cstのLOAD(フェンスなし)およびSTORE+SFENCE?
4

4 に答える 4

6

x86 が行う唯一の並べ替え (通常のメモリ アクセスの場合) は、ストアに続くロードを並べ替える可能性があることです。

SFENCE は、フェンスの前のすべてのストアがフェンスの後のすべてのストアの前に完了することを保証します。LFENCE は、フェンスの前のすべてのロードが、フェンスの後のすべてのロードの前に完了することを保証します。通常のメモリ アクセスの場合、個々の SFENCE または LFENCE 操作の順序保証は、既定で既に提供されています。基本的に、LFENCE と SFENCE 自体は、x86 の弱いメモリ アクセス モードでのみ役立ちます。

LFENCE、SFENCE、または LFENCE + SFENCE のいずれも、ストアの後に続くロードの順序変更を防止しません。MFENCEはそうです。

関連するリファレンスは Intel x86 アーキテクチャ マニュアルです。

于 2013-10-12T11:30:16.497 に答える
5

std::atomic<int>::storeコンパイラ組み込み関数にマップされ__atomic_store_nます。(これと他のアトミック操作の組み込み関数は、メモリ モデル対応のアトミック操作の組み込み関数で説明されています。)_n接尾辞により、型ジェネリックになります。バックエンドは、特定のサイズ (バイト単位) のバリアントを実際に実装します。 intx86 では、AFAIK は常に 32 ビット長であるため、__atomic_store_4. このバージョンの GCC の内部マニュアルには、__atomic_store操作は という名前のマシン記述パターンに対応すると書かれています。4 バイト整数に対応するモードは「SI」です (ここに記載されています)。atomic_store‌modeatomic_storesi" x86 マシンの説明。これによりconfig/i386/sync.md、具体的には次のビットが表示されます。

(define_expand "atomic_store<mode>"
  [(set (match_operand:ATOMIC 0 "memory_operand")
        (unspec:ATOMIC [(match_operand:ATOMIC 1 "register_operand")
                        (match_operand:SI 2 "const_int_operand")]
                       UNSPEC_MOVA))]
  ""
{
  enum memmodel model = (enum memmodel) (INTVAL (operands[2]) & MEMMODEL_MASK);

  if (<MODE>mode == DImode && !TARGET_64BIT)
    {
      /* For DImode on 32-bit, we can use the FPU to perform the store.  */
      /* Note that while we could perform a cmpxchg8b loop, that turns
         out to be significantly larger than this plus a barrier.  */
      emit_insn (gen_atomic_storedi_fpu
                 (operands[0], operands[1],
                  assign_386_stack_local (DImode, SLOT_TEMP)));
    }
  else
    {
      /* For seq-cst stores, when we lack MFENCE, use XCHG.  */
      if (model == MEMMODEL_SEQ_CST && !(TARGET_64BIT || TARGET_SSE2))
        {
          emit_insn (gen_atomic_exchange<mode> (gen_reg_rtx (<MODE>mode),
                                                operands[0], operands[1],
                                                operands[2]));
          DONE;
        }

      /* Otherwise use a store.  */
      emit_insn (gen_atomic_store<mode>_1 (operands[0], operands[1],
                                           operands[2]));
    }
  /* ... followed by an MFENCE, if required.  */
  if (model == MEMMODEL_SEQ_CST)
    emit_insn (gen_mem_thread_fence (operands[2]));
  DONE;
})

あまり詳しく説明しませんが、これの大部分は、アトミック ストア操作の低レベルの " RTL " 中間表現を生成するために呼び出される C 関数本体です。サンプルコードによって呼び出されると、<MODE>mode != DImodemodel == MEMMODEL_SEQ_CST、およびTARGET_SSE2が true になるため、 および が呼び出さgen_atomic_store<mode>_1れますgen_mem_thread_fence。後者の関数は常に を生成しmfenceます。(このファイルには を生成するコードがありますがsfence、明示的にコード化された_mm_sfence(から<xmmintrin.h>) にのみ使用されると思います。)

コメントは、誰かがこの場合 MFENCE が必要であると考えたことを示唆しています。ロード フェンスが不要であると考えているのが間違っている、GCC の最適化バグが見逃されているかのいずれかであると結論付けます。たとえば、コンパイラの使用方法のエラーではありません。

于 2013-09-29T19:16:05.243 に答える
5

SFENCE + LFENCE はStoreLoad バリア (MFENCE) ではないため、質問の前提が正しくありません。(同じユーザーからのこの同じ質問の別のバージョンに関する私の回答も参照してください。なぜSFENCE + LFENCEはMFENCEと同等なのですか?


  • SFENCE は、以前のロードをパス (前に表示) できます。(これは単なる StoreStore バリアです)。
  • LFENCEは以前の店舗を通過できます。(負荷はどちらの方向にも交差できません: LoadLoad バリア)。
  • ロードは SFENCE を渡すことができます (ただし、ストアは LFENCE を渡すことができないため、LoadStore バリアであると同時に LoadLoad バリアでもあります)。

LFENCE+SFENCE には、後でロードするまでストアのバッファリングを停止するものは含まれていません。MFENCEこれを防ぎます。

Preshing のブログ投稿では、StoreLoad バリアがどのように特別であるかを図を使用してより詳細に説明し、MFENCE を使用しない並べ替えを示す実用的なコードの例を示しています。メモリの順序について混乱している人は、そのブログから始めるべきです。

x86 には強力なメモリ モデルがあり、すべての通常のストアにはリリース セマンティクスがあり、すべての通常のロードにはセマンティクスの取得があります。 この投稿には詳細があります。

LFENCE と SFENCEは、弱く順序付けられ、キャッシュをバイパスするload/storesで使用するためにのみ存在します。movnt


これらのリンクが切れた場合に備えて、別の同様の質問に対する私の回答にさらに詳しい情報があります。

于 2015-09-22T02:09:44.887 に答える