Skip to content

build: reduce WASM initial memory 128β†’24 MiB to cut Node load footprint#158

Closed
benasher44 wants to merge 1 commit into
constructive-io:mainfrom
benasher44:benasher44/reduce-wasm-memory
Closed

build: reduce WASM initial memory 128β†’24 MiB to cut Node load footprint#158
benasher44 wants to merge 1 commit into
constructive-io:mainfrom
benasher44:benasher44/reduce-wasm-memory

Conversation

@benasher44

@benasher44 benasher44 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Problem

Loading the parser on Node pulls in ~200 MB of memory. The bulk of it is a fixed reservation in the WASM binary itself: the module declared an initial linear memory of 128 MiB (2048 pages), which is allocated when the module is instantiated (before a query is parsed). Node reports this as ~129 MB of external memory immediately on load.

memory section: min 2048 pages = 128 MiB   max 16384 pages = 1024 MiB

The build already sets -sALLOW_MEMORY_GROWTH=1. I'm not sure what a large initial value buys us. Static data in the binary looks to be <1 MiB.

Change

Two build flags, applied to templates/Makefile.template (regenerated versions/13–18/Makefile) and full/Makefile:

flag before after
-sINITIAL_MEMORY 128 MiB (134217728) 24 MiB (25165824)
-sSTACK_SIZE 32 MiB (33554432) 16 MiB (16777216)

ALLOW_MEMORY_GROWTH and MAXIMUM_MEMORY (1 GiB) are unchanged, so the heap still grows on demand. The 32 MiB stack was what pinned INITIAL_MEMORY so high; profiling confirmed deep nesting fails cleanly (a catchable SqlError) well within 16 MiB.

Measured impact (Node)

before after
external at load ~129 MB ~24 MB

Regression guard

  • memory.test.js: instantiates the module and asserts HEAPU8.buffer.byteLength (the live initial allocation, read before any parse) stays within budget
  • deep-nesting.test.js: asserts deeply nested input (up to 10k-deep function-call trees) always parses or throws a clean error, never a WebAssembly.RuntimeError. A stack that's too small would surface as a hard trap and fail CI, so the stack cut is validated by its own test on every rebuild.

Notes / scoping

  • v13 is intentionally excluded from the test wiring. CI can't build v13 (it downloads the prebuilt @libpg-query/v13, still 128 MiB), so adding the guard there would fail CI. Its Makefile is updated, so the next real v13 rebuild picks up 24 MiB.
  • v15 customization preserved. copy:templates copied over v15's forked-repo pin (constructive-io/libpg_query @ fix/negative-int-pg15), which the template doesn't model. I restored it by hand so v15's diff is the memory line only.

@benasher44 benasher44 force-pushed the benasher44/reduce-wasm-memory branch from 0b06fe7 to fd36006 Compare June 27, 2026 00:16
The WASM module reserves its full initial linear memory the instant it is
instantiated, before any query is parsed. The binaries declared 128 MiB
initial (2048 pages), which Node reports as ~129 MB of `external` memory on
load regardless of workload β€” the bulk of the ~200 MB footprint users hit.

Since the build already sets ALLOW_MEMORY_GROWTH=1 (max 1 GiB), a large
initial value buys nothing: it only inflates the load-time footprint. Static
data in the binary is <1 MiB. Two changes:

- INITIAL_MEMORY 128 -> 24 MiB.
- STACK_SIZE 32 -> 16 MiB (this was the floor on INITIAL_MEMORY). Profiling
  shows the parser fails cleanly (catchable "memory exhausted" SqlError) under
  deep nesting rather than overflowing the C stack, and practical nesting depth
  is bounded well within 16 MiB.

Net: `external` at load drops ~129 MB -> ~24 MB.

Regression guards (per-package, run by CI which rebuilds the WASM from source):
- memory.test.js asserts HEAPU8.buffer.byteLength (the live initial allocation)
  stays within budget.
- deep-nesting.test.js asserts deeply nested input always parses or throws a
  clean error, never a hard WebAssembly trap β€” so a too-small stack fails CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@benasher44 benasher44 force-pushed the benasher44/reduce-wasm-memory branch from fd36006 to c83b198 Compare June 27, 2026 00:25
@benasher44 benasher44 changed the title build: reduce WASM initial memory 128β†’48 MiB to cut Node load footprint build: reduce WASM initial memory 128β†’24 MiB to cut Node load footprint Jun 27, 2026
@benasher44

Copy link
Copy Markdown
Contributor Author

I think we're going to just maintain our own native build. Closing for now, but will link here the final direction we take

@benasher44 benasher44 closed this Jun 29, 2026
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