WIP:Clojure 軽量スレッドを作ってみる
動機
STMが好きなので、STMを活かした軽量スレッドが作れたら面白そうだと思った
ネイティブスレッドの問題
いっぱい作れない
1スレッドで固定長の大きなメモリが必要
コンテキストスイッチが遅い
リング0 にスイッチする必要がある(よく分かっていない)
暇してるスレッドであろうともスイッチをしてしまう
解決策
メモリ消費
ユーザースレッドならば、スタックの動的確保が再現できるので解決
リング0
ユーザースレッドならば、カーネルのスレッドとは関係ないので解決
暇スレッドを見分ける
どうするか?
暇スレッドの見分け方
暇になるタイミングは基本的に IO 待ち。他にもスリープなど
これらはプログラマがあらかじめ判別可能
待ちが発生する処理をプログラマに教えてもらう。
code: clojure
; 軽量スレッドを仮に k-thread とする
(k-thread
; CPUを酷使する忙しい処理
(+ 1 2 3)
; 待ちが発生する処理は k-idle の中に書く
(k-idle
; IO 待ちが発生する、CPUを全然使わない処理
; CPUを酷使する忙しい処理
(+ 1 2 3))
これでスレッドが暇なのか、忙しいのかの判断ができるようになった。
暇スレッドと忙しスレッドの処理
ネイティブスレッドはスレッドプールで管理する。
待ち処理キューと、忙し処理キューを作る。
軽量スレッドが作られたら、まずはそれを忙し処理キューに入れる。
空いているネイティブスレッドがあれば、忙し処理キューから処理を取ってきて作業する。
その途中で待ちが発生すれば、その処理を中止し、待ち処理キューに入れる。
待ちによって処理を中止したネイティブスレッドは、忙し処理キューからまた処理を取ってくる。
待ち処理キューを処理するためのネイティブスレッドは必ず1つ以上確保して、そいつは常に待ち処理をする
待ち処理が終われば、それを忙し処理キューに入れる。
忙し処理キューが少量になりネイティブスレッドが余れば、そのネイティブスレッドを待ち処理に使うこともある
違うネイティブスレッドで跨って処理するが、STMは大丈夫なのか?
スコープごと移動するから大丈夫?
スレッドにさせる「処理」をどう切り分けて、どうデータ化して、キューに入れるかは慎重にやらないと……
メモ
thread pool を作る時の注意点と、自分で作らん方がいいという勧告
1つのスレッドプールに対して、2つのキューがあるから自作せざるを得なさそう
その他
名前どうしよう
1つの処理に対して busy / idle を切り替えながら継続する処理
待ちが発生する処理は、IO やらスリープやらあらかじめ決まっているので、言語側のコードで待ちが判別できる仕組みを持てば、プログラマが k-idle とかやる必要がなくなってハッピーになれそう
ライブラリ側で busy / idle してて、それを知らずにアプリ側でも busy / idle したら……
busy / idle を無駄に行き来することになる。
やっぱり言語側で対応するのが理想的だなぁ
JSどうしよう
Web Worker が使えるなら使う
シングルスレッドでも、idle 処理を後回しにして、busy 処理を優先的に処理する方法は有効なはず
core/async と比較
チャネルで暇/忙しを判定しているので、最効率を目指すとIO待ちでチャネルを作ることになる(ちゃんと調べてない)ので、書きやすさとしては変わらない気がする
むしろチャネルを作る必要がない分、書くのは楽かもしれない
キューへの移動時間が勿体無い