RandomThoughts

RandomThoughts

【書籍】CppConcurrencyInAction

Contents:
  1. 読み始めた雑感 2023-10-28 (土)
    1. 何故threadの話から始めるのが良くないのか?
    2. 3章 sharing dataは思ったよりも良い - 2023-10-29 (日)
    3. 4章 Concurrency TS関連の印象
    4. 5章 memory orderはかなり詳しくて良い
    5. 6章、7章の並列コンテナはなかなか良い
    6. 8章のDesigning concurrent codeは読む価値無し - 2023-11-01 (水)
    7. 9章のthread poolとinterruptible threadはまぁまぁ - 2023-11-04 (土)
    8. この本はここまで計測の話が無い
  2. 10章のC++ 17のconcurrencyの章はあまり読む気が起こらなかった
  3. 11章のテストと計測の話はほとんど読む価値無し
  4. 全体を読んでの感想 - 2023-11-04 (土)

並列プログラム

C++ Concurrency in Action の第二版。 WikiNameとしてはプラス記号は使いたくないのでCppと書いたが。

本家のmanningのサイトで安売りしてたのでこっちで買う。

C++ Concurrency in Action, Second Edition

読み始めた雑感 2023-10-28 (土)

【書籍】ConcurrentProgrammingOnWindowsの並列データ構造系のを読もうとしたら、.NETでC++ではそのままでは実現しづらいコードや、 C++特有の可視性周りの落とし穴がありそうな所などが目について、 やっぱC++の本じゃないとダメだなぁ、と思いこの本を買ってみた。

自分の手持ちでは、The Art of Multiprocessor Programmingが一番しっかりした教科書なんだが、

これもJavaだし、やっぱC++の本が欲しいなぁ、という事で買ってみた。

現在二章でthreadの話をしているのであまり印象が良くない。しかもSTLが基本的には十分だ、みたいな事が書いてあって、 全く同意出来ない(というかそもそも提供しているAPIが良くないと思っている)自分とは相容れないなぁ、という気はする。

ただ最初はthreadから話をするしか無い、という立場も有り得るだろうし、何にせよ自分は並列コンテナ系の話がメインのつもりで読んでいるので、もう少し読み進めてみる予定。

何故threadの話から始めるのが良くないのか?

この辺の話は何度かしている。

基本的にthreadとlockというのはうまく行かない考え方で、thread poolとnon blocking futureしかありえない、という事は大前提にすべきである、と思っている。 これは自分の他にも言っている人は居るというか割とガチで並列やっている人の間ではほぼコンセンサスだろうと思う。(Adobeのadobe standard libraryとfollyがこの辺では足並みが揃っている)

threadとlockは他への依存が無く、何らかのパラダイムを押し付けないという点で最初に教えやすい。 ただこのやり方で並列プログラムが期待通りに動く事はほぼ無い。 教えやすいが使うのが難しい。

並列のプログラムでうまくいっているものは、書き込みに何らかの制約があるアーキテクチャになっている。 例えば最近読んでいるmimallocでは確保をするのはいつもオーナーのスレッドのみなので、 それに排他制御が不要になっている。 このように問題をうまく排他制御が要らないように構成するのがthreadやlockを使うプログラムの根本にあるのだけれど、 それはかなりadvancedな話題で、 単に並列のプリミティブを教えてそれを使った並列コンテナの実装を見ていく、という話題では全くたどり着けない話に思う。

だから最初はnon blocking futureとそれにともなう基本的なプログラム方法を教えて、 その範囲に収まらないより発展的な事をやりたい時に良くあるパターンを教える、 みたいに進んで欲しいのだけれど、その為にはSTLに無くしかもプログラムモデルとしてかなり強い制約を最初に課す non blocking futureとthread poolが必要になってしまうのだよな。

本を書く人としてはthreadやmutex、conditional variableなどのプリミティブを教えてそれの落とし穴を教えてlock freeな並列コンテナの話をする、 という方が断然書きやすいし、他人にもっとも押し付けるものが少ないのだけれど、 現場とは大きく乖離してしまう。

現実的には並列プログラムはすごく厳しい制約の何らかのプログラムモデルを受け入れるしか無いのだけれど、 これにコンセンサスが無くてどれかを選ばなくてはいけないんだよな。 いくつかのうまく行っている例を学んでそれらでいろいろ書く技能を身に着けて、それらを実現する為のlow levelなメカニズムを学ぶ、 という風に進むのが理想なのだろうが、 正直うまくいってるプログラムモデル一つマスターするだけでお腹いっぱいなので複数を学ぶのは非現実的なのでは無かろうか。

3章 sharing dataは思ったよりも良い - 2023-10-29 (日)

3章のsharing dataで、3.3のstaticの話とかはなかなか良い。 標準として関数内staticは呼び出しが一つのスレッドに保証されて、他のスレッドからアクセスする時は初期化が終わっている事が保証されているらしい。 へー、随分と強い制約だね。 なんか昔どこかで聞いた事ある気もするが、覚えていなかったので勉強になった。>Cppに関連メモを残す

こういうC++特有の話は良いね。2章は印象悪かったが3章の後半はだいぶ印象が改善した。

4章 Concurrency TS関連の印象

4章の前半はfutureの話とか関数型とかメッセージパッシングがどうとかのポエムばかりで全く実用性を感じないが、 Concurrency TSの話は勉強になった。

Concurrency TSにはcontinuationを指定出来るfutureがあるとの事で、 へーっと思ってみていたが、executer周りの概念が無いので微妙に使い物にならない。 結局この辺は、future以外の所を実装依存にする余地を残しておくと、必要な要素が入れきれないんだよな。 ただ方向性としては正しい方向に進んでいるので、これの次のバージョンは使い物になるかもしれないが。 C++の標準で求められるのは、必要なものを作るためのビルディングブロックになる事だと思うんだが、 この辺はAPI的にこの上に必要なのが作れない所が難しいよな。 状況を限定する代わりにその状況では使えてそれ以外の状況では使えないものにするか、 状況を限定せずに誰も使えないものにするかの二択になってしまっていて、後者を選んでいる。

latchとbarrierはよそでは良く見るヤツで、こういうのは欲しいんだが、 これもブロッキングしてしまうと使えないじゃん、って感じはする。 やはりthread poolと分けてこの辺の概念を作るのは無理なんだよな。 ただこういうのがあるというのは知識としては知っておく価値はあるので、良い勉強になった。

現状は使い物にならないという結論は変わらないが、こっちの動向はウォッチしておく価値があるかもしれん。 flexible_barrierみたいなのがもうちょっと整備されれば使える日も来るかもしれない。

5章 memory orderはかなり詳しくて良い

例え話はかえって分かりにくくて説明の内容は気に食わないが、 それでも必要な例がいろいろあるのは素晴らしい。 自分はだいぶ理解が足りていなかったという事を理解した。

orderingは1024cores - Orderingと合わせて見るともうちょっとわかりやすい。

だいぶ理解が深まり、release-acquireの組も使える程度の理解にはなった。 ただこのatomic operationがボトルネックになった事はこれまで無いし、今後もなさそうなのでseq cstでいいかな、という気はする。

この本の印象がだいぶ良くなった。こういう解説はやはりC++の本で読まないとね。

6章、7章の並列コンテナはなかなか良い

6章がlockを使った並列コンテナだが、lockの粒度を下げる為に良くあるロックフリーを作るのと同じようなトリックを使う。 この章をあえて設けるのは良い判断に思う。 C++のロックフリーのコンテナはJavaやC#に比べてかなり複雑なので、 一旦ロックを使ってメモリ管理に絡む面倒さは置いておいて、 けれど並列性を上げるにはどうしたらいいか、という話を見る。

7章はlock freeのスタックやキューの話だが、これはなかなか素晴らしい。 メモリ管理の話がややこしすぎてやれる気がしないけれど、 こういうのはC++じゃないと扱えない話題なので、他の本で見た事は無く、 そうそう、こういう話が見たかったんだよ、という内容だ。

【書籍】ConcurrentProgrammingOnWindowsを見た時に、「あれ?これあC#ならGCがどうにかしてくれるだろうけれど、C++だとどうしたらいいんだ?」と思った事がちょくちょくあったが、 やはりC++だとめちゃくちゃ面倒くさい、という事が良く分かる。 一方でlock freeを諦めてshread_ptrでカウンタを管理すればだいぶ楽になるし、 また解放に関しても何らかの方法でそれが安全だと分かるならこれらの面倒を大きく迂回出来るが、 ロックベースのコードの説明が先にあるので、妥協した場合にどういうコードになるかを組み合わせて考える事が出来る。 これは実用的に良い。

C++はノードをunchainした後に、まだそのノードを参照している人がいるかもしれないのでその場では削除出来ない、という問題が出てくる。 これは並列GCがなんとかしてくれる問題の一部をプログラマがやらなくてはいけない、という話になる。 うわぁ、これは無理だな〜という内容だ。 説明も分かりにくくてexternal countとinternal countの説明は意味が分からない。 ただそれでも、必要な事は書いてあるので自分で考える事は出来る。この情報は貴重だ。

個人的にはARMとかx64ではどうなのか、という話をもう少し見たい。現代的なプロセッサでどうなのか、というのが一番の関心事だろうから。そういった情報が無いのは残念ではある。

それでも、C++で書いた例というのは手元においておく必要がある、と思わせる内容で、内容も十分に必要な事が入っているように思う。

8章のDesigning concurrent codeは読む価値無し - 2023-11-01 (水)

7章までがなかなか良かったのだが、8章は酷い。 7章までで道具を作って8章からそれらを作った大きなプログラムの話をするのかと思いきや、 並列の問題みたいなのをまとめサイト並の中身のなさで並べるだけで、 最後に書くのもparallelなfor_eachとかのおもちゃみたいな物ばかり。 普通の規模のアプリを書いた事があるのか?と疑いたくなるほど。

小さなものだけを考えるのが何故良くないか、というと、そこの単位でブロッキングして問題が終わってしまうからだ。 コアを使い切ろうとすればそういう終わりの所で余るコアには次のタスクを入れなくてはいけなくて、 それらをアプリケーション全体でいろいろ調整するのが日常なのに、 ここにある道具立てではそれが出来ない。 大きなコードの元になるような小さなコードじゃなくて、小さなコードじゃないと使えない方法を使っているのがダメな所だ。

7章の専門性の高さと8章の素人っぽさは、本当に同じ作者が書いたのか?と思いたくなる内容だけれど、 並列コンテナはトピックとしてそれだけ洗練された分野だという事なんだろうなぁ。

9章のthread poolとinterruptible threadはまぁまぁ - 2023-11-04 (土)

thread poolはmutexを使った普通のwork stealingの実装で簡単なコードで難しい事はないが、 自作のthread poolには必要になる事もあるだろう、という感じのコードなので参考にはなる。

interruptible threadはそんなに必要とは思えない(使う機会が少なく、使い機会では必要なメカニズムを手書きすれば良い)が、 必要な機会がある人には参考にはなりそうなので悪くは無いと思う。

どちらも7章までの専門性の高さに比べると突然凡庸なプログラマが書いた感じの内容になっていて、 コードも簡単になっているが、この手のコードを触る人にはアイデアとして触れておく価値はある気がするし、 何より簡単なので見ておくのは悪くないとも思った。

一方でこの程度の内容なら5章、6章、7章の話は全く要らない訳で、 本の構成としてターゲットを絞りきれてない感じはあるよなぁ。 こういう内容を書くなら前半をもっと薄くしてmutexを使ってもっと本格的なコードについて語るような本にした方が良いし、 そういう内容が嫌ならこの章ももっとロックフリーな話をしていかないと、それ以前との断絶がある。 それぞれ価値があると思うのだが、現実はどっちつかずなこの本一冊しか無いので、 これで学べる事を引き出す事でお茶を濁すしか無かろう。

この本はここまで計測の話が無い

ちなみにこの本がいまいちな事として、ここまで計測の話が全く出てこない。 例えば自分も特定の環境ではthread poolを自作しているのだが、グローバルなタスクキューしか持ってない。 なんでかというと、そこがボトルネックになった事が今まで無いから。 遅くなったら直そうと思って最初に作ったものが、全く遅くならない。

この辺はどういう時にどこが遅くなるのか、みたいなのを具体的な計測結果を元に話をしないと、 どういう時にやるべき最適化なのかの説得力が無い。 work stealはみんなやるのでここが詰まるのは良くある事なんだろうけれど、 自分の感覚だとかなりタスクが小さくないと詰まらないんだよなぁ。 ソートとかのアルゴリズム的なものをタスクに分けるような特殊なケース以外では大して問題にはならないんじゃないか、という気はする。

ロックフリーも計測の結果どのくらいご利益があるかを話してくれないと、 この複雑さをどのくらい受け入れるべきか、みたいなのが良く分からない。 特にアーキテクチャによって大きく違う1ワード以上のshared_ptrとかconsistency周りの話は、 いくつかの主要なアーキテクチャでの比較が無いと判断出来ないと思うんだが、 そういう話が全然無いんだよなぁ。

なんか時分で測った事無くて、他人が測って困って対策した事の対策だけを写しているだけなんじゃないか?と邪推してしまう。 特に7章の専門性の高さは単に業界の蓄積がそこだけ良く知られているのでそれを写しているだけなんじゃないか、と思ってしまう。 本当に自分のアプリで困ってロックフリーにした事あるの?みたいな。

10章のC++ 17のconcurrencyの章はあまり読む気が起こらなかった

C++ 17のconcurrencyはparallel forとかの話で、自分の経験上この手のライブラリで一定以上複雑なアプリでコアが使い切れる事は無いので、あまり関心が湧かなかったので飛ばし読み。 C++ 17に興味がある人なら良いと思うが。MSのPPLとか、その他似たようなのはいたる所にあるがどれもあんま使われてないよね。

11章のテストと計測の話はほとんど読む価値無し

前半はコードをレビューして頑張ろうとかレビューする時にはこういう所を気をつけてるよ、という程度の話で、 その内容もそれほど深い何かを感じるものでは無い。 役に立たない訳では無いが、この章を読まなくてもレビューを知っている人がチェックリストを作れば同じような内容になるだろう。

テストは簡単なテストを書いてみた、みたいな内容であまり役に立たない。 自分で書いても同じ感じになるし、これだけ大変になってしまうのでカバレッジを上げていくのはとても大変だ、というあたりに問題があるので、 これを示されても「うーん、こりゃ大変だね」という結論以上のなにかは無い。

パフォーマンスの計測に関しては、いろいろな設定で測れ、と書いてあるだけ。使うツールすら書いてない。これは酷い。

という事で11章の前半は価値は無いでも無いが薄く、後半は全く価値がない、酷い内容だった。

全体を読んでの感想 - 2023-11-04 (土)

最後が酷かったので印象は悪いけれど、全体としてはC++で並列のコードを書くなら必携の本ではあろうと思う。 この本が素晴らしいというよりは代替が無いから、という感じではあるが、 代替が無いので価値は高い。

C++はC++特有の問題がいろいろあるので、それ以外の言語の並列の本では不十分だ、 という事が良く分かる本ではある。だからC++の本である価値が高い。 そしてC++は11以降で並列の話が整備されているので、それ以前の本は役に立たず、 14以降の内容で並列の話があるのはたぶんこの本が唯一の本なので、 他の選択肢は無い。

C++のメモリモデル周辺の内容と、ロックフリー周りの内容は素晴らしかった。高い専門性を持った人が書いた内容であるのは間違いない。 著者がこの辺の専門家でこの辺しか知らないのか、はたまた何か元になっている素晴らしい知見があるのかは分からないが(たぶんある程度は後者だと思う、人類の蓄積という点ではほぼ確実なので)、 この辺の内容がちゃんと解説されているという点でこの本は素晴らしい価値がある。 ただし解説はめちゃ分かりにくく、最後まで何が言いたいのか良く分からなかった所は結構ある。 そういう所は他を参照する必要があるが、 この本の価値は知らなくてはいけない事をすべて提示する事にあるので、 それを知る事が出来なくても目的はある程度満たせている。

しかもコード自体はちゃんとしたコードなので、参考になる事は多い。 なんだかんだで似たような問題に遭遇する事が多い、というか似たような問題しか人類にはまだ解けてないという感じと思うが、 そういう訳でどれか一つの回答のコードはとても参考になる。 このコード例の為にも手元に置いておく価値がある。

逆にそれ以外の内容は全くその辺の知見が活かされず、なんか残念な本になっている印象はある。 役に立たないコンサルって感じ。

STLの並列周りの説明としては良く書けているのかもしれないが、 STL自体があまり使い物にならない機能が多いので、あまり役に立たない印象だ。 この辺はSTLの並列周りがパラダイムを押し付けないが、押し付けないとどれか一つのパラダイムを選ぶとほとんど使い物にならなくなる、 という問題の結果であって、書籍の問題では無いかもしれない。 ただ書籍の多くの紙面が役に立たない、という問題はある。

この本は並列プログラムを書くのに必要な多くのことが欠けている。 それはmimallocとかfollyの中にある何かで、本当はそれを学びたい。 ただこの本には必要な事もいろいろ書いてあるので、それは学ぶ必要はある。 足りないだけで、必要なものは含まれている。

という事でいまいちな所も多いけれど、それでもC++で並列のコードを書くなら必携の本と思う。 いまいちな所をちゃんと無視出来る能力が読み手に求められるが、 そもそもC++で並列なコードを書くのは相当の技量を求められるものなので、 それが出来ないならそもそも近づいてはいけない。