Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions common/malloc_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ jemalloc-profiling = ["tikv-jemallocator/profiling"]
sysmalloc = []
# Enable jemalloc with unprefixed malloc (recommended for reproducible builds)
jemalloc-unprefixed = ["jemalloc", "tikv-jemallocator/unprefixed_malloc_on_supported_platforms"]
# Use mimalloc as the global allocator (alternative to jemalloc).
mimalloc = ["dep:mimalloc"]
# Use tcmalloc (gperftools) as the global allocator (alternative to jemalloc).
tcmalloc = ["dep:tcmalloc"]

[dependencies]
libc = "0.2.79"
metrics = { workspace = true }
mimalloc = { version = "0.1", optional = true, default-features = false }
parking_lot = { workspace = true }
tikv-jemalloc-ctl = { version = "0.6.0", optional = true, features = ["stats"] }

Expand All @@ -39,3 +44,7 @@ tikv-jemallocator = { version = "0.6.0", optional = true, features = [
"stats",
"background_threads",
] }

# tcmalloc links to gperftools which requires a UNIX-like system.
[target.'cfg(unix)'.dependencies]
tcmalloc = { version = "0.3", optional = true }
99 changes: 89 additions & 10 deletions common/malloc_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
//! This crate can be compiled with different feature flags to support different allocators:
//!
//! - Jemalloc, via the `jemalloc` feature.
//! - mimalloc, via the `mimalloc` feature.
//! - tcmalloc, via the `tcmalloc` feature.
//! - GNU malloc, if no features are set and the system supports it.
//! - The system allocator, if no features are set and the allocator is not GNU malloc.
//!
//! It is assumed that if Jemalloc is not in use, and the following two statements are correct then
//! we should expect to configure `glibc`:
//! When multiple allocator features are enabled (e.g. because `jemalloc` is hardcoded in the
//! binary dependencies), the priority order is: mimalloc > tcmalloc > jemalloc > glibc > system.
//!
//! It is assumed that if no allocator feature is in use, and the following two statements are
//! correct then we should expect to configure `glibc`:
//!
//! - `target_os = linux`
//! - `target_env != musl`
Expand All @@ -24,22 +29,55 @@
//! detecting `glibc` are best-effort. If this crate throws errors about undefined external
//! functions, then try to compile with the `not_glibc_interface` module.

// Ensure mimalloc and tcmalloc are not both enabled.
// Note: jemalloc + mimalloc/tcmalloc is allowed because jemalloc is hardcoded in the lighthouse
// and lcli binary dependencies. mimalloc/tcmalloc override jemalloc via cfg guards, matching the
// existing sysmalloc override pattern.
#[cfg(all(feature = "mimalloc", feature = "tcmalloc"))]
compile_error!("Cannot enable both `mimalloc` and `tcmalloc` allocator features");

// mimalloc and tcmalloc modules are only compiled on unix. Fail loudly rather than
// silently falling back to the system allocator.
#[cfg(all(not(unix), feature = "mimalloc"))]
compile_error!("`mimalloc` feature is only supported on unix targets");

#[cfg(all(not(unix), feature = "tcmalloc"))]
compile_error!("`tcmalloc` feature is only supported on unix targets");

#[cfg(all(
any(feature = "sysmalloc", not(feature = "jemalloc")),
any(
feature = "sysmalloc",
not(any(feature = "jemalloc", feature = "mimalloc", feature = "tcmalloc"))
),
target_os = "linux",
not(target_env = "musl")
))]
pub mod glibc;

#[cfg(all(unix, not(feature = "sysmalloc"), feature = "jemalloc"))]
#[cfg(all(
unix,
not(feature = "sysmalloc"),
not(feature = "mimalloc"),
not(feature = "tcmalloc"),
feature = "jemalloc"
))]
pub mod jemalloc;

#[cfg(all(unix, not(feature = "sysmalloc"), feature = "mimalloc"))]
pub mod mimalloc_alloc;

#[cfg(all(unix, not(feature = "sysmalloc"), feature = "tcmalloc"))]
pub mod tcmalloc_alloc;

pub use interface::*;

// Glibc malloc is the default on non-musl Linux if the sysmalloc feature is enabled, or jemalloc
// is disabled.
// Glibc malloc is the default on non-musl Linux when no allocator feature is set, or when
// sysmalloc is explicitly requested.
#[cfg(all(
any(feature = "sysmalloc", not(feature = "jemalloc")),
any(
feature = "sysmalloc",
not(any(feature = "jemalloc", feature = "mimalloc", feature = "tcmalloc"))
),
target_os = "linux",
not(target_env = "musl")
))]
Expand All @@ -52,8 +90,15 @@ mod interface {
}
}

// Jemalloc is the default on UNIX (including musl) unless the sysmalloc feature is enabled.
#[cfg(all(unix, not(feature = "sysmalloc"), feature = "jemalloc"))]
// Jemalloc is the default on UNIX (including musl) unless overridden by sysmalloc, mimalloc, or
// tcmalloc.
#[cfg(all(
unix,
not(feature = "sysmalloc"),
not(feature = "mimalloc"),
not(feature = "tcmalloc"),
feature = "jemalloc"
))]
mod interface {
#[allow(dead_code)]
pub fn configure_memory_allocator() -> Result<(), String> {
Expand All @@ -70,10 +115,44 @@ mod interface {
}
}

// mimalloc allocator.
#[cfg(all(unix, not(feature = "sysmalloc"), feature = "mimalloc"))]
mod interface {
#[allow(dead_code)]
pub fn configure_memory_allocator() -> Result<(), String> {
Ok(())
}

pub use crate::mimalloc_alloc::scrape_mimalloc_metrics as scrape_allocator_metrics;

pub fn allocator_name() -> String {
"mimalloc".to_string()
}
}

// tcmalloc allocator (via gperftools).
#[cfg(all(unix, not(feature = "sysmalloc"), feature = "tcmalloc"))]
mod interface {
#[allow(dead_code)]
pub fn configure_memory_allocator() -> Result<(), String> {
Ok(())
}

pub use crate::tcmalloc_alloc::scrape_tcmalloc_metrics as scrape_allocator_metrics;

pub fn allocator_name() -> String {
"tcmalloc".to_string()
}
}

// System allocator fallback for platforms where no allocator feature applies.
#[cfg(any(
not(unix),
all(
any(feature = "sysmalloc", not(feature = "jemalloc")),
any(
feature = "sysmalloc",
not(any(feature = "jemalloc", feature = "mimalloc", feature = "tcmalloc"))
),
any(not(target_os = "linux"), target_env = "musl")
)
))]
Expand Down
8 changes: 8 additions & 0 deletions common/malloc_utils/src/mimalloc_alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Set the allocator to `mimalloc`.

#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

pub fn scrape_mimalloc_metrics() {
// Metrics for mimalloc can be added in a follow-up.
}
8 changes: 8 additions & 0 deletions common/malloc_utils/src/tcmalloc_alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Set the allocator to `tcmalloc` (via gperftools).

#[global_allocator]
static ALLOC: tcmalloc::TCMalloc = tcmalloc::TCMalloc;

pub fn scrape_tcmalloc_metrics() {
// Metrics for tcmalloc can be added in a follow-up.
}
4 changes: 4 additions & 0 deletions lcli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ normal = ["malloc_utils"]
[features]
portable = ["bls/supranational-portable"]
fake_crypto = ['bls/fake_crypto']
# Use mimalloc as the global allocator (alternative to jemalloc).
mimalloc = ["malloc_utils/mimalloc"]
# Use tcmalloc (gperftools) as the global allocator (alternative to jemalloc).
tcmalloc = ["malloc_utils/tcmalloc"]

[dependencies]
account_utils = { workspace = true }
Expand Down
4 changes: 4 additions & 0 deletions lighthouse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ beacon-node-redb = ["store/redb"]
console-subscriber = ["console-subscriber/default"]
# Force the use of the system memory allocator rather than jemalloc.
sysmalloc = ["malloc_utils/sysmalloc"]
# Use mimalloc as the global allocator (alternative to jemalloc).
mimalloc = ["malloc_utils/mimalloc"]
# Use tcmalloc (gperftools) as the global allocator (alternative to jemalloc).
tcmalloc = ["malloc_utils/tcmalloc"]

[dependencies]
account_manager = { "path" = "../account_manager" }
Expand Down