diff --git a/Cargo.lock b/Cargo.lock index 9aa6da9c..9166f018 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1792,6 +1792,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libmimalloc-sys" +version = "0.1.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9" +dependencies = [ + "cc", +] + [[package]] name = "libredox" version = "0.1.16" @@ -1884,6 +1893,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "mimalloc" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862" +dependencies = [ + "libmimalloc-sys", +] + [[package]] name = "mime" version = "0.3.17" @@ -1971,6 +1989,7 @@ dependencies = [ "hex", "http-body-util", "lazy_static", + "mimalloc", "rand 0.8.6", "reqwest 0.12.28", "serde", diff --git a/Cargo.toml b/Cargo.toml index 73b96c64..e8a81183 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,46 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } [profile.dev] opt-level = 3 +# Release profile is what the Dockerfile builds (`cargo build --release -p +# node`). Defaults from Cargo are intentionally lax (codegen-units = 16, +# lto = false, incremental = false), tuned for build-time and developer +# laptops — not for a prover hot-path that runs for minutes per call. +# +# Three knobs are turned on here. Each is documented in +# https://doc.rust-lang.org/cargo/reference/profiles.html. +# +# `lto = "thin"` enables cross-crate Link-Time Optimization on a +# per-codegen-unit basis. Plonky2's hot path (`plonky2_field::goldilocks` +# `Mul`, `reduce128`, the FFT inner loops) is heavily `#[inline]`d +# intra-crate, but the `node` / `script-plonky2` boundary calls *into* +# those generics through a trait surface. `thin` lets LLVM inline +# across that boundary. `fat` was tried upstream and reverted (see +# `0xPolygonZero/plonky2/Cargo.toml` — `#lto = "fat"` commented out); +# `thin` keeps almost the full speedup with a far smaller link-time +# regression. +# +# `codegen-units = 1` forces the whole crate into a single LLVM +# compilation unit. Trades ~30-60% link-time on the node binary for +# the most aggressive intra-crate optimization. Justified because the +# binary's wall-clock is dominated by Plonky2 prove calls (3-15 min at +# `MAX_IN_COINS=8`), not by `cargo build` time. +# +# `incremental = false` is the default for release; restating it +# explicitly so a future `CARGO_INCREMENTAL=1` env var on a build host +# (or a global `~/.cargo/config.toml` override) cannot silently +# fragment the codegen unit and undo the line above. Plonky2 upstream +# left `incremental = true` in their release profile — that is +# explicitly counter to what we want here. +# +# `panic = "abort"` is *not* set: `node/src/main.rs` installs a global +# panic hook (PR #36, MINTING_ADDRESS bootstrap fix) and the account +# / state layers use `RwLock` poison recovery to fail a single request +# without taking the process down. `panic = "abort"` would defeat both. +[profile.release] +lto = "thin" +codegen-units = 1 +incremental = false + [workspace.package] version = "1.1.0" edition = "2021" diff --git a/node/Cargo.toml b/node/Cargo.toml index 30cb885f..cf09cf5d 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -6,6 +6,21 @@ edition.workspace = true [dependencies] bitcoin = { workspace = true } bitcoin_hashes = { version = "0.16.0", features = ["std"] } +# Drop-in allocator. Plonky2's prove path allocates many polynomial +# and witness buffers across the rayon worker pool; glibc-malloc on +# the Debian-bookworm-slim runtime image is not tuned for that +# pattern. mimalloc consistently outperforms glibc malloc on +# multi-thread alloc-heavy ARM64 workloads. Wired in `main.rs` via +# `#[global_allocator]`; tests, integration tests, and the +# `program-plonky2` / `script-plonky2` library crates keep using the +# system allocator (no behavioural change at the prover circuit +# level). `default-features = false` is defensive — upstream +# (`purpleprotocol/mimalloc_rust`) currently ships `default = []`, so +# this disables nothing today. It pins the opt-in posture for the +# future: if upstream ever turns on `secure`, `debug`, `extended`, or +# a v2 flip via default features, the node binary stays on the small +# portable build until someone opts in deliberately. +mimalloc = { version = "0.1", default-features = false } sha2 = { workspace = true } serde = { workspace = true } bincode = { workspace = true } diff --git a/node/src/main.rs b/node/src/main.rs index ead1a6fe..9c4feb5a 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -8,6 +8,16 @@ //! duplicating definitions or making the binary itself reachable //! from a `cargo test --test ...` target. +// mimalloc replaces the default allocator process-wide. Scoped to the +// binary (`main.rs`) so the `node` library crate, integration tests, +// and the `program-plonky2` / `script-plonky2` crates continue to use +// the system allocator — keeps unit-test behaviour identical to CI +// and avoids pulling a C build dependency into every test target. +// See the comment on the `mimalloc` line in `node/Cargo.toml` for the +// rationale. +#[global_allocator] +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + use node::account_node; use node::db; use node::publisher::EsploraConfig;