Skip to content

[Based on #134] runtime/wasmresume: reuse retained frame arenas - #142

Draft
cpunion wants to merge 3 commits into
codex/fork-wasm-resumable-integration-20260802from
codex/fork-wasm-resumable-arena-20260802
Draft

[Based on #134] runtime/wasmresume: reuse retained frame arenas#142
cpunion wants to merge 3 commits into
codex/fork-wasm-resumable-integration-20260802from
codex/fork-wasm-resumable-arena-20260802

Conversation

@cpunion

@cpunion cpunion commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Depends on #134.

Implements the K2 frame-allocation stage of xgo-dev#2152. This keeps the resumable backend opt-in through LLGO_WASM_RESUME=1.

Problem

The K1 backend allocates and frees a GC-root block for almost every lowered call frame. A 10,000-round channel handoff therefore spends most of its time in frame storage management and creates about 20.7 KiB / 12 allocations per operation in the acceptance workload.

Design

  • Retain frame blocks at each context's high-water mark and clear released ranges so stale pointers do not remain GC roots.
  • Reuse retained blocks by capacity; free the full chain only when the goroutine context is closed.
  • Give compatibility wrappers a stable runtime-owned arena while preserving stack-local context nesting.
  • Generate package-local O(1) allocation/release fast paths over the shared five-word frameBlock prefix. Block growth, retained-block selection, and reclamation remain runtime slow paths.
  • Mark fast helpers noinline. Unrestricted inlining inflated J64/P1 outputs by 1.34/1.16 MiB; one helper per using package keeps the improvement without duplicating the helper at every call site.
  • Keep the public resumable Context ABI at three pointer-sized words. Native and embedded targets do not emit these paths.

Results

Five interleaved macOS arm64 runs of the J32 hardening workload (10,000 channel roundtrips, median):

Backend Median Relative to K1
Asyncify baseline 2.776 us/op 11.53x faster
K1 #134 32.007 us/op 1.00x
K2 5.643 us/op 5.67x faster

K2 still takes 2.03x the Asyncify latency, so it does not yet satisfy the proposal's default-backend performance gate.

The final hardening workload reports about 80 B/op and 2.00 allocs/op, down from K1's about 20,739 B/op and 12.01 allocs/op.

Final artifact deltas against K1:

Target K1 K2 Delta
J32 (.mjs + .wasm) 2,271,504 B 2,284,548 B +13,044 B (+0.57%)
J64 (.mjs + .wasm) 2,593,225 B 2,600,398 B +7,173 B (+0.28%)
P1 (.wasm) 1,701,475 B 1,685,425 B -16,050 B (-0.94%)

Forced J32 build time was 43.790 s median versus K1's 43.281 s (+1.18%). Cortex-M4 output was byte-identical; macOS native text/data sizes were unchanged.

Validation

Resource bounds: GOMAXPROCS=2, GOMEMLIMIT=4GiB, -p=1.

  • compiler internal/wasmresume: 95.2% statement coverage
  • runtime internal/wasmresume: 92.0% statement coverage
  • go test -p=1 ./internal/build ./ssa ./cl
  • J32 optimization matrix: -O0, -O3, ThinLTO, full LTO
  • J32/J64/P1: scheduler, deadlock, timers, GC, hardening, 1,000 blocked goroutines, zero retained-root delta
  • J64/P1 final artifacts validated and executed after the noinline code-size guard
  • Ubuntu 24.04: J32/J64/P1 scheduler, deadlock, timers, GC, hardening and P1 validation
  • host frame arena benchmarks: 0 allocs/op on hot reuse and retained-overflow paths

Diff against #134: 16 files, +726/-90 in three reviewable commits.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

86d59afe4b35 | workflow run | long-term charts

Program measurements

Platform Workload File size vs main Build vs main Run vs main
Linux cprintf 18816 B +1.5% (worse) 372.120 ms -9.8% (better) 1.365 ms -12.0% (better)
Linux fmtprintf 2219448 B +0.3% (worse) 3.364 s -12.7% (better) 2.762 ms -16.0% (better)
Linux println 71776 B -1.2% (better) 361.455 ms -9.7% (better) 1.729 ms +3.3% (worse)
macOS cprintf 84672 B +0.0% 297.168 ms -28.3% (better) 2.295 ms -36.0% (better)
macOS fmtprintf 2361968 B +0.7% (worse) 3.019 s -23.4% (better) 17.534 ms -25.0% (better)
macOS println 125712 B -1.0% (better) 314.034 ms -24.3% (better) 3.334 ms -54.1% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs main
Linux BenchmarkLookupPCRandom 12.320 ns/op -8.6% (better)
Linux BenchmarkMergeCompilerFlags 144.400 ns/op -6.5% (better)
Linux BenchmarkMergeLinkerFlags 94.530 ns/op -6.7% (better)
Linux BenchmarkChannelBuffered 36.280 ns/op +4.4% (worse)
Linux BenchmarkChannelHandoff 22725 ns/op -29.1% (better)
Linux BenchmarkDefer 42.340 ns/op -24.8% (better)
Linux BenchmarkDirectCall 1.758 ns/op +12.5% (worse)
Linux BenchmarkGlobalRead 1.763 ns/op +13.0% (worse)
Linux BenchmarkGlobalWrite 2.805 ns/op +12.5% (worse)
Linux BenchmarkGoroutine 36428 ns/op -51.8% (better)
Linux BenchmarkInterfaceCall 9.141 ns/op +17.3% (worse)
Linux BenchmarkRuntimeGetG 1.758 ns/op -66.9% (better)
macOS BenchmarkLookupPCRandom 10.670 ns/op -14.2% (better)
macOS BenchmarkMergeCompilerFlags 94.040 ns/op -24.5% (better)
macOS BenchmarkMergeLinkerFlags 62.860 ns/op -9.4% (better)
macOS BenchmarkChannelBuffered 21.500 ns/op -22.5% (better)
macOS BenchmarkChannelHandoff 6576 ns/op -6.2% (better)
macOS BenchmarkDefer 28.830 ns/op -23.0% (better)
macOS BenchmarkDirectCall 0.943 ns/op -35.1% (better)
macOS BenchmarkGlobalRead 0.945 ns/op -22.0% (better)
macOS BenchmarkGlobalWrite 0.947 ns/op -36.3% (better)
macOS BenchmarkGoroutine 25211 ns/op -20.4% (better)
macOS BenchmarkInterfaceCall 4.120 ns/op -23.7% (better)
macOS BenchmarkRuntimeGetG 1.947 ns/op -24.4% (better)

Compared only with the latest matching platform in the main series.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant