Files
gex/web/gex-wgpu-split.js
velzieandClaude Opus 5.5 cbc89c58bf Mesa cold part: a miss names its caller; list from the final build's profiles
The final build profiled again (trace replays with no WGSL cache, gears, the
Muck and HK pages) ran nothing outside the list; its margin adds 20 small
callees. A miss now logs the wasm frames under the trampoline. MP primary
9,593,437 bytes (1,929,678 zstd -19) against master's whole 18,898,007
(3,305,617); SP 9,392,394 (1,929,097) against 18,669,577 (3,300,261).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
2026-10-11 19:38:44 -04:00

162 lines
7.6 KiB
JavaScript

// Mesa's cold part (docs/mesa-wasm-size.md): a --pre-js of gex-wgpu-replay.
//
// The build splits the module with Binaryen's wasm-split (web/mesa-split.py).
// The functions the page's workloads run stay in gex-wgpu-replay.wasm; the
// rest moves to gex-wgpu-replay.deferred.wasm, and each table slot that would
// reach one of them holds a trampoline instead. A trampoline calls the import
// `gexcold.load` below, which instantiates the whole cold part over those
// slots, and then calls through its slot again: the function itself now. So
// a miss costs one load per instance of the module (the page's or Worker's
// that made it, and a pthread's own should Mesa run code there), not one per
// function, and leaves no JS frame on the stack that JSPI would have to
// suspend across. (Emscripten's own placeholders are JS functions that make
// the call themselves; web/mesa-split.py replaces them.)
//
// Two ways in, both here because this file runs in every realm the module
// does:
//
// - Prefetch. Once the module has presented a frame, the realm fetches the
// cold part and compiles it asynchronously, in the background. A miss
// after that only instantiates it.
// - A miss before then loads it synchronously: a synchronous XHR as text in
// the x-user-defined charset, compiled synchronously, which a Worker may
// do at any size.
//
// A page's main thread may not (Chromium refuses a synchronous compile or
// instantiation over 8 MB there), so on one the cold part is instantiated
// with the module, asynchronously, and never missed.
//
// The build sets the marker below when it split the module
// (web/build-mesa-wasm.sh); an unsplit build fetches nothing and changes
// nothing.
var gexMesaColdSplit = 'GEX_MESA_COLD_NO';
var gexMesaCold = function () {
return globalThis.__gexMesaCold || (globalThis.__gexMesaCold = {});
};
var gexMesaColdFile = function () {
return wasmBinaryFile.slice(0, -5) + '.deferred.wasm';
};
var gexMesaColdInstance = null;
// `gexcold.load`: the cold part, instantiated over this instance's table.
var gexMesaColdLoad = function () {
if (gexMesaColdInstance) return;
var t0 = performance.now();
var file = gexMesaColdFile();
var cache = gexMesaCold();
var module = cache[file];
var how = 'prefetched';
if (!(module instanceof WebAssembly.Module)) {
var xhr = new XMLHttpRequest();
xhr.open('GET', file, false);
xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.send(null);
if (xhr.status !== 200 && xhr.status !== 0) {
throw new Error("Mesa's cold part " + file + ': HTTP ' + xhr.status);
}
var text = xhr.responseText;
var buf = new Uint8Array(text.length);
for (var i = 0; i < text.length; i++) buf[i] = text.charCodeAt(i) & 0xff;
module = cache[file] = new WebAssembly.Module(buf);
how = 'fetched and compiled now, ' + buf.length + ' bytes';
}
gexMesaColdInstance = new WebAssembly.Instance(module, { primary: wasmRawExports });
// Who missed: the frames under the trampoline, for web/mesa-hot.txt.
var where = String(new Error().stack).split('\n').filter(function (l) {
return l.indexOf('.wasm') >= 0 && l.indexOf('gexcoldtramp') < 0;
}).slice(0, 4).map(function (l) { return l.trim().replace(/^at /, '').replace(/ \(.*$/, ''); });
console.warn('[gex-wgpu] cold part loaded on a miss (' + how + ') in ' +
(performance.now() - t0).toFixed(1) + ' ms, from ' + (where.join(' <- ') || '?'));
};
if (gexMesaColdSplit === 'GEX_MESA_COLD_YES' && !Module['instantiateWasm']) {
// The glue's own instantiation, with the trampolines' import added.
Module['instantiateWasm'] = function (imports, done) {
var cold = { load: gexMesaColdLoad };
var all = new Proxy(imports, {
get: function (target, ns) { return ns === 'gexcold' ? cold : target[ns]; },
});
if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) {
// A pthread instantiates the module the page sent it, and has no file
// name of its own for the cold part's to follow.
wasmBinaryFile ??= findWasmBinary();
done(new WebAssembly.Instance(wasmModule, all), wasmModule);
return {};
}
wasmBinaryFile ??= findWasmBinary();
var file = wasmBinaryFile;
var slow = function () {
return fetch(file, { credentials: 'same-origin' })
.then(function (r) { return r.arrayBuffer(); })
.then(function (bytes) { return WebAssembly.instantiate(bytes, all); });
};
// A page's main thread may not compile or instantiate a module over
// 8 MB synchronously, so there the cold part is not left for a miss: it
// is instantiated now, asynchronously, before the module starts. (The
// pages that run Mesa there are demos and test harnesses; the desktop's
// compositor and SP's system Worker are Workers, and load it lazily.)
// `gexColdLazy` keeps it lazy anyway, for measuring a miss.
var eager = typeof ENVIRONMENT_IS_WEB !== 'undefined' && ENVIRONMENT_IS_WEB &&
!Module['gexColdLazy'];
WebAssembly.instantiateStreaming(fetch(file, { credentials: 'same-origin' }), all)
.catch(slow)
.then(function (r) {
if (!eager) return r;
var t0 = performance.now();
var cold = gexMesaColdFile();
var imp = { primary: r.instance.exports };
return WebAssembly.instantiateStreaming(fetch(cold, { credentials: 'same-origin' }), imp)
.catch(function () {
return fetch(cold, { credentials: 'same-origin' })
.then(function (x) { return x.arrayBuffer(); })
.then(function (bytes) { return WebAssembly.instantiate(bytes, imp); });
})
.then(function (c) {
gexMesaColdInstance = c.instance;
console.warn('[gex-wgpu] cold part loaded with the module (a page\'s main thread) in ' +
(performance.now() - t0).toFixed(1) + ' ms');
return r;
});
})
.then(function (r) { done(r.instance, r.module); },
function (e) { abort('instantiating Mesa failed: ' + e); });
return {};
};
}
if (gexMesaColdSplit === 'GEX_MESA_COLD_YES' && !Module['gexNoColdPrefetch'] &&
!(typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD)) {
// Wait for the first presented frame, then fetch and compile in the
// background. Not in a pthread: the module's pool started each with this
// file, Mesa runs no code there, and every one of them fetched and
// compiled its own copy (four, on an mt build). A miss on a pthread would
// compile the cold part synchronously, which a Worker may do.
var gexColdPoll = setInterval(function () {
if (typeof _gex_gl_presented !== 'function' || typeof wasmBinaryFile !== 'string') return;
var presented = 0;
try { presented = _gex_gl_presented(); } catch (e) { return; }
if (!presented) return;
clearInterval(gexColdPoll);
if (gexMesaColdInstance) return;
var file = gexMesaColdFile();
var cache = gexMesaCold();
if (cache[file]) return;
cache[file] = 'pending';
var t0 = performance.now(), size = 0;
fetch(file).then(function (r) {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.arrayBuffer();
}).then(function (bytes) {
size = bytes.byteLength;
return WebAssembly.compile(bytes);
}).then(function (module) {
if (!(cache[file] instanceof WebAssembly.Module)) cache[file] = module;
console.warn('[gex-wgpu] cold part prefetched (' + size + ' bytes) in ' +
(performance.now() - t0).toFixed(1) + ' ms');
}, function (e) {
if (cache[file] === 'pending') delete cache[file];
console.warn('[gex-wgpu] cold part prefetch failed: ' + e);
});
}, 1000);
}