From 361c48f6560b36bc665324b4618ccb089f073ac2 Mon Sep 17 00:00:00 2001 From: Rayhaan Jaufeerally Date: Sun, 4 Oct 2026 01:03:16 +0200 Subject: [PATCH] Use jemalloc as the daemon's memory allocator A full table from several neighbors means millions of small, long-lived allocations that are replaced as routes change. jemalloc keeps the resident size of such a workload closer to what is in use than the system allocator does, glibc's in particular, and does not depend on the C library the binary is linked with. Not on MSVC, which jemalloc does not support. Building now needs a C compiler and make; the installation page says so. Co-Authored-By: Claude Opus 5.5 --- Cargo.lock | 43 +++++++++++++++++++ Cargo.toml | 2 + bin/bgp/Cargo.toml | 5 +++ bin/bgp/src/main.rs | 7 +++ .../docs/getting-started/installation.md | 3 ++ 5 files changed, 60 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9f59f8b..cbf5746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,6 +164,7 @@ dependencies = [ "clap", "eyre", "protobuf", + "tikv-jemallocator", "tokio", "tracing", "tracing-subscriber", @@ -373,6 +374,16 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cc" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -489,6 +500,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + [[package]] name = "flate2" version = "1.1.10" @@ -1476,6 +1493,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -1657,6 +1680,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tikv-jemalloc-sys" +version = "0.7.1+5.3.1-0-g81034ce1f1373e37dc865038e1bc8eeecf559ce8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2825c78386b4ae0314074867860ba9577875de945f05992c38815cbec327f0" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249f09e49ab1609436f34c776e84231bead18d6a955f119f939bdc1d847561bd" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "tinystr" version = "0.8.4" diff --git a/Cargo.toml b/Cargo.toml index f234901..98438de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,3 +63,5 @@ hashbrown = "0.17" axum = "0.8.9" tokio-stream = { version = "0.1.19", features = ["sync"] } reqwest = { version = "0.13.5", default-features = false, features = ["json"] } +# The daemon's memory allocator; see bin/bgp/src/main.rs. +tikv-jemallocator = "0.7.0" diff --git a/bin/bgp/Cargo.toml b/bin/bgp/Cargo.toml index 383a248..45e1820 100644 --- a/bin/bgp/Cargo.toml +++ b/bin/bgp/Cargo.toml @@ -18,5 +18,10 @@ tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true +# jemalloc does not build with the MSVC toolchain; there the system +# allocator is used. +[target.'cfg(not(target_env = "msvc"))'.dependencies] +tikv-jemallocator.workspace = true + [lints] workspace = true diff --git a/bin/bgp/src/main.rs b/bin/bgp/src/main.rs index 5c905b8..5f453c5 100644 --- a/bin/bgp/src/main.rs +++ b/bin/bgp/src/main.rs @@ -13,6 +13,13 @@ use crate::logging::LoggingArgs; mod logging; +// jemalloc, rather than the C library's allocator. Taking in a full table +// allocates from many threads at once, which glibc's allocator handles +// less well and musl's, in the static build, much worse. +#[cfg(not(target_env = "msvc"))] +#[global_allocator] +static ALLOCATOR: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + /// A BGP speaker. #[derive(Parser, Debug)] #[command(version)] diff --git a/docs/content/docs/getting-started/installation.md b/docs/content/docs/getting-started/installation.md index 369de26..7ab850c 100644 --- a/docs/content/docs/getting-started/installation.md +++ b/docs/content/docs/getting-started/installation.md @@ -14,6 +14,9 @@ Cargo, or build a container image with the provided `Dockerfile`. - A stable Rust toolchain from [rustup.rs](https://rustup.rs). The container image uses Rust 1.92. +- A C compiler and `make`, to build the jemalloc memory allocator that the + daemon uses (for example `build-essential` on Debian and Ubuntu, + `base-devel` on Arch Linux, or the Xcode command line tools on macOS). - Linux, for kernel route installation. On other operating systems, Ferrix operates as a BGP speaker only and does not modify forwarding.