Less Relevant: Blocking vs Non-Blocking
ブロッキングかノンブロッキングかという議論、10-20 年前と比べるとだいぶどうでもよくなった。これは全ての二項対立に言えることだが、理論的な良さ・悪さは個別の実装のがんばりによっていくらでもひっくり返る。
ブロッキング API と大量のスレッドが嫌われた理由の一つは OS スレッドのオーバーヘッドだった。スタックとかのためにアロケートされるメモリや、スケジューラへの負担。
スケジューリングを自分で持つような言語処理系だと、たいがいスレッドはずっと軽量になる。その筆頭は Go や Erlang だろう。Java Project Loom の Fiber も似たようなものになりそうな雰囲気がある。OS に fiber 的な仕組みを突っ込むこともできることはできて、先の Project Loom の資料は Google が C++ fiber のため Linux kernel を改変している事例に触れている。根本的なレベルで言語(C++)の可搬性を損なうそういう変更はちょっと勘弁してほしいもんだけれども。
ランタイムによる軽量スレッドの反対にいるのがコンパイラによる非同期の隠蔽で、Haskell の do syntax にはじまり C# を経て async/await 構文は多くのメインストリーム言語が備えるに至った。Async/await はたしかにシステムの負担は少ないが、yield 時にコンテクストを heap に避難する必要があるためコンパイラの optimizer から見えるコードは分断されるし停止再開もキューへの出入りがあるし、真にまっすぐなコードのように速くはならない。システムへの負担がコンパイラとライブラリに皺寄せられている。
Fiber によってランタイムの負担を減らすアプローチと、非同期構文でコンパイラがランタイムの負担を引き取るアプローチ、どちらが良いかは結局実装次第。
そしてアプリケーションプログラマからすると、どちらを使うかは性能を意識して選ぶというよりエコシステムとの親和性で半自動的に決まるものになった。この話題で喧嘩できるのはレガシー言語のユーザーと真にヒマな troll だけではなかろうか。(そしてこれらは大部分が重複している。)どちらでもない人は完全にほっといてよい。
レガシー言語のユーザである自分はもともとノンブロッキング寄りの派閥だったけれど、Android の仕事をはじめてからはブロッキングでスレッド作りまくるのにも一定の理はあると考えるようになった。
OS スレッド、オーバーヘッドがでかいだけあって OS やランタイムの支援が手厚い。たとえば OS スレッドは止めてダンプできる。Thread dump はデッドロックのデバッグになくてはならない。ノンブロッキングの世界は一見 deadlock が無いように見えるけれど、結局依存関係が循環したり待つべきものを待ちそびれると dead lock 相当の問題は起こり、しかも多くのノンブロックシステムはそのデバッグ手段を提供していない。
同様に OS スレッドは Systrace やプロファイラのようなツールで実行フローを可視化することができる。ついでにロックの競合もランタイムが教えてくれる。これがコンパイラベースの非同期になると OS からは実行フローも依存関係も見えなくなり、性能解析が辛くなる。
などデバッグや性能分析のしさすさという点で OS スレッドとブロッキングには非同期構文にない良さがあり、自分の仕事にとってそれはすごく重要。もちろん Android で非同期を捨てタスク毎にスレッドを作りまくるのはそれはそれで非現実的だけれども、一方で GCD のある世界ほど非同期を強く推す必要もない気がする。Android にも GCD 的なものがあれば事情も違うかもだが。がんばれ(TODO: あとで観る)! Java の Executor とか 10 年以上進歩してないじゃんか・・・やる気出せよ・・・。
話がそれた。ノンブロッキングな世界にある性能解析やデバッグのやりづらさも、言語処理系のコード生成やランタイムががんばって色々実装すれば解決する問題かもしれない。OS には皆が使う故に大量のエンジニアリング支援をつっこめる強みがある一方、言語処理系がコードを annotation すると簡単に解決できるはずのことはたくさんあるし、ツールも誰かがやる気をだして作り込めばよいといえばよい。たとえば Chrome には一時期 Promise Debugger というのがあったらしい。
非同期構文を持つ言語はどのくらい性能分析やデバッグを助けてくれるのか。自分からは今の所あまり助けてくれていないように見えるけれども、不勉強なせいで自分が知らないだけかもしれないし、今はなくても言語の成熟にあわせて整ってくるかもしれない。
なのでどうしても二項対立で口論したいならせめて具体的な実装2つを戦わせてほしいし、そもそも喧嘩するヒマがあったら自分の使っている言語や OS が良いものになるよう足りてないものを議論したり作ったりで応援してほしいもんです。
つまり JVM や ART は Executor や VM や ftrace と実行フローをコミュニケートするための API を足して Systrace やプロファイラの可視化をなんとかしろ!