何かを作らないという決定
完全な並行処理の物語は、atomic・mutex・read-write lock を含むはずだ。Mere のロードマップはそれらを並べていた —— そして意図して作らなかった。選択はチェックリストでなく需要でなされた:どのプログラムが実際にそれらを要するか? 動く example が答え、その一つが gap の正確なコストを定量化し —— そして最初の分析が退けた genuine な case が現れたとき、正直な結論が一段修正された。
並行処理はいま動く:安全な primitive、人間的なコンビネータ、一致した四つの backend。だがロードマップは もっと並べていた —— 「完全な」並行処理の物語が持つと期待される、より低水準の共有状態の機構:atomic、 mutex、read-write lock。この Part の締めの回は、それらを作らない —— 少なくとも今は —— と決めることに ついて、そして何を作らないかをよく決めることが本物の工学的行為だという事実についてだ。チェックリストを 辿るのでなく、需要を測ることでなされる。
チェックリストは yes と言う;代わりに需要が問われた
並行処理システムの feature チェックリストは、ためらいなく atomic とロックを並べる;まともな言語は みなそれらを持つ。より有用な問いはより狭い:すでに存在するものを踏まえて、どのプログラムが実際に それらを要するのか? 動く example が答え、答えはたいてい「これらは要さない」だった。
共有可変状態に見える問題の大半は、実は map-reduce で、channel がすでにそれらを覆う。述語を満たす個数を 数えるのは共有カウンタに見えるが、各 worker が 0 か 1 を返し結果を合計するだけだ —— ロックなし、 atomic なし、backend を越えて変わらず走る。本当に状態を持つ問題は actor として書ける:一つの worker が 状態を所有し、他は channel でそれに request を送り reply を得る —— Erlang の「通信することでメモリを 共有せよ」で、Mere の channel は多対多なので、work queue が同じ機構からこぼれ落ちる。そして read-only の 共有は、新しい feature なしにすでに動く:不変な値は共有可能なので、複数の worker が一つの大きなテーブル や config を並行に capture して read できる。その三つの形 —— reduce、actor、共有 read —— を越えて、 既存の primitive で足りる。
それが正確な残余を残す:atomic とロックが genuine に効くのは性能のためだけだ。高頻度カウンタは、 actor への message より atomic increment の方がはるかに安い。read-heavy な共有状態は、あらゆる access を 一つの thread に直列化する actor より、read を並行に走らせる read-write lock の裏の方が速い。最初の判断が 従った:機能的には channel と actor が共有状態を完全に覆うので、この機構は性能最適化で、それを建てる 正しい時は、実の benchmark が実の bottleneck を示すときだ —— それはまた API が何であるべきかを正確に 定義する。それまで defer。それは、パッケージマネージャを defer し、より一般的な型システムの機構を棚に 残したのと同じ需要駆動の規律だ:まだ指させない需要のために建てるな。
正直な修正
その整った結論 —— 「純粋に性能、defer」—— は一段修正された。より難しく見ることが、それが退けた case を 掘り出したからだ。並列アルゴリズムのいくつかは実行中に共有可変状態を read して次に何をするかを決める、 そして branch-and-bound 探索がその clean な例だ:探索するにつれ、走行中の best 結果を保ち、それを使って それに勝てない枝を刈る。
小さな example がコストを正確に測った。同じ最大化を二通り —— 逐次に、一つの共有された走行中 best で、 そして並列に四つの worker を越えて、各自のローカル best で —— 走らせると、同じ答えを見つけたが、 まったく違うコストで:逐次版は高価な評価を一度、並列版は四度した。並列の worker は、互いの走行中 best を 安く read できず、共有された best が飛ばさせたはずのものを各自評価した。message passing は正しい答えを 与えるが、刈りを保つほど安く生きた値を共有できず、だからアルゴリズムの質が、その結果が正しくとも 劣化する。
それは純粋な性能でなく、アルゴリズムが悪くなることで、以前の結論が整いすぎだったことを意味した。 atomic への genuine な motivator が結局ある —— branch-and-bound、適応探索、生きた共有 budget。正直な 訂正は両方向に同時に走る:case は本物なので「ただの性能の心地よさ」は歩み戻すべき過剰主張だった;だが それはまた、ハードな capability gap ではない、並列プログラムはなお正しい答えを生むから —— だから 「これを表現できない」もまた過剰主張だろう。測られた真実は正確なものだ:今日、表現可能で正しい、だが 共有 atomic が取り戻すであろう、定量化された刈りの損失を伴って。
領収書つきの defer
だから決定は保たれた —— defer —— が、きちんとなされた defer は、drop と同じではない。gap を見つけた example は、それを閉じるものも指定する:read と compare-and-update 操作を持つ共有 atomic scalar、 スレッド間で共有可能と印づけられ、そして構造化された共有状態のための read-write lock、使用パターンを 綴って (worker が共有 best を read して刈り、勝ったとき atomic に更新)。トリガも名づけられる: branch-and-bound か適応並列探索が実のターゲットになったとき、これを建てる。これは領収書つきの先送りされた 問いだ —— それが無いことのコストは測られ、それが要する API は起草され、それを un-defer すべき条件は 書き下される。それが、決定と肩をすくめることの違いだ。
そしてそれは、連載全体が回ってきた規律の静かな頂点だ。何度も何度も、答えは defer することだった —— パッケージマネージャ、一般的な制約 solver、プログラムが欲しがるまでの Wasm スレッド —— 常に同じ試験で: 実の、現在の需要があるか、それとも推測された未来のものか? 作らないと決めることは、決定の不在ではない; よくなされれば、それは測定であり、起草された答えであり、名づけられたトリガであり、それはしばしば yes と 言うより多くの仕事だ。言語は、feature を拒むことでなく、各拒否を各 feature と同じくらい注意深くその 場所を稼がせることで、小さく保たれる。
これで、並行処理の Part が閉じ、それとともに主要なシステムの最後が。言語は思想、メモリモデル、 エフェクト、厳密な一致に保たれた四つの backend、自分自身で書かれたコンパイラ、公的な存在、そして安全な 並行処理を持つ —— そして、それがどこで意図して止まるかの、明確で測られた説明を。残るのはシステムでなく 地平だ。次回、最後の Part:次に来るもの —— 地図の縁の feature と、それらが建てられる条件。