🙌
Emscripten wasm を zstd 圧縮して読み込むメモ
Emscripten で WASM module 作ったけど 2 MB くらいある.
圧縮してファイルサイズ減らしたい...
2025 年ですと zstd 圧縮がメインですから, zstd 使います.
tinyusdz の例では 2MB -> 400kb になりました.
背景
Emscripten(emcc) で -sMODULE
-sEXPORT_ES6
有効にしているとする.
この場合, 例えば tinyusdz.js(ブートストラップ)と tinyusdz.wasm ができて,
import initTinyUSDZNative from './tinyusdz.js';
let mod = await initTinyUSDZNative({});
こんな感じで初期化すると内部で wasm を読んでくれる.
tinyusdz.wasm ファイル名は tinyusdz.js の内部にハードコードされているので, 圧縮した wasm を扱う場合, tinyusdz.js をいじらなければならないか... と思いますが, 幸いにも引数で wasmBinary
(Uint8Array) を与えてあげるとそちらを読むようになっている.
zstd npm package
ブラウザ(client)では decompress だけあればよい. fzstd か zstddec あたりがメジャーであろうか.
fzstd は Pure JS 実装, zstddec は C コードを WASM 化.
(fzstd 24KB, zstddec 34 KB くらい)
方法
import { decompress } from 'fzstd'; // or your preferred zstd library
import initTinyUSDZNative from './tinyusdz.js';
// Decompress zstd compressed WASM
async decompressZstdWasm(compressedPath) {
try {
console.log(`Loading compressed WASM from: ${compressedPath}`);
const response = await fetch(compressedPath);
if (!response.ok) {
throw new Error(`Failed to fetch compressed WASM: ${response.statusText}`);
}
const compressedData = await response.arrayBuffer();
console.log(`Compressed WASM size: ${compressedData.byteLength} bytes`);
// Decompress using zstd
const decompressedData = decompress(new Uint8Array(compressedData));
console.log(`Decompressed WASM size: ${decompressedData.byteLength} bytes`);
return decompressedData;
} catch (error) {
console.error('Error decompressing zstd WASM:', error);
throw error;
}
}
let wasmBinary = await this.decompressZstdWasm("./tinyusdz.wasm.zstd");
const initOptions = wasmBinary ? { wasmBinary } : {};
let mod = await initTinyUSDZNative(initOptions);
こんな感じでいけます!
zstd -19
で tinyusdz.wasm を圧縮したら 400 KB(元は 2 MB くらい)になりました.
19は圧縮レベルかなり高いほうですが, 数MBくらいなら decode 速度にそれほど影響ないと思います.
(圧縮も 2,3 秒くらい)
さらなる高みへ
詳しいことは Claude 4 くんや ChatGPT くんに聞いて
Discussion