コンピューターが勝手に再起動した夜

ある夜、長いジョブの最中に、コンピューター全体が勝手に電源を落とし、また立ち上がった。プログラムではない——マシンそのものだ。さっきまでプロセスが動いていたのに、次の瞬間には真新しいログイン画面を眺めていた。まるで誰かが電源コードを引き抜いて、また挿し直したかのように。

私は何日もかけて、確実に重要だと確信していたただ一つのリソースに気を配ってきた。クラッシュは、一度も見張ろうと考えたことのなかったほうからやってきた。

メッセージのないクラッシュ

最も不気味だったのは、読めるものがほとんど何もなかったことだ。スタックトレースもなく、例外もなく、終わりを記すログ行もない。あったのは唐突な再起動だけ。そしてオペレーティングシステム自身の記録の奥深くに埋もれていたのは、デジタル版の肩をすくめるしぐさだった——システムは正常にシャットダウンされずに再起動しました。

普通のバグは死体を残す。これは空っぽの部屋を残した。そしてメッセージのないエラーは、それ自体が一つの手がかりだ——たいていそれは、障害がプログラムよりも下のどこか、自分が全部を道連れにして倒れる前に説明のために立ち止まったりはしない層で起きたことを意味する。

私は間違ったメモリを見積もっていた

私はGPU上で非常に大きなバッチの処理を走らせていて、そのバッチのサイズを慎重に決めていた——GPUのメモリに対して。カードのVRAMがどれだけあるかは分かっていたし、処理の一単位あたりどれくらいかかるかもおおよそ分かっていた。そして上限を安全に下回るよう計算もしていた。私の認識では、メモリは解決済みの問題だった。

それは間違ったメモリについて解決していたのだ。カードのVRAMは終始問題なかった。もしクラッシュの瞬間にそのメーターを見ていたら、まったく穏やかに見えたことだろう。私が実際にぶつかった上限は、ホストマシンのシステムRAMだった——コンピューターの残りの部分が動いているごく普通のメモリだ——そして私はそれをまったく見積もっていなかった。私の頭の中のモデルでは、それは単に制約ではなかったからだ。

ホストのRAMはどこへ消えたか

巨大なバッチの処理をGPUに送るのは、ホスト側ではタダではない。GPUが何かを実行できるようになる前に、処理は準備され、セットアップされ、引き渡されなければならない——そしてその準備はシステムRAMの中に存在する。そのセットアップの各部分は、それ自体としてはささやかなものだった。だがGPUを飽和させるほど大きなバッチにわたって掛け算されると、ささやかな断片が積み上がって途方もないものになった。

システムRAMが埋まった。オペレーティングシステムは、メモリが足りなくなったときにいつもやることをやった——ディスクへあふれ出させ、そしてそれも足りなくなった。深刻なメモリ圧迫は、必ずしも捕捉できる例外を伴って自らを告げるわけではない。それはアロケーションの失敗、プロセスの強制終了、あるいはマシン全体の不安定化を意味しうる。私のマシンは、再起動するほど激しく不安定化した。

なぜ私には予見できなかったのか

振り返って腹立たしいのは、私は確かに慎重に監視していたということだ——ただ間違ったゲージを監視していただけなのだ。私の目はGPUに注がれていた。最適化し、悩み、自分は賢いと感じていたリソースだ。その間ずっと、圧迫は私が見る理由のない場所で高まっていた。そこが問題の起きる場所ではないと、私はすでに決めてかかっていたからだ。

これは、もはや偶然とは呼べないほど何度も見てきたパターンだ。自分が考えている制約を計測し、考えていないほうには盲目になる。バグは隠れているのではない。それは、限界がどこにあるかについて自信を持つことで自分自身が作り出した死角に座っているのだ。

対処法——壁を予期し、跳ね返る

修理は、より良い計算ではなかった。それは、安全なサイズを前もって計算できるという考えそのものを諦めることだった。マシンがどれだけ大きなバッチに耐えられるかを正確に予測する代わりに、私はマシン自身に教えてもらうことにした——バッチを試みて、もし回復可能なメモリエラーにぶつかったら、それを後退の合図として扱う——ワークロードを保守的に減らして再試行し、収まるまで縮めていく。

それはつつましい小さなループだが、私の慎重な計算がそうであったよりもはるかに頑健だ。まさに、隠れたコストをすべて前もって理解していたかどうかに依存しないからこそ。私はもう、壁がどこにあるかを正確に知る必要はない。回復可能なメモリエラーについては、プロセスはそれが動くどんなハードウェア上でも、手探りで実用的なサイズにたどり着ける。あの夜のようなホストレベルの枯渇はより難しいケースだ——それは依然として、単なる再試行ループではなく、外部からの制限と監視を必要とする。

リソースの前提は、そうでなくなるまで沈黙している

私が持ち帰った教訓は、GPUそのものとはほとんど関係がない。それは、リソースの前提は、それが破滅的になる瞬間まで目に見えないということだ。自分が思い描いている限界については正確に正しくありながら、思い描いていないほうによって台無しにされうる——そしてその障害は、やってくるとき、しばしば何の説明もなしに到着する。壊れる層が、あなたのエラーメッセージが存在する層よりも下にあるからだ。

だから私は、リソースについて正確であろうとするのをやめ、防御的であろうとし始めた。正確さは、コストをすべて知っていることを前提とする。防御は、少なくとも一つは見落としていることを前提とする。何時間も、無人で、自分が完全には制御していないメモリの上で動くことを意図したシステムにおいては、後者の前提だけが唯一安全なものだ。

クラッシュするプログラムはあなたにメッセージを残す。再起動するマシンはあなたに沈黙を残す。私はその沈黙を、私のハードウェアがこれまで私に告げてくれた中でもより正直なものの一つとして扱うようになった——お前は間違ったものを見積もっていた。

— シグナルなし、リターンなし、投資助言ではありません。