「詳解 Rustアトミック操作とロック」を読んだ
詳解 Rustアトミック操作とロック / Mara Bosを読了したので印象に残った点を中心にまとめる。
フィヨルドブートキャンプのDiscord上で@udzuraさんにおすすめいただき、読んだ。
内容の概要
この書籍は低レイヤーの並行プログラミングに関連する知識をRustをベースにして学べるものだ。Rust固有の話が多いが、原理となる知識や考え方は他のプログラミング言語にも活かせるのではないかと思う。
章立てとしては、1章でRustの基本に簡単に触れた後、2章ではアトミック操作の概要に触れる。3章で重要な概念であるメモリオーダリングについての解説が1章まるごとある。4章から6章はスピンロック、チャンネル、Arcの実装を実際のRust標準ライブラリ実装に近い形で組み立てていく。さらに7章以降ではアセンブリなどさらに低レイヤ部分を掘り下げていく。
気になった部分
メモリオーダリングの使い分け
以前の記事で、メモリオーダリングについては自分なりにまとめていた。 ただ、このときは
Ordering::SeqCstなどの「シンプルな」(おそらくここではコードを読んで理解する素朴な実行順序に近いものがシンプルと表現されている)ものをまず使うようにする。その後計測してボトルネックとなるところを最適化していく。という流れが推奨されている。
と Ordering::SeqCst を「とりあえず」使っておけばOK、という結論にとどまっていた。そのあたりの理解の浅さをこの書籍である程度補完できたように思う。
本書によれば
一番わかりやすいメモリオーダリングのように思うかもしれないが、SeqCst オーダリングが実際に必要になることはほとんどない。ほとんどすべての場合において、通常の Acquire/Releaseオーダリングで十分だ。
と言われている。以前の記事でも書いたが、 Ordering::SeqCst はコストが高いので避けられるのなら避けるに越したことはない。本書を読むことで、そのあたりの理解の解像度が上がったように思う。
メモリオーダリングの形式定義
また、本文中に
個々のメモリオーダリングは、それぞれ厳密に形式的に定義されている。
という記述があり、気になった。「先行発生関係(happens-before relationships)」について簡易的にWikipediaの記述を参照すると、
The happened-before relation is formally defined as the least strict partial order on events such that: …
とあり、strict partial order(強半順序関係)、つまり
- 非反射律(Irreflexivity)
- 自分自身とは順序関係がない。つまり自分自身の先行発生関係については成立しない
- 推移律(Transitivity)
- ある操作a, 操作b, 操作cについて先行発生関係が a -> b かつ b -> cとなっているときには a -> c が成り立つ
- 反対称律(Asymmetry)
- ある操作aと操作bについてa -> bという先行発生関係があるならb -> a は成立しない
ということが定義されているようだ。半順序なので順序関係がない場合がある。普段コードを書いていると上から下に実行順序が決まっているように思えるため、この「順序が決まらない」という部分が直感に反し、難しいポイントだと感じた。
ロックの実装
4章ではスピンロックの実装に Guard という概念が登場する。この Guard 型がdropされるときにあわせてロックを開放することで、 Guard が生存している限りは安全に値を参照できることを保証するものだ。
これは実際の Mutex が MutexGuard を返すことの仕組みをあらためて実感できたように思う。
その他
OSプリミティブを使ったロック実装の最適化なども後半で出てくる。一通りは読んで、理解したつもりになった。ただ、実際に実務で使えるレベルかといわれるとそうでもないところも多いので、折に触れて読み返したい。
x86-64のアセンブリだとRelaxedとAcquire/Releaseが同じになる、というのは豆知識として面白かった。
まとめ
総じて、今までより一段深いところまで理解が進んだように感じるよい書籍だった。すすめてくださったudzuraさんに感謝。