feat(compiler): Compilation memory ceiling by RafaelGranza · Pull Request #3786 · NethermindEth/juno · GitHub
Skip to content

feat(compiler): Compilation memory ceiling#3786

Open
RafaelGranza wants to merge 6 commits into
granza/compiler-memory-limitfrom
granza/compiler-mem-2gb-pin
Open

feat(compiler): Compilation memory ceiling#3786
RafaelGranza wants to merge 6 commits into
granza/compiler-memory-limitfrom
granza/compiler-mem-2gb-pin

Conversation

@RafaelGranza

Copy link
Copy Markdown
Contributor

Sets two memory defaults for Sierra compilation, from measurements.

What changes

flag old new role
--max-compilation-memory 4 GB 2 GB per-compile address-space ceiling; also sets how many run at once
--node-memory-reserve 4 GB 4 GB (unchanged) memory held back for the rest of the node

Plus: the compile child process is pinned to GOMAXPROCS=2.

Real vs virtual memory

Each compilation runs in its own child process, with two very different memory numbers:

a compilation what it means
Real memory (RSS) ~150-200 MB what it actually occupies in RAM
Virtual memory (address space) ~1.5-1.8 GB what it maps, used or not

They differ this much because Go reserves far more virtual memory than it uses.

Compilation memory ceiling (2 GB)

--max-compilation-memory is an RLIMIT_AS limit, so it caps virtual memory. A compile maps ~1.6 GB of it, so the limit has to be around that, not the ~200 MB of real usage. Capping real memory is not reliable, only trhorugh cgroup, which needs special permision in containers.

Measured peak virtual memory of the heaviest honest mainnet contract:

arch / cores VmPeak
arm64 ~1.6 GB
amd64, 16 cores 1.77 GB
amd64, 128 cores (unpinned) 1.93 GB
any, pinned to GOMAXPROCS=2 ~1.58 GB

2 GB sits just above that peak. This flag also caps how many compile at once, so lowering it from 4 GB doubles concurrency (a 16 GB node goes from 3 to 6 slots) and halves the worst-case ceiling per compile.

Pinning the compile child to GOMAXPROCS=2

A compilation is one Rust FFI call, so extra Go threads do not make it faster; they only add per-thread stacks that grow the virtual memory peak. Inheriting a large host GOMAXPROCS pushes the memory peak up (1.93 GB at 128 cores, near the limit). Pinning keeps it host-independent (~1.58 GB) at no time cost.

Node memory reserve (4 GB)

Measured on a real synced mainnet node via process_resident_memory_bytes, which counts the node's whole footprint together: Go heap, pebble (DB) cache, state, contract execution and RPC.

RSS
steady state ~1.5 GB
peak over 24h ~2.25 GB
worst over 7d ~2.86 GB

4 GB covers the peak with headroom.

Validation

Checked derivation, fallback and virtual peak across Docker (arm64), native macOS (arm64) and a native amd64 host, from 1 to 128 cores. Build, tests and lint green.

@RafaelGranza RafaelGranza self-assigned this Jul 3, 2026
@RafaelGranza

Copy link
Copy Markdown
Contributor Author

Wanted to be sure the 2 GB ceiling doesn't kill honest compiles. --max-compilation-memory is a per-process RLIMIT_AS cap, so every address space for each compilation must stay under 2 GB.

contract arm64 amd64
carmine 1628 MB 1660 MB
vesu 1591 MB 1612 MB
ekubo 1548 MB 1557 MB

Max is ~1.66 GB, about 19% under the 2048 MB limit. It doesn't grow with concurrency either (address space is a per-process reservation, not shared budget), so no honest compile is killed by the limit on either architecture.

(arm64 is Docker on Apple Silicon, amd64 a native 16-core host. On macOS RLIMIT_AS is a no-op.)

@RafaelGranza RafaelGranza marked this pull request as ready for review July 3, 2026 03:27
@rodrodros

Copy link
Copy Markdown
Contributor

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.

2 participants