Software Transactional Memo

STM関係のことをメモっていこうと思います。

書評:並行プログラミング入門

サムネ画像


TL;DR 並行処理を実装する人のこれからのスタンダードになる一冊。買い。 

買ったら思いの外早く届いたのでパラパラと読み始めたら一気に読み終えてしまった。

総評

敢えて雑な喩え方をするなら The Art of Multiprocessor Programming (通称TAoMP本) の内容を薄めてRustやアセンブラや計算モデルを足したような本だった。

日本語の書籍としてはかなり珍しくWait-Free, Lock-Free, Obstruction-Freeの違いなどを適切に論じており、TTAS Lock, MCS Lock, TL2といった日本語では希少な情報が書かれているレアな本である。これらに付いて論じている日本語の本は知る限り (TAoMP本と昔僕が書いたプログラミングの魔導書の一章を除いて)無い。

僕はRustはTour of Rustを大昔に一周したぐらいでロクに知らなかったがunsafeや時にはアセンブラを駆使してロックやファイバー(グリーンスレッド)やFutureを実装する過程をこの品質で懇切丁寧に説明している日本語の文書はネット上を探してもなかなかないはずである。Rustが得意な人には冗長な説明が続く箇所も序盤にはあるが、中盤からはRustでマルチスレッドのコードを丁寧に書くお手本が豊富に載っていて次に時間を見つけて写経してみようかと思っている。epollとC++でしばらく格闘しまくって出来栄えにイマイチ満足行ってないなんて経験を持ってる人は世界中にいっぱい居ると思うが、Rustを使ってそこに改めて言語のサポート込みで挑戦するのは楽しそうである(本書の中にはRustでepollを使うコードももちろん解説されている)。

また僕はあまり興味がなくて追いかけていなかった線形論理やπ計算やアクターモデルなどの話も盛り込んであり、そちら方向で興味がある人の知的好奇心も満たしてくれそうである(あんまり詳しい事まで追ってなさそうだが)。

本書の中ではいろんな並行処理技法が紹介されているが、「おわりに」の章を読むとわかるように筆者は基本的にasync/awaitでカタがつく仕事はそれでやれと言っておりその経験量とバランス感覚に好感が持てる。その上でRustを使ったソリッドな実装の知識を蓄えていけるのはこれからの並行処理プログラミングの展望において明らかに大きな前進であると思う。人類はJavaを使っても(思想として)COBOLプログラムを書けるのと同様に、任意のプログラム言語でRustを書けるのだから、Rustでの記述が手元にあることの安心感は計り知れない。

この本の良いところ

浅く広く並行プログラミングの界隈の技法が紹介されており、これらを軒並み抑えておけば並行プログラムを書く際に「学ぶべき事が無限にあるように感じられる」みたいな状況に陥ることはない。またRustのコードは書くのは難しいが(ライフタイムだの借用だのを気にしなければ)基本的に読みやすいので並行処理のドキュメントとしても優れている。

この本を書いている人が本に書いた内容の数倍の知識を持った上で書いている事をヒシヒシと感じられる内容であり、細かいレベルを除いて説明のアラは見つからなかった。

Rustのコードは実はほとんど読み飛ばしてもおそらく並行処理の世界の大まかなマップは脳内に完成するので、かなりコストパフォーマンスが良い本だと思う。また比較的最近の研究に対しても論文へのポインタがおいてあるのが発展話題を追いかける為に便利。

細かい事

僕は並行処理の話になると整理できていない話したい事がありすぎて要領を得ない事になってしまうので箇条書きで書く。興味のある人はオタクの早口だと思って聞き流して欲しい。

 

- 学術的なレベルでは、計算モデルの話を除いてThe Art of Multiprocessor Programmingに書いてある内容を超えていない。その初版が2008年ごろなので13年以上前の古典的な技術ばかりである。つまりこのトピックの専門家にとってはRustでの記法以外に真新しい情報はそんなにない。入門と銘打っているのだから本としては完全に正しい。

- せっかく実装があるのにベンチマークがほとんど無いのでそのように書いてどのような恩恵があるのかピンと来ない。

- ロックについて言うと自分ならまずはスピンロックは使うなと思っている、手書きのスピンロックを持ち出すのはpthread_mutexがボトルネックになったとプロファイル結果が出てからでも遅くない。僕の記憶が確かならMCSロックがスケールするのは通常のメモリアクセス命令が使えるからではなく、ロック獲得待ちスレッドがローカルの変数に対してスピンできるのでキャッシュコヒーレントトラフィックを大幅に削減できるからだったはずである、極論するとローカルのlockedフラグをAcquireフェンス付きでReadしまくってもスケーラビリティは確保できる、もっと言うとnextフィールドとlockedフィールドが別のキャッシュラインになるように細工をすることでlockedフィールドのキャッシュラインステートをEやSに落とせるはずだがこれはオリジナルの論文にはない拡張だったか未確認。まだまだロックについて語りだすとfutexのようなOS寄りの話から、コホートロックなどのNUMA向け、バックオフ待機戦略やそもそもキューのロックを使うべきでないケースなど無限に話題が吹き出して話が終わらない可能性があるが、ここの領域はまさに並行プログラミングの醍醐味で、直感と実測が一致しないことが多い面白い部分でもっとページを割く価値があると思う。

- STMの研究はTL2が確かに一つの大きなマイルストーンになっているが実際この本に書かれたような方法でSTMを使う必要に駆られる事はかなり稀であり「食事をする哲学者問題」より面白くて実用性のある問題(トランザクション等)で有用性を示した方が面白いと思った、というのもSiloなどの近年のトランザクションアルゴリズムは多少なりTL2の影響を受けているからである。STMの実行に関して言うとOpacityの実現はTL2の偉業の一つであり、そこに至るまでに多くの紆余曲折があったのでそういう言及が会ったほうが歴史を感じれて良かったかも知れない(別にこれは書かなくていい)。またTL2の改良策はさらっと紹介されているが「いろいろある」で片付けるには惜しい改良がいっぱいあってそこの解説が為されたらもっと良かったと思う、僕はSkySTMとかTLRWとかが好き。STMの戦略は並行制御・バージョニング・衝突検知に広大なデザイン空間があり、その特定の組み合わせがイノベーティブであったという順序でTL2が解説されていたら僕はスタンディングオベーションをしていたと思う、単に僕の性癖に刺さるだけの話であるがこの近辺は並行処理の面白い部分が詰まっている。

- ロックフリーデータ構造もロックフリースタックのみが記述されており、これのRust実装は確かに興味深いのだがロックフリー成分を求めて本を手に取った側からすると物足りない。ロックフリースタックにおいてABA問題が発生する真の理由は解放済みメモリを使いまわしている事であり、GCが適切に走ればここに問題は起きない。なので「LL/SCを用いればLock-Free StackのABA問題が解決できる」という説明順序にはせず、EBRで良いので簡単な局所GCを実装してABA問題を解決して欲しかった、またタグを使ったABA避けはポインタ内でのタグが循環すると依然としてABA問題は(特にポインタに埋め込む方法だと)確率的に起きうるのでこれは解決ではなく緩和である。実はLL/SCを使うとそもそも絶対にロックフリーにはならないので、LL/SCを用いたロックフリースタックは存在し得ない。なぜならLoadLinked命令で付けられた目印は書き換えが起きなくてもキャッシュが揮発したり同じキャッシュラインで全く別件の書き込みがあったりすると消えるので論理衝突がなくてもStoreConditionalが失敗することはありえて、スケジューラがちょっと悪さをすれば進行保証は守れなくなるのである。

- そういえばキャッシュコヒーレンシプロトコルについて知っておくとこのエリアでの並行処理の肌感覚はだいぶわかると思ったけどそれらに関する記述はほとんどなかった。

- 線形論理・π計算などはロクに知らなかったのでざっと目を通して勉強になったが未だにこれを通して何をしたいのかわからなかった、僕が異様に実用性に寄った考え方をしているせいだが、定義が記述されていてもご利益を感じる所まで解説されていないと自分は満足しないようである(これは僕の頭が足りなくてご利益を理解できていなかった可能性もある)。アクターモデルで処理を記述する事がasync/awaitより優れている気もあんまりしないあたり自分でも腹落ちしていないんだと思う。

 

ちょっとだけ箇条書きを書き並べるつもりがなんか本文よりやたら長くなってしまった事に自分でやや引きながらもこれは総じて良い本で、TAoMP本は詳しくて正確だけど非常に難解なのに対しこちらは平易で軽妙なのでRustに興味ない人も是非読んでみると良いと思った。

 

更新

枯れた→古典的な に変更。古い並行アルゴリズムの事を悪く言いたくなかったので言葉選びに気を使ったらミスってしまった。