diff --git a/Cargo.lock b/Cargo.lock --- a/Cargo.lock +++ b/Cargo.lock @@ -977,7 +977,7 @@ checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1535,9 +1535,11 @@ "serde_urlencoded", "sha2", "smol_str", + "snmalloc-rs", "socket2", "tempfile", "thiserror 2.0.18", + "tikv-jemallocator", "tokio", "tokio-util", "tokio-websockets", @@ -2349,7 +2351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3000,7 +3002,7 @@ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3419,13 +3421,31 @@ ] [[package]] +name = "snmalloc-rs" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb317153089fdfa4d8a2eec059d40a5a23c3bde43995ea23b19121c3f621e74a" +dependencies = [ + "snmalloc-sys", +] + +[[package]] +name = "snmalloc-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065fea53d32bb77bc36cca466cb191f2e5216ebfd0ed360b1d64889ee6e559ea" +dependencies = [ + "cmake", +] + +[[package]] name = "socket2" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3560,7 +3580,7 @@ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3570,7 +3590,7 @@ checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3630,6 +3650,26 @@ checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" +dependencies = [ + "libc", + "tikv-jemalloc-sys", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,10 @@ edition = "2024" [features] -default = ["indexer", "indexer_stream"] +default = ["indexer", "indexer_stream", "alloc-jemalloc"] +alloc-mimalloc = ["dep:mimalloc"] +alloc-jemalloc = ["dep:tikv-jemallocator"] +alloc-snmalloc = ["dep:snmalloc-rs"] __persist_sync_all = [] backlinks = [] relay = [] @@ -52,7 +55,9 @@ chrono = { version = "0.4.43", features = ["serde"] } humantime = "2.3.0" -mimalloc = { version = "0.1", features = ["v3"] } +mimalloc = { version = "0.1", features = ["v3"], optional = true } +tikv-jemallocator = { version = "0.6", optional = true } +snmalloc-rs = { version = "0.3", optional = true } hex = "0.4" scc = "3.6.6" data-encoding = "2.10.0" diff --git a/src/main.rs b/src/main.rs --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use futures::FutureExt; use hydrant::config::Config; use hydrant::control::{ApiBinds, Hydrant}; -use mimalloc::MiMalloc; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; const DEFAULT_API_PORT: u16 = 3000; @@ -59,8 +58,31 @@ .ok_or_else(|| miette::miette!("HYDRANT_API_BIND is set but contains no addresses")) } +#[cfg(feature = "alloc-mimalloc")] #[global_allocator] -static GLOBAL: MiMalloc = MiMalloc; +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + +#[cfg(feature = "alloc-jemalloc")] +#[global_allocator] +static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + +// bake the tuned jemalloc runtime config into the binary. the prefixed tikv build reads +// the `_rjem_malloc_conf` symbol (plain `malloc_conf` silently no-ops). `percpu_arena` +// pins one arena per cpu, bounding per-arena dirty-page retention — the dominant rss lever +// here (~2.4x less resident than the default 4*ncpu arenas); `background_thread` purges +// asynchronously so `dirty_decay_ms:1000`/`muzzy_decay_ms:0` return pages without sync +// munmap churn. measured 1.72 GiB steady vs 4.14 for mimalloc. both options are linux-only +// (they no-op or warn elsewhere), so gate to linux; other targets get vanilla jemalloc. +// overridable at runtime via _RJEM_MALLOC_CONF. +#[cfg(all(feature = "alloc-jemalloc", target_os = "linux"))] +#[allow(non_upper_case_globals)] +#[unsafe(export_name = "_rjem_malloc_conf")] +pub static _rjem_malloc_conf: Option<&'static core::ffi::CStr> = + Some(c"background_thread:true,percpu_arena:percpu,dirty_decay_ms:1000,muzzy_decay_ms:0"); + +#[cfg(feature = "alloc-snmalloc")] +#[global_allocator] +static GLOBAL: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; #[tokio::main] async fn main() -> miette::Result<()> {