From 080a264b5cd0a144b4940b83e07ca8846a905990 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Tue, 21 Jul 2026 20:19:46 -0400 Subject: [PATCH] perf(web): disable icount by default (4.7x faster book open) icount throttled TCG and starved the guest watchdog. Measured book open dropped from 146s to 31s. Off by default; ?icount restores it, ?noicount removed in favor of the new default. Harness now reports open-file timing. --- ISSUES.md | 8 ++++++-- tests/browser_raw_sd.py | 5 ++++- web/app.js | 7 +++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ISSUES.md b/ISSUES.md index cc5d3e5..93ea4b9 100644 --- a/ISSUES.md +++ b/ISSUES.md @@ -12,9 +12,13 @@ Opening `/Test/example.txt` through QEMU `vvfat` could corrupt coroutine/guest s Opening a book panics **only in WASM** (`Invalid read ... reason: rejected` → Load access fault) inside `uzlib_uncompress` during the reader's font `prewarmCache` **if** compiled guest stores are enabled. Native QEMU from the same submodule renders fine. -Root cause (confirmed by full diff against pristine upstream): `wasm_tci_only_tb = true` in `tcg_out_qemu_st` is **upstream qemu-wasm's own design**. Upstream forces every store-containing TB to the TCI interpreter because its compiled restartable-block store path is not rewind-safe (a yielded softmmu helper replays the block; safe for loads, corrupting for stores). This is the dominant cost of book open (font inflate is store-heavy, so the hot loop never JIT-promotes). +Root cause (confirmed by full diff against pristine upstream): `wasm_tci_only_tb = true` in `tcg_out_qemu_st` is **upstream qemu-wasm's own design**. Upstream forces every store-containing TB to the TCI interpreter because its compiled restartable-block store path is not rewind-safe (a yielded softmmu helper replays the block; safe for loads, corrupting for stores). Font inflate is store-heavy, so that hot loop never JIT-promotes — a real but secondary perf ceiling. -The real fix is to make compiled stores rewind-safe and drop the blanket TCI — a substantial TCG-backend task, ~15 min per browser iteration, no native repro. Full analysis: `docs/wasm-inflate-panic.md`. +Remaining fix is to make compiled stores rewind-safe and drop the blanket TCI — a substantial TCG-backend task, ~15 min per browser iteration, no native repro. Full analysis: `docs/wasm-inflate-panic.md`. + +## Resolved: `-icount` throttled book open ~4.7x + +The browser ran with `-icount shift=auto,align=off,sleep=off`, which serialized TCG and starved the guest watchdog (the earlier `wdt timeout` panics). Measured book open: 146s with icount, 31s without. icount is now off by default in `web/app.js`; `?icount` restores it. Guest timekeeping tracks host wall-clock, which is fine here because running flat-out feeds the watchdog faster, not slower. ## Resolved: reader grayscale rendered inverted and muddled diff --git a/tests/browser_raw_sd.py b/tests/browser_raw_sd.py index fece0e9..ed4665d 100644 --- a/tests/browser_raw_sd.py +++ b/tests/browser_raw_sd.py @@ -163,7 +163,9 @@ try: press(confirm, 'opening Browse Files') press(confirm, 'opening Test directory') press(confirm, 'highlighting example.txt') + open_start = time.monotonic() press(confirm, 'opening example.txt') + open_seconds = time.monotonic() - open_start down = driver.find_element(By.CSS_SELECTOR, '[data-button="down"]') up = driver.find_element(By.CSS_SELECTOR, '[data-button="up"]') @@ -187,7 +189,8 @@ try: print( f'browser {MODE} test passed at frame generation ' f'{final_state["generation"]}, ' - f'wasm heap {final_state["wasmHeap"] / 1024 / 1024:.1f} MiB' + f'wasm heap {final_state["wasmHeap"] / 1024 / 1024:.1f} MiB, ' + f'open example.txt took {open_seconds:.1f}s' ) except Exception: state = save_artifacts() diff --git a/web/app.js b/web/app.js index 441b382..e600a89 100644 --- a/web/app.js +++ b/web/app.js @@ -462,14 +462,17 @@ async function boot(file, variant, setStatus) { logLine(sdMessage); const { default: createQemu } = await import(qemuUrl); - const qemuLogFlags = new URL(location.href).searchParams.has('trace') + const params = new URL(location.href).searchParams; + const qemuLogFlags = params.has('trace') ? 'guest_errors,unimp,in_asm' : 'guest_errors,unimp'; + // icount throttles TCG ~4.7x here and starves the guest watchdog; off by default. ?icount restores it. + const icount = params.has('icount') ? ['-icount', 'shift=auto,align=off,sleep=off'] : []; const options = { arguments: [ '-machine', `xteink,variant=${variant}`, '-accel', 'tcg,tb-size=16', - '-icount', 'shift=auto,align=off,sleep=off', + ...icount, '-L', '/bios', '-display', 'none', '-serial', 'stdio',