Upgrade to Pro — share decks privately, control downloads, hide ads and more …

いかにして命令の入れ替わりについて心配するのをやめ、メモリモデルを愛するようになったか

 いかにして命令の入れ替わりについて心配するのをやめ、メモリモデルを愛するようになったか

kernel/vm探検隊 17回 @nullpo_head

Takaya Saeki

August 10, 2024
Tweet

More Decks by Takaya Saeki

Other Decks in Technology

Transcript

  1. クイズ 16 このプログラムでありうる(Ry, Rx)は? 1. (1, 0), (0, 1), (1,1)

    だけ 2. x86では (1, 0), (0, 1), (1,1) 、 armでは加えて (0,0)も 3. x86/armともに (1, 0), (0, 1), (1,1), (0,0) すべて
  2. クイズ 17 このプログラムでありうる(Ry, Rx)は? 1. (1, 0), (0, 1), (1,1)

    だけ 2. x86では (1, 0), (0, 1), (1,1) 、 armでは加えて (0,0)も 3. x86/armともに (1, 0), (0, 1), (1,1), (0,0) すべて
  3. • CPUは「実行結果を変えない限り」 なんでも最適化をやる • 古典的なのがwrite-backキャッシュ • マルチコアだと 「実行結果を変えない限り」 が成り立たなくなりうる •

    Out-of-orderも含むがそれに限らない 21 CPUの最適化 Execution Order のキャッシュが任意のタイ ミングでメモリに反映される 一旦write buffer にキャッシュだ けすれば、書き 込み完了を待た なくていいしあ とでキャッシュ から読める
  4. 22 マルチコアで実行結果が変わる最適化 Execution Order の キ ャ ッ シ ュ

    が メモリに反映される の キ ャ ッ シ ュ が メモリに反映される
  5. 23 マルチコアで実行結果が変わる最適化 Execution Order の キ ャ ッ シ ュ

    が メモリに反映される の キ ャ ッ シ ュ が メモリに反映される
  6. 24 マルチコアで実行結果が変わる最適化 Execution Order の キ ャ ッ シ ュ

    が メモリに反映される の キ ャ ッ シ ュ が メモリに反映される
  7. 25 マルチコアで実行結果が変わる最適化 Execution Order の キ ャ ッ シ ュ

    が メモリに反映される の キ ャ ッ シ ュ が メモリに反映される
  8. 26 マルチコアで実行結果が変わる最適化 Execution Order の キ ャ ッ シ ュ

    が メモリに反映される の キ ャ ッ シ ュ が メモリに反映される
  9. • Sequential Consistency (逐次実行、理想の世界) • TSO (x86) • Weak (arm)

    • なおrisc-vはTSO実装もweak実装もある 35 主要な3つのハードウェアメモリモデル ペア Sequential Consistency TSO (x86) Weak (arm) load -> load No No Yes store -> store No No Yes load -> store No No Yes store -> load No Yes Yes
  10. 36 最初の例 • Store -> load の順だから、x86 / armの両方で追い越しが起こる ペア

    TSO (x86) Weak (arm) load -> load No Yes store -> store No Yes load -> store No Yes store -> load Yes Yes
  11. Store-> load -> store-> store -> load • Loadは前二つのstoreの片方、または両方追い越しうる •

    しかし先頭のstoreは追い越さない • そのためにはload->loadの追い越しが必要だから 37 複数命令の追い越しの考え方 pair TSO load -> load No store -> store No load -> store No store -> load Yes
  12. • X86のmfenceやarmのdmbなど • 本来のメモリモデルなら許されている場所の追い越しを禁止すると考えていい • 局所的にWeak -> TSO -> SC

    の昇格を行える • 細かい命令はリファレンスを参照してね (lock, xchg, CPUID, .. も追い越し禁止をするよ) 41 メモリバリア命令 – フェンス mov [x] <- 1 mfence mov rax <- [y] ペア SC TSO (s86) load -> load No No store -> store No No load -> store No No store -> load No Yes mfence
  13. • コンパイラも自由に命令を並べ替えてコンパイルすることがある • X86 (TSO)のCPUであろうと、もとのプログラムからはload->loadの入れ替えさえ起こる 45 コンパイル時追い越し void func(int *p1)

    { int *p2 = heavy_calc(p1); int i1 = *p2; int i2 = *p1; func: reg_for_i2 <- load(reg_for_p1) call heavy_calc(reg_for_p1) reg_for_p2 <- reg_for_return reg_for_i1 <- load(reg_for_p2)
  14. • “atomic”型の操作にメモリモデルを指定させるAPIで追い越しとアトミック性の両方を制御 1. seq_cst 2. relaxed 3. acquire 4. release

    5. acq_rel (for atomic read-write) 6. consume (非推奨忘れていい) • informalには結局のところ、他のatomic/non-atomic変数との間の追い越しの有無で理解していい • なお、追い越しの結果、複数スレッドが同時にnon atomic変数に読み書きするようなコードは非合法 47 C++メモリモデル atomic_bool.store(true, std::memory_order::seq_cst)
  15. • ハードウェアメモリモデルのWeakに対応 • すべての追い越しを許す • 他のseq_cstやacquire,releaseに邪魔されない限り 49 releaxed ペア 追い越す?

    下行以外の relaxed/non-atomic load/store relaxed/non-atomic load/store Yes seq_cst load/store relaxed load/store No relaxed load/store → release store No acquire load → relaxed load/store No
  16. • C++ではこれを使いこないしたいわけです! • 一旦formalなC++のセマンティクスをみてみよう An atomic operation A that performs

    a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M 10 50 acquire, release _人人人人人人人人人人人_ > なかなか意味不明! <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ^
  17. An atomic operation A that performs a release operation on

    an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M 10 “synchronizes”: ざっくり、 • あるスレッドのatomic変数への書き込みで、 同じatomic変数への読み込みで別のスレッドが入れた値を観測したら、 • 読み込み以降のメモリ操作はすべて書き込み以前のメモリ操作の結果を観測できる • 逆にそれ以外は保証しない まだ難しい • ちなみに基本的にペアで使うが、そろわなくても害はない 51 acquire, release
  18. An atomic operation A that performs a release operation on

    an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M 10 “synchronizes”: • acquire loadで、release storeで別のスレッドが入れた値を観測したら、acquire以降 のメモリ操作はすべてrelease以前のメモリ操作の結果を観測できる → sd.rlは以前のメモリ命令を追い越さないことでこれを実装 • 逆にそれ以外は保証しない → releaseより後のメモリ操作はreleaseを追い越しているかもしれないし、 acquire側は先のメモリ命令を追い越しているかもしれない 60 acquire, release
  19. • informalには、結局以下の表の追い越しが起こるということ 61 acquire, release ペア 追い越す? Any atomic/non-atomic load/store

    → release store No acquire load → any atomic/non-atomic load/store No release store / relaxed or non-atomic load/store → acquire load / relaxed or non-atomic load/store Yes release store → seq_cst load/store No seq_cst load/store → acquire load No
  20. • seq_cst, acquire/release, relaxedは、だいたいSC, TSO, Weakに対応して追い越し関係を制御する • 許可された追い越しが起きても、non-atomic変数に複数スレッドが 同時に読み書きしないようモデルを選択する ⇒

    synchronizeの結果non-atomic変数のアクセス順序がきまるようにする 62 C++メモリモデルまとめ ペア 追い越す? seq_cst load/store → any atomic/non-atomic variable No any atomic/non-atomic variable → seq_cst load/store No any atomic/non-atomic load/store → release store No acquire load → any atomic/non-atomic load/store No release store / relaxed or non-atomic load/store → acquire load / relaxed or non-atomic load/store Yes
  21. メモリモデルを通して命令の追い越しを理解し、必要なバリアやmemory_order::*を使おう まとめ 63 ペア Sequential Consistency TSO (x86) Weak (arm)

    load -> load No No Yes store -> store No No Yes load -> store No No Yes store -> load No Yes Yes ペア 追い越す? seq_cst load/store → any atomic/non-atomic variable No any atomic/non-atomic variable → seq_cst load/store No any atomic/non-atomic load/store → release store No acquire load → any atomic/non-atomic load/store No release store / relaxed or non-atomic load/store → acquire load / relaxed or non-atomic load/store Yes