24

のような配列を考えてみましょうatomic<int32_t> shared_array[]。SIMD をベクトル化したい場合はどうしますfor(...) sum += shared_array[i].load(memory_order_relaxed)か?. または、最初の非ゼロ要素の配列を検索するか、その範囲をゼロにしますか? おそらくまれですが、要素内でのティアリングは許可されていませんが、要素間の並べ替えは問題ないというユースケースを検討してください。 (おそらく、CAS の候補を見つけるための検索)。

現在のハードウェアでは、ティアリングは最悪の場合でも 8B 境界でのみ発生するため、x86 で整列されたベクターのロード/ストアは、操作で SIMD に使用するのに実際には安全であると思います(それが、自然に整列された 8B アクセスをアトミック1にするためです)。残念ながら、インテルのマニュアルには次のようにしか書かれていません。mo_relaxed

「クワッドワードより大きいデータにアクセスする x87 命令または SSE 命令は、複数のメモリ アクセスを使用して実装される可能性があります。」

これらのコンポーネント アクセスが自然に整列されている、重複していないなどの保証はありません。(おもしろい事実: Agner Fog によるとfld m80、Haswell で 2 つのロード uops と 2 つの ALU uops で行われた x87 10 バイトのロード、おそらく qword + word。)

現在の x86 マニュアルが将来のすべての x86 CPU で動作すると言っているような将来性のある方法でベクトル化したい場合は、 / を使用して 8B チャンクでロード/ストアできmovqますmovhps

または、256bvpmaskmovdをすべて true の maskで使用することもできます。これは、マニュアルの操作セクションで、 のように複数の個別の 32 ビット ロードに関して定義されているためLoad_32(mem + 4)です。それは、各要素が個別の 32 ビット アクセスとして機能し、その要素内の原子性が保証されるということですか?

(実際のハードウェアでは、Haswell では 1 回のロードと 2 つの port5 uops、または Ryzen では 1 つまたは 2 つのロード + ALU uops (128 / 256) です。 (ただし、マイクロコードの支援が必要な場合は IDK) とにかく、これは少なくともvmovdqaHaswell の通常のロードと同じくらいアトミックであることを示していますが、16B の x86 Deathstation 9000 については何もわかりません。 / 32B ベクトル アクセスは 1 バイト アクセスに分割されるため、各要素内でティアリングが発生する可能性があります。

実際には、実際の x86 CPU で整列されたベクトルのロード/ストアの 16、32、または 64 ビット要素内でティアリングが見られないと想定するのは安全だと思います。自然にアラインされた 64 ビットのスカラー ストアをアトミックに保つ必要がありますが、マニュアルの保証が実際にどこまで進んでいるかを知ることは興味深いことです。)


ギャザー (AVX2、AVX512) / スキャッター (AVX512)

のような命令vpgatherddは、明らかに複数の個別の 32b または 64b アクセスで構成されています。AVX2 フォームは、複数を行うと文書化FETCH_32BITS(DATA_ADDR);されているため、おそらくこれは通常の原子性保証によってカバーされ、境界を越えない場合、各要素は原子的に収集されます。

AVX512 ギャザーは、Intel の PDF insn ref マニュアル
DEST[i+31:i] <- MEM[BASE_ADDR + SignExtend(VINDEX[i+31:i]) * SCALE + DISP]), 1) 、要素ごとに個別に記載されています。(順序付け: 要素は任意の順序で収集できますが、フォールトは右から左の順序で配信する必要があります。他の命令によるメモリの順序付けは、Intel-64 メモリ順序付けモデルに従います。)

AVX512 スキャッタ、同じ方法で文書化されています (前のリンクの 1802 ページ)。原子性については言及されていませんが、いくつかの興味深いコーナーケースをカバーしています。

  • 2 つ以上の宛先インデックスが完全に重複する場合、「前の」書き込みはスキップされる可能性があります。

  • 要素は任意の順序で分散できますが、障害は右から左の順序で配信する必要があります

  • この命令がそれ自体を上書きしてからフォルトが発生した場合、(前述のように) フォルトが配信される前に要素のサブセットのみが完了する可能性があります。フォールト ハンドラが完了してこの命令を再実行しようとすると、新しい命令が実行され、スキャッタは完了しません。

  • 重複するベクトル インデックスへの書き込みのみが、相互に順序付けられることが保証されます (ソース レジスタの LSB から MSB へ)。これには、部分的に重複するベクトル インデックスも含まれることに注意してください。重複していない書き込みは、任意の順序で発生する可能性があります。他の命令でのメモリの順序付けは、Intel-64 メモリ順序付けモデルに従います。これは、同じ物理アドレスの場所にマップされる重複しないインデックスを考慮していないことに注意してください。

(つまり、同じ物理ページが 2 つの異なる仮想アドレスで仮想メモリにマップされているためです。そのため、オーバーラップ検出は、後で再チェックすることなく、アドレス変換の前に (または並行して) 行うことができます。)

最後の 2 つを含めたのは、それらが興味深い特殊なケースであり、考えもしなかったからです。自己変更のケースは面白いですが、同じ問題があると思います(進行状況を追跡するためrep stosdに使用して中断することもできます)。rcx

アトミック性は Intel-64 メモリ順序付けモデルの一部であると思います。そのため、彼らがそれについて言及し、他に何も言わないという事実は、要素ごとのアクセスがアトミックであることを暗示しているようです。(2 つの隣接する 4B 要素を収集しても、ほぼ確実に単一の 8B アクセスとしてカウントされません。)


x86 マニュアルで要素ごとにアトミックであることが保証されているベクトル ロード/ストア命令はどれですか?

実際のハードウェアでの実験的なテストでは、Skylake CPU ではすべてがアトミックであることがほぼ確実にわかりますが、それはこの質問の目的ではありません。 マニュアルの私の解釈がvmaskmov/vpmaskmovロード、およびギャザー / スキャッターに対して正しいかどうかを尋ねています。

(実際のハードウェアが単純なロードに対して要素単位のアトミックであり続けることを疑う理由がある場合movdqa、それも有用な答えです。)


  1. 脚注: x86 原子性の基本:

Intel および AMD のマニュアルによると、x86 では、自然に整列された 8B 以下のロードおよびストアは、atomic であることが保証されています。実際、キャッシュされたアクセスの場合、8B 境界を越えないアクセスもアトミックです。(Intel P6 以降では、AMD よりも強力な保証を提供します: キャッシュ ライン (64B など) 内でアライメントされていないことは、キャッシュされたアクセスに対してアトミックです)。

16B 以上のベクターのロード/ストアは、アトミックであることが保証されていません。それらは一部の CPU 上にあります (少なくともオブザーバーが他の CPU である場合のキャッシュ アクセスの場合)、L1D キャッシュへの 16B 幅のアトミック アクセスでさえ、アトミックにはなりません。たとえば、AMD K10 Opterons のソケット間の HyperTransport コヒーレンシ プロトコルでは、同じソケット (物理 CPU) 内のスレッドでのテストではティアリングが示されなくても、アラインされた 16B ベクトルの半分の間でティアリングが発生します。

(完全な 16B のアトミック ロードまたはストアが必要な場合は、lock cmpxchg16bgcc が に対して行うようにハックすることができますがstd::atomic<T>、これはパフォーマンスにとってひどいものです。Atomic double float または SSE/AVX vector load/store on x86_64も参照してください。)

4

0 に答える 0