例えばGCを止める

今作っているツールでGCを止めたくなったという話。

https://github.com/splaplapla/procon_bypass
作っているツールとは、ゲームコントローラーの信号をゲーム機にバイパスするためのものだ。

このツールでは、ゲームの信号をバイパスしている時に0.3秒くらいでも遅延すると、ゲーム体験で「ラグさ」を感じてしまう。遅延にはすごくシビアだ。

このツールはRuby言語で記述している。最近の調べによってわかったことがある。GCによって定期的に多少の遅延が発生していることがわかった。ボトルネックを特定する前は、「コントローラーから読み取る」「ゲーム機へ送信する」のどちらかのIOがネックかと思ったが、犯人はGCだった。

(これは余談だけど、BusyLoopを避けるために、ゲームコントローラーからの読み取りをブロッキングリードにしている。この実装がGCによる遅延の影響を引き上げている要因になっている。また、ノンブロッキングリードを使う場合、任意のsleepを入れて読み取りが成功するまでretryするのでCPUリソースを使いがちの反面、このretry中にGCが走り、結果的にGCの回数が全体的に減っている(憶測)。)

Rubyを使っている以上、GCは避けられないと思ったけど、そんなことはなかった。

マルチプロセス構成にし、処理を行うのは1つのプロセスのみにして、アイドル中のプロセスでGCすれば、処理中プロセスでのGCを止めれることに気がついた。
このアイディアは、 https://secon.dev/entry/20111006/1317893282/ に書いていることをそのままだ。感謝〜。

このアイディアを実現するには、マルチプロセスである必要はあるけど、プロセスは2つのみで十分だ。「処理時間 > GCにかかる時間」が前提になる。

ということで、それを実装したgemを作った。冒頭で紹介したツールにはまだ組み込めていない。
https://github.com/splaplapla/blue_green_process

自分のユースケースに寄せた実装なので柔軟性には乏しい。pipe経由で特定クラスの処理を、プロセスを切り替えつつ直列に実行するだけ。もちろん処理中はGCが走らないようになっている。

実際にツールに組み込んだ時、「処理時間 < GCにかかる時間」 になったら泣きます。

おわり