TinyGo 0.41.1, -target wasm, browser.
A wasm module I build declares 3328 MB of linear memory. The same source built with standard Go declares 29 MB — a factor of 115. Reading memory.buffer.byteLength immediately after instantiateStreaming, before go.run, already reports 3328 MB, so this is declared rather than grown, and no Go code has run to allocate it.
It matters beyond the footprint: at 3.3GB, ordinary addresses sit above 2GB, which puts every syscall/js call into #5095 (addresses arriving as signed i32). The terminals in my program stay blank while everything around them draws, one uncaught RangeError per call.
What it is not
- Not the collector.
-gc=precise produces byte-identically 3328 MB. Consistent with the memory never having been allocated by the GC.
- Not
-stack-size. I pass -stack-size 1MB; on a small binary that accounts for about 6MB (2MB without it, 8MB with).
- Not any one dependency. Built individually as
package main importing only that package, with the same flags:
| import |
declared |
charmbracelet/glamour (pulls goldmark, chroma) |
11 MB |
gdamore/tcell/v2 |
1 MB |
rivo/tview |
1 MB |
gdamore/proxima5 |
2 MB |
| xterm-go (a VT emulator) |
1 MB |
| two smaller DOM packages |
0 MB |
| all of them, in one program |
3328 MB |
So it appears to be emergent from the combination rather than attributable to a package, which is why I have not been able to reduce it further. Happy to bisect against a hypothesis if there is one worth testing — each build takes about 17 minutes, so I would rather aim than sweep.
Measuring it
// in wasm_exec.js constructor
globalThis.__memlog = () => this._inst.exports.memory.buffer.byteLength;
then, after instantiation:
(globalThis.__memlog() / 1048576).toFixed(0) + " MB" // 3328 MB
Flags: -no-debug -stack-size 1MB -interp-timeout 10m. The stack size is needed because the default overflows in regexp/syntax.Simplify when expanding a bounded repeat ({1,256} in goldmark's linkify pattern) — the overflow does not fault, it corrupts, and regexp then panics at startup on a case it treats as unreachable. The interp timeout is needed because the default three minutes is not enough for goldmark.
TinyGo 0.41.1,
-target wasm, browser.A wasm module I build declares 3328 MB of linear memory. The same source built with standard Go declares 29 MB — a factor of 115. Reading
memory.buffer.byteLengthimmediately afterinstantiateStreaming, beforego.run, already reports 3328 MB, so this is declared rather than grown, and no Go code has run to allocate it.It matters beyond the footprint: at 3.3GB, ordinary addresses sit above 2GB, which puts every
syscall/jscall into #5095 (addresses arriving as signed i32). The terminals in my program stay blank while everything around them draws, one uncaughtRangeErrorper call.What it is not
-gc=preciseproduces byte-identically 3328 MB. Consistent with the memory never having been allocated by the GC.-stack-size. I pass-stack-size 1MB; on a small binary that accounts for about 6MB (2MB without it, 8MB with).package mainimporting only that package, with the same flags:charmbracelet/glamour(pulls goldmark, chroma)gdamore/tcell/v2rivo/tviewgdamore/proxima5So it appears to be emergent from the combination rather than attributable to a package, which is why I have not been able to reduce it further. Happy to bisect against a hypothesis if there is one worth testing — each build takes about 17 minutes, so I would rather aim than sweep.
Measuring it
then, after instantiation:
Flags:
-no-debug -stack-size 1MB -interp-timeout 10m. The stack size is needed because the default overflows inregexp/syntax.Simplifywhen expanding a bounded repeat ({1,256}in goldmark's linkify pattern) — the overflow does not fault, it corrupts, andregexpthen panics at startup on a case it treats as unreachable. The interp timeout is needed because the default three minutes is not enough for goldmark.