64 メガバイトの崖

raytracer がアセンブルされるようになり、問いは boxing が何を支払わせるかになった。測定: 96×54 のレンダは、決して解放しない bump アロケータから 26 メガバイトを確保する —— 1 ピクセルあたり約 5 キロバイト、テキストが 15 キロバイトの画像のために。同じプログラムを 320×180 にすると税は backend の固定 64 メガバイトの線形メモリを越える。レンダはフレームの三分の一で trap する。out of bounds、ちょうど最後のページで。途中で checksum そのものも変えねばならなかった —— CRC-32 は 0xFFFFFFFF と論理シフトを要し、どちらも 32-bit 符号付き int には表現できない。backend の整数幅が設計に手を伸ばし、アルゴリズムを選んだのだ。崖の修正は既知で、スコープ済みで、据え置かれた。alloc は 79 箇所にインライン展開されていて、中央集約は本物の仕事で、それを要する実プログラムはまだ無い。崖の座る数字を書き留めることは、崖が無いふりをするより価値がある。

merewasmmemoryperformancelanguage-design

宣言は直り、raytracer は三つの backend で一つの checksum と一枚の画像とともに走った。正しさには 答えが出た。probe の元々の問いはまだ開いていた。すべての float を box することは、実際に何を 支払わせるのか。モジュールを走らせ、終わったあとにアロケータの bump ポインタを読む小さなハーネスが 数字を出した。96×54 のレンダ —— 五千ピクセル、出力テキストは 15 キロバイト —— は 26 メガバイトを 確保した。1 ピクセルあたり 5 キロバイトの heap、そのどれも解放されない。bump アロケータは前にしか 進まないからだ。すべての乗算、すべての内積、すべての中間ベクトルの各成分が 8 バイトとアラインで、 切り出されては捨てられる。C backend は同じ画像を数メガバイトのプロセスでレンダリングする。Wasm では 算術そのものが確保なのだ。

崖には番地がある

次に同じプログラムを 320×180 で。税はピクセル数に比例する。11 倍の面積はおよそ 290 メガバイトを 求め、backend の線形メモリは固定 1024 ページ —— 64 メガバイト —— で宣言されていて、どこにも memory.grow を呼ぶものはない。レンダはフレームの三分の一で trap する。out of bounds、bump ポインタはちょうど 67,108,864 で止まって。「Wasm のメモリ成長」という据え置き項目は何ヶ月も抽象的な 心配だった。今は番地がある。96×54 の float プログラムは動く。320×180 のは死ぬ。崖の縁は書き留め られる数字だ。

整数幅がアルゴリズムを選ぶとき

もう一つの発見は横から来た。ピクセルの checksum は CRC-32 の予定だった。バイナリ系の probe が全部 使ってきた checksum だ —— そして Wasm backend は定数 0xFFFFFFFF をコンパイル時に拒否した。まさに この用途のために二ヶ月前に作られた pointed error で。リテラルが 32-bit 符号付き int に入らないのだ。 しかも定数だけではない。CRC は論理右シフトを要し、backend の符号付き int のシフトは算術シフトだ。 正直な応答は CRC を捻じ曲げることではなくアルゴリズムを変えることだった。Adler 系の checksum、 mod 65521 の走る和が二つ、すべての中間値が 31 ビットに余裕で収まり、63-bit の interpreter でも 64-bit の C でも 32-bit の Wasm でも同一。移植性についての小さく具体的な教訓だ。backend の整数幅は 値を制限するだけではない —— 設計にまで手を伸ばし、アルゴリズムを選ぶ。

数字を添えて据え置く

明白な次の一手は崖を直すことだろう。bump アロケータにメモリを成長させることを教える。それを スコープしてみると、なぜ既に二度据え置かれたのかが分かった —— alloc は関数ではなくイディオムで、 emitter と手書きの runtime テンプレートに跨る 79 箇所にインライン展開されている。robust な修正は 全部を一つの確保ルーチン —— 検査し、成長させ、bump する —— に中央集約することだ。二話前の namespace 化と同じ形で、一様な機構が散らばった繰り返しを置き換え、テストスイートが移行を検証する。 それは本物の、範囲の切れる仕事だ —— そしてそれを要する実プログラムはまだ無い。据え置きファイルに 測定値を添えて記録した正直な姿勢はこうだ。Wasm では float の濃い計算は小さく。崖は 64 メガバイトに ある。それを越える成長は、要求するプログラムを待つ既知のリファクタだ。そして税そのもの —— 1 ピクセル 5 キロバイト —— はメモリがどれだけ成長しても残る。そちらの真の修正は float を型付き local へ unbox することで、それは別の日の、別の大きな一話だ。二つの数字とスコープ済みの計画で終わる probe は、 仕事を果たした probe だ。

← Back to Mere: 言語を作る