add preferred jemalloc config

add muzzy/dirty configuration mallctl interface

add program argument for --gc-muzzy=false

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2025-01-18 01:30:41 +00:00
parent 3eed408b29
commit 3dae02b886
12 changed files with 289 additions and 82 deletions

57
Cargo.lock generated
View file

@ -175,9 +175,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
version = "1.12.0" version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f409eb70b561706bf8abba8ca9c112729c481595893fd06a2dd9af8ed8441148" checksum = "1ea835662a0af02443aa1396d39be523bbf8f11ee6fad20329607c480bea48c3"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"paste", "paste",
@ -186,9 +186,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.24.1" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "923ded50f602b3007e5e63e3f094c479d9c8a9b42d7f4034e4afe456aa48bfd2" checksum = "71b2ddd3ada61a305e1d8bb6c005d1eaa7d14d903681edfc400406d523a9b491"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
@ -368,7 +368,7 @@ version = "0.69.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
"itertools 0.12.1", "itertools 0.12.1",
@ -393,9 +393,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.7.0" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]] [[package]]
name = "blake2" name = "blake2"
@ -495,9 +495,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.9" version = "1.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -1047,7 +1047,7 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
"crossterm_winapi", "crossterm_winapi",
"futures-core", "futures-core",
"mio", "mio",
@ -1122,9 +1122,9 @@ checksum = "817fa642fb0ee7fe42e95783e00e0969927b96091bdd4b9b1af082acd943913b"
[[package]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.6.0" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
[[package]] [[package]]
name = "date_header" name = "date_header"
@ -2378,7 +2378,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -2911,7 +2911,7 @@ version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
"memchr", "memchr",
"pulldown-cmark-escape", "pulldown-cmark-escape",
"unicase", "unicase",
@ -3032,7 +3032,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
] ]
[[package]] [[package]]
@ -3377,7 +3377,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-librocksdb-sys" name = "rust-librocksdb-sys"
version = "0.31.0+9.9.3" version = "0.31.0+9.9.3"
source = "git+https://github.com/girlbossceo/rust-rocksdb-zaidoon1?rev=123d6302fed23fc706344becb2f19623265a83f8#123d6302fed23fc706344becb2f19623265a83f8" source = "git+https://github.com/girlbossceo/rust-rocksdb-zaidoon1?rev=2d31cf323df7c6d95396ef0213e28936c2218bd6#2d31cf323df7c6d95396ef0213e28936c2218bd6"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"bzip2-sys", "bzip2-sys",
@ -3394,7 +3394,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-rocksdb" name = "rust-rocksdb"
version = "0.35.0" version = "0.35.0"
source = "git+https://github.com/girlbossceo/rust-rocksdb-zaidoon1?rev=123d6302fed23fc706344becb2f19623265a83f8#123d6302fed23fc706344becb2f19623265a83f8" source = "git+https://github.com/girlbossceo/rust-rocksdb-zaidoon1?rev=2d31cf323df7c6d95396ef0213e28936c2218bd6#2d31cf323df7c6d95396ef0213e28936c2218bd6"
dependencies = [ dependencies = [
"libc", "libc",
"rust-librocksdb-sys", "rust-librocksdb-sys",
@ -3441,7 +3441,7 @@ version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -3559,9 +3559,12 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "sd-notify" name = "sd-notify"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1be20c5f7f393ee700f8b2f28ea35812e4e212f40774b550cd2a93ea91684451" checksum = "561e6b346a5e59e0b8a07894004897d7160567e3352d2ebd6c3741d4e086b6f5"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "security-framework" name = "security-framework"
@ -3569,7 +3572,7 @@ version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
dependencies = [ dependencies = [
"bitflags 2.7.0", "bitflags 2.8.0",
"core-foundation", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -4171,7 +4174,7 @@ dependencies = [
[[package]] [[package]]
name = "tikv-jemalloc-ctl" name = "tikv-jemalloc-ctl"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/girlbossceo/jemallocator?rev=d87938bfddc26377dd7fdf14bbcd345f3ab19442#d87938bfddc26377dd7fdf14bbcd345f3ab19442" source = "git+https://github.com/girlbossceo/jemallocator?rev=82af58d6a13ddd5dcdc7d4e91eae3b63292995b8#82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
dependencies = [ dependencies = [
"libc", "libc",
"paste", "paste",
@ -4181,7 +4184,7 @@ dependencies = [
[[package]] [[package]]
name = "tikv-jemalloc-sys" name = "tikv-jemalloc-sys"
version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
source = "git+https://github.com/girlbossceo/jemallocator?rev=d87938bfddc26377dd7fdf14bbcd345f3ab19442#d87938bfddc26377dd7fdf14bbcd345f3ab19442" source = "git+https://github.com/girlbossceo/jemallocator?rev=82af58d6a13ddd5dcdc7d4e91eae3b63292995b8#82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -4190,7 +4193,7 @@ dependencies = [
[[package]] [[package]]
name = "tikv-jemallocator" name = "tikv-jemallocator"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/girlbossceo/jemallocator?rev=d87938bfddc26377dd7fdf14bbcd345f3ab19442#d87938bfddc26377dd7fdf14bbcd345f3ab19442" source = "git+https://github.com/girlbossceo/jemallocator?rev=82af58d6a13ddd5dcdc7d4e91eae3b63292995b8#82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
dependencies = [ dependencies = [
"libc", "libc",
"tikv-jemalloc-sys", "tikv-jemalloc-sys",
@ -4445,7 +4448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
dependencies = [ dependencies = [
"async-compression", "async-compression",
"bitflags 2.7.0", "bitflags 2.8.0",
"bytes", "bytes",
"futures-core", "futures-core",
"futures-util", "futures-util",
@ -4709,9 +4712,9 @@ dependencies = [
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"

View file

@ -431,17 +431,23 @@ version = "0.35.0"
# jemalloc usage # jemalloc usage
[workspace.dependencies.tikv-jemalloc-sys] [workspace.dependencies.tikv-jemalloc-sys]
git = "https://github.com/girlbossceo/jemallocator" git = "https://github.com/girlbossceo/jemallocator"
rev = "d87938bfddc26377dd7fdf14bbcd345f3ab19442" rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
default-features = false default-features = false
features = ["unprefixed_malloc_on_supported_platforms"] features = [
"background_threads_runtime_support",
"unprefixed_malloc_on_supported_platforms",
]
[workspace.dependencies.tikv-jemallocator] [workspace.dependencies.tikv-jemallocator]
git = "https://github.com/girlbossceo/jemallocator" git = "https://github.com/girlbossceo/jemallocator"
rev = "d87938bfddc26377dd7fdf14bbcd345f3ab19442" rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
default-features = false default-features = false
features = ["unprefixed_malloc_on_supported_platforms"] features = [
"background_threads_runtime_support",
"unprefixed_malloc_on_supported_platforms",
]
[workspace.dependencies.tikv-jemalloc-ctl] [workspace.dependencies.tikv-jemalloc-ctl]
git = "https://github.com/girlbossceo/jemallocator" git = "https://github.com/girlbossceo/jemallocator"
rev = "d87938bfddc26377dd7fdf14bbcd345f3ab19442" rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
default-features = false default-features = false
features = ["use_std"] features = ["use_std"]

View file

@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
[dependencies.rust-rocksdb] [dependencies.rust-rocksdb]
git = "https://github.com/girlbossceo/rust-rocksdb-zaidoon1" git = "https://github.com/girlbossceo/rust-rocksdb-zaidoon1"
rev = "123d6302fed23fc706344becb2f19623265a83f8" rev = "2d31cf323df7c6d95396ef0213e28936c2218bd6"
#branch = "master" #branch = "master"
default-features = false default-features = false

View file

@ -967,7 +967,7 @@ pub(super) async fn database_stats(
#[admin_command] #[admin_command]
pub(super) async fn trim_memory(&self) -> Result<RoomMessageEventContent> { pub(super) async fn trim_memory(&self) -> Result<RoomMessageEventContent> {
conduwuit::alloc::trim()?; conduwuit::alloc::trim(None)?;
writeln!(self, "done").await?; writeln!(self, "done").await?;

View file

@ -36,6 +36,7 @@ jemalloc_stats = [
"tikv-jemalloc-ctl/stats", "tikv-jemalloc-ctl/stats",
"tikv-jemallocator/stats", "tikv-jemallocator/stats",
] ]
jemalloc_conf = []
hardened_malloc = [ hardened_malloc = [
"dep:hardened_malloc-rs" "dep:hardened_malloc-rs"
] ]

View file

@ -1,7 +1,7 @@
//! Default allocator with no special features //! Default allocator with no special features
/// Always returns Ok /// Always returns Ok
pub fn trim() -> crate::Result { Ok(()) } pub fn trim<I: Into<Option<usize>>>(_: I) -> crate::Result { Ok(()) }
/// Always returns None /// Always returns None
#[must_use] #[must_use]

View file

@ -3,7 +3,7 @@
#[global_allocator] #[global_allocator]
static HMALLOC: hardened_malloc_rs::HardenedMalloc = hardened_malloc_rs::HardenedMalloc; static HMALLOC: hardened_malloc_rs::HardenedMalloc = hardened_malloc_rs::HardenedMalloc;
pub fn trim() -> crate::Result { Ok(()) } pub fn trim<I: Into<Option<usize>>>(_: I) -> crate::Result { Ok(()) }
#[must_use] #[must_use]
//TODO: get usage //TODO: get usage

View file

@ -2,8 +2,9 @@
use std::{ use std::{
cell::OnceCell, cell::OnceCell,
ffi::{c_char, c_void}, ffi::{c_char, c_void, CStr},
fmt::Debug, fmt::Debug,
sync::RwLock,
}; };
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
@ -11,10 +12,14 @@ use tikv_jemalloc_ctl as mallctl;
use tikv_jemalloc_sys as ffi; use tikv_jemalloc_sys as ffi;
use tikv_jemallocator as jemalloc; use tikv_jemallocator as jemalloc;
use crate::{err, is_equal_to, utils::math::Tried, Result}; use crate::{
err, is_equal_to, is_nonzero,
utils::{math, math::Tried},
Result,
};
#[cfg(feature = "jemalloc_conf")] #[cfg(feature = "jemalloc_conf")]
#[no_mangle] #[unsafe(no_mangle)]
pub static malloc_conf: &[u8] = b"\ pub static malloc_conf: &[u8] = b"\
metadata_thp:always\ metadata_thp:always\
,percpu_arena:percpu\ ,percpu_arena:percpu\
@ -22,19 +27,26 @@ metadata_thp:always\
,max_background_threads:-1\ ,max_background_threads:-1\
,lg_extent_max_active_fit:4\ ,lg_extent_max_active_fit:4\
,oversize_threshold:33554432\ ,oversize_threshold:33554432\
,tcache_max:2097152\ ,tcache_max:1048576\
,dirty_decay_ms:16000\ ,dirty_decay_ms:16000\
,muzzy_decay_ms:144000\ ,muzzy_decay_ms:144000\
\0"; \0";
#[global_allocator] #[global_allocator]
static JEMALLOC: jemalloc::Jemalloc = jemalloc::Jemalloc; static JEMALLOC: jemalloc::Jemalloc = jemalloc::Jemalloc;
static CONTROL: RwLock<()> = RwLock::new(());
type Key = ArrayVec<usize, KEY_SEGS>;
type Name = ArrayVec<u8, NAME_MAX>; type Name = ArrayVec<u8, NAME_MAX>;
type Key = ArrayVec<usize, KEY_SEGS>;
const KEY_SEGS: usize = 8;
const NAME_MAX: usize = 128; const NAME_MAX: usize = 128;
const KEY_SEGS: usize = 8;
#[crate::ctor]
fn _static_initialization() {
acq_epoch().expect("pre-initialization of jemalloc failed");
acq_epoch().expect("pre-initialization of jemalloc failed");
}
#[must_use] #[must_use]
#[cfg(feature = "jemalloc_stats")] #[cfg(feature = "jemalloc_stats")]
@ -49,6 +61,9 @@ pub fn memory_usage() -> Option<String> {
kibs / 1024.0 kibs / 1024.0
}; };
// Acquire the epoch; ensure latest stats are pulled in
acq_epoch().ok()?;
let allocated = mibs(stats::allocated::read()); let allocated = mibs(stats::allocated::read());
let active = mibs(stats::active::read()); let active = mibs(stats::active::read());
let mapped = mibs(stats::mapped::read()); let mapped = mibs(stats::mapped::read());
@ -76,6 +91,9 @@ pub fn memory_stats(opts: &str) -> Option<String> {
.into_raw() .into_raw()
.cast_const(); .cast_const();
// Acquire the epoch; ensure latest stats are pulled in
acq_epoch().ok()?;
// SAFETY: calls malloc_stats_print() with our string instance which must remain // SAFETY: calls malloc_stats_print() with our string instance which must remain
// in this frame. https://docs.rs/tikv-jemalloc-sys/latest/tikv_jemalloc_sys/fn.malloc_stats_print.html // in this frame. https://docs.rs/tikv-jemalloc-sys/latest/tikv_jemalloc_sys/fn.malloc_stats_print.html
unsafe { ffi::malloc_stats_print(Some(malloc_stats_cb), opaque, opts_p) }; unsafe { ffi::malloc_stats_print(Some(malloc_stats_cb), opaque, opts_p) };
@ -95,7 +113,7 @@ unsafe extern "C" fn malloc_stats_cb(opaque: *mut c_void, msg: *const c_char) {
}; };
// SAFETY: we have to trust the string is null terminated. // SAFETY: we have to trust the string is null terminated.
let msg = unsafe { std::ffi::CStr::from_ptr(msg) }; let msg = unsafe { CStr::from_ptr(msg) };
let msg = String::from_utf8_lossy(msg.to_bytes()); let msg = String::from_utf8_lossy(msg.to_bytes());
res.push_str(msg.as_ref()); res.push_str(msg.as_ref());
@ -114,58 +132,168 @@ macro_rules! mallctl {
}}; }};
} }
pub fn trim() -> Result { set(&mallctl!("arena.4096.purge"), ()) }
pub fn decay() -> Result { set(&mallctl!("arena.4096.purge"), ()) }
pub fn set_by_name<T: Copy + Debug>(name: &str, val: T) -> Result { set(&key(name)?, val) }
pub fn get_by_name<T: Copy + Debug>(name: &str) -> Result<T> { get(&key(name)?) }
pub mod this_thread { pub mod this_thread {
use super::{get, key, set, Key, OnceCell, Result}; use super::{is_nonzero, key, math, Debug, Key, OnceCell, Result};
pub fn trim() -> Result { pub fn trim() -> Result { notify(mallctl!("arena.0.purge")) }
let mut key = mallctl!("arena.0.purge");
key[1] = arena_id()?.try_into()?; pub fn decay() -> Result { notify(mallctl!("arena.0.decay")) }
set(&key, ())
pub fn flush() -> Result { super::notify(&mallctl!("thread.tcache.flush")) }
pub fn set_muzzy_decay(decay_ms: isize) -> Result<isize> {
set(mallctl!("arena.0.muzzy_decay_ms"), decay_ms)
} }
pub fn decay() -> Result { pub fn get_muzzy_decay() -> Result<isize> { get(mallctl!("arena.0.muzzy_decay_ms")) }
let mut key = mallctl!("arena.0.decay");
key[1] = arena_id()?.try_into()?; pub fn set_dirty_decay(decay_ms: isize) -> Result<isize> {
set(&key, ()) set(mallctl!("arena.0.dirty_decay_ms"), decay_ms)
} }
pub fn cache(enable: bool) -> Result { pub fn get_dirty_decay() -> Result<isize> { get(mallctl!("arena.0.dirty_decay_ms")) }
set(&mallctl!("thread.tcache.enabled"), u8::from(enable))
pub fn enable_cache(enable: bool) -> Result<bool> {
super::set::<u8>(&mallctl!("thread.tcache.enabled"), enable.into()).map(is_nonzero!())
} }
pub fn flush() -> Result { set(&mallctl!("thread.tcache.flush"), ()) } pub fn is_cache_enabled() -> Result<bool> {
super::get::<u8>(&mallctl!("thread.tcache.enabled")).map(is_nonzero!())
}
pub fn allocated() -> Result<u64> { get::<u64>(&mallctl!("thread.allocated")) } pub fn set_arena(id: usize) -> Result<usize> {
super::set::<u32>(&mallctl!("thread.arena"), id.try_into()?).and_then(math::try_into)
}
pub fn deallocated() -> Result<u64> { get::<u64>(&mallctl!("thread.deallocated")) } pub fn arena_id() -> Result<usize> {
super::get::<u32>(&mallctl!("thread.arena")).and_then(math::try_into)
}
pub fn arena_id() -> Result<u32> { get::<u32>(&mallctl!("thread.arena")) } pub fn allocated() -> Result<u64> { super::get(&mallctl!("thread.allocated")) }
pub fn deallocated() -> Result<u64> { super::get(&mallctl!("thread.deallocated")) }
fn notify(key: Key) -> Result { super::notify_by_arena(Some(arena_id()?), key) }
fn set<T>(key: Key, val: T) -> Result<T>
where
T: Copy + Debug,
{
super::set_by_arena(Some(arena_id()?), key, val)
}
fn get<T>(key: Key) -> Result<T>
where
T: Copy + Debug,
{
super::get_by_arena(Some(arena_id()?), key)
}
} }
fn set<T>(key: &Key, val: T) -> Result pub fn trim<I: Into<Option<usize>>>(arena: I) -> Result {
notify_by_arena(arena.into(), mallctl!("arena.4096.purge"))
}
pub fn decay<I: Into<Option<usize>>>(arena: I) -> Result {
notify_by_arena(arena.into(), mallctl!("arena.4096.decay"))
}
pub fn set_muzzy_decay<I: Into<Option<usize>>>(arena: I, decay_ms: isize) -> Result<isize> {
if let Some(arena) = arena.into() {
set_by_arena(Some(arena), mallctl!("arena.4096.muzzy_decay_ms"), decay_ms)
} else {
set(&mallctl!("arenas.muzzy_decay_ms"), decay_ms)
}
}
pub fn set_dirty_decay<I: Into<Option<usize>>>(arena: I, decay_ms: isize) -> Result<isize> {
if let Some(arena) = arena.into() {
set_by_arena(Some(arena), mallctl!("arena.4096.dirty_decay_ms"), decay_ms)
} else {
set(&mallctl!("arenas.dirty_decay_ms"), decay_ms)
}
}
#[inline]
#[must_use]
pub fn is_affine_arena() -> bool { is_percpu_arena() || is_phycpu_arena() }
#[inline]
#[must_use]
pub fn is_percpu_arena() -> bool { percpu_arenas().is_ok_and(is_equal_to!("percpu")) }
#[inline]
#[must_use]
pub fn is_phycpu_arena() -> bool { percpu_arenas().is_ok_and(is_equal_to!("phycpu")) }
pub fn percpu_arenas() -> Result<&'static str> {
let ptr = get::<*const c_char>(&mallctl!("opt.percpu_arena"))?;
//SAFETY: ptr points to a null-terminated string returned for opt.percpu_arena.
let cstr = unsafe { CStr::from_ptr(ptr) };
cstr.to_str().map_err(Into::into)
}
pub fn arenas() -> Result<usize> {
get::<u32>(&mallctl!("arenas.narenas")).and_then(math::try_into)
}
pub fn inc_epoch() -> Result<u64> { xchg(&mallctl!("epoch"), 1_u64) }
pub fn acq_epoch() -> Result<u64> { xchg(&mallctl!("epoch"), 0_u64) }
fn notify_by_arena(id: Option<usize>, mut key: Key) -> Result {
key[1] = id.unwrap_or(4096);
notify(&key)
}
fn set_by_arena<T>(id: Option<usize>, mut key: Key, val: T) -> Result<T>
where where
T: Copy + Debug, T: Copy + Debug,
{ {
// SAFETY: T must be the exact expected type. key[1] = id.unwrap_or(4096);
unsafe { mallctl::raw::write_mib(key.as_slice(), val) }.map_err(map_err) set(&key, val)
}
fn get_by_arena<T>(id: Option<usize>, mut key: Key) -> Result<T>
where
T: Copy + Debug,
{
key[1] = id.unwrap_or(4096);
get(&key)
}
fn notify(key: &Key) -> Result { xchg(key, ()) }
fn set<T>(key: &Key, val: T) -> Result<T>
where
T: Copy + Debug,
{
let _lock = CONTROL.write()?;
let res = xchg(key, val)?;
inc_epoch()?;
Ok(res)
} }
fn get<T>(key: &Key) -> Result<T> fn get<T>(key: &Key) -> Result<T>
where where
T: Copy + Debug, T: Copy + Debug,
{ {
acq_epoch()?;
acq_epoch()?;
// SAFETY: T must be perfectly valid to receive value. // SAFETY: T must be perfectly valid to receive value.
unsafe { mallctl::raw::read_mib(key.as_slice()) }.map_err(map_err) unsafe { mallctl::raw::read_mib(key.as_slice()) }.map_err(map_err)
} }
fn xchg<T>(key: &Key, val: T) -> Result<T>
where
T: Copy + Debug,
{
// SAFETY: T must be the exact expected type.
unsafe { mallctl::raw::update_mib(key.as_slice(), val) }.map_err(map_err)
}
fn key(name: &str) -> Result<Key> { fn key(name: &str) -> Result<Key> {
// tikv asserts the output buffer length is tight to the number of required mibs // tikv asserts the output buffer length is tight to the number of required mibs
// so we slice that down here. // so we slice that down here.

View file

@ -13,7 +13,7 @@ use std::{
use async_channel::{QueueStrategy, Receiver, RecvError, Sender}; use async_channel::{QueueStrategy, Receiver, RecvError, Sender};
use conduwuit::{ use conduwuit::{
debug, debug_warn, err, error, implement, debug, debug_warn, err, error, implement,
result::DebugInspect, result::{DebugInspect, LogDebugErr},
trace, trace,
utils::sys::compute::{get_affinity, nth_core_available, set_affinity}, utils::sys::compute::{get_affinity, nth_core_available, set_affinity},
Error, Result, Server, Error, Result, Server,
@ -289,6 +289,20 @@ fn worker_init(&self, id: usize) {
// affinity is empty (no-op) if there's only one queue // affinity is empty (no-op) if there's only one queue
set_affinity(affinity.clone()); set_affinity(affinity.clone());
#[cfg(feature = "jemalloc")]
if affinity.clone().count() == 1 && conduwuit::alloc::je::is_affine_arena() {
use conduwuit::alloc::je::this_thread::{arena_id, set_arena};
let id = affinity.clone().next().expect("at least one id");
if let Ok(arena) = arena_id() {
if arena != id {
set_arena(id).log_debug_err().ok();
}
}
}
debug!( debug!(
?group, ?group,
affinity = ?affinity.collect::<Vec<_>>(), affinity = ?affinity.collect::<Vec<_>>(),

View file

@ -41,6 +41,7 @@ default = [
"gzip_compression", "gzip_compression",
"io_uring", "io_uring",
"jemalloc", "jemalloc",
"jemalloc_conf",
"media_thumbnail", "media_thumbnail",
"release_max_log_level", "release_max_log_level",
"systemd", "systemd",
@ -85,6 +86,9 @@ jemalloc_prof = [
jemalloc_stats = [ jemalloc_stats = [
"conduwuit-core/jemalloc_stats", "conduwuit-core/jemalloc_stats",
] ]
jemalloc_conf = [
"conduwuit-core/jemalloc_conf",
]
media_thumbnail = [ media_thumbnail = [
"conduwuit-service/media_thumbnail", "conduwuit-service/media_thumbnail",
] ]

View file

@ -92,6 +92,22 @@ pub(crate) struct Args {
require_equals(false), require_equals(false),
)] )]
pub(crate) gc_on_park: Option<bool>, pub(crate) gc_on_park: Option<bool>,
/// Toggles muzzy decay for jemalloc arenas associated with a tokio
/// worker (when worker-affinity is enabled). Setting to false releases
/// memory to the operating system using MADV_FREE without MADV_DONTNEED.
/// Setting to false increases performance by reducing pagefaults, but
/// resident memory usage appears high until there is memory pressure. The
/// default is true unless the system has four or more cores.
#[arg(
long,
hide(true),
env = "CONDUWUIT_RUNTIME_GC_MUZZY",
action = ArgAction::Set,
num_args = 0..=1,
require_equals(false),
)]
pub(crate) gc_muzzy: Option<bool>,
} }
/// Parse commandline arguments into structured data /// Parse commandline arguments into structured data

View file

@ -9,8 +9,12 @@ use std::{
}; };
use conduwuit::{ use conduwuit::{
result::LogErr, is_true,
utils::sys::compute::{nth_core_available, set_affinity}, result::LogDebugErr,
utils::{
available_parallelism,
sys::compute::{nth_core_available, set_affinity},
},
Result, Result,
}; };
use tokio::runtime::Builder; use tokio::runtime::Builder;
@ -21,9 +25,11 @@ const WORKER_NAME: &str = "conduwuit:worker";
const WORKER_MIN: usize = 2; const WORKER_MIN: usize = 2;
const WORKER_KEEPALIVE: u64 = 36; const WORKER_KEEPALIVE: u64 = 36;
const MAX_BLOCKING_THREADS: usize = 1024; const MAX_BLOCKING_THREADS: usize = 1024;
const DISABLE_MUZZY_THRESHOLD: usize = 4;
static WORKER_AFFINITY: OnceLock<bool> = OnceLock::new(); static WORKER_AFFINITY: OnceLock<bool> = OnceLock::new();
static GC_ON_PARK: OnceLock<Option<bool>> = OnceLock::new(); static GC_ON_PARK: OnceLock<Option<bool>> = OnceLock::new();
static GC_MUZZY: OnceLock<Option<bool>> = OnceLock::new();
pub(super) fn new(args: &Args) -> Result<tokio::runtime::Runtime> { pub(super) fn new(args: &Args) -> Result<tokio::runtime::Runtime> {
WORKER_AFFINITY WORKER_AFFINITY
@ -34,6 +40,10 @@ pub(super) fn new(args: &Args) -> Result<tokio::runtime::Runtime> {
.set(args.gc_on_park) .set(args.gc_on_park)
.expect("set GC_ON_PARK from program argument"); .expect("set GC_ON_PARK from program argument");
GC_MUZZY
.set(args.gc_muzzy)
.expect("set GC_MUZZY from program argument");
let mut builder = Builder::new_multi_thread(); let mut builder = Builder::new_multi_thread();
builder builder
.enable_io() .enable_io()
@ -83,11 +93,13 @@ fn enable_histogram(builder: &mut Builder, args: &Args) {
), ),
)] )]
fn thread_start() { fn thread_start() {
if WORKER_AFFINITY debug_assert_eq!(
.get() Some(WORKER_NAME),
.copied() thread::current().name(),
.expect("WORKER_AFFINITY initialized by runtime::new()") "tokio worker name mismatch at thread start"
{ );
if WORKER_AFFINITY.get().is_some_and(is_true!()) {
set_worker_affinity(); set_worker_affinity();
} }
} }
@ -95,10 +107,6 @@ fn thread_start() {
fn set_worker_affinity() { fn set_worker_affinity() {
static CORES_OCCUPIED: AtomicUsize = AtomicUsize::new(0); static CORES_OCCUPIED: AtomicUsize = AtomicUsize::new(0);
if thread::current().name() != Some(WORKER_NAME) {
return;
}
let handle = tokio::runtime::Handle::current(); let handle = tokio::runtime::Handle::current();
let num_workers = handle.metrics().num_workers(); let num_workers = handle.metrics().num_workers();
let i = CORES_OCCUPIED.fetch_add(1, Ordering::Relaxed); let i = CORES_OCCUPIED.fetch_add(1, Ordering::Relaxed);
@ -111,8 +119,33 @@ fn set_worker_affinity() {
}; };
set_affinity(once(id)); set_affinity(once(id));
set_worker_mallctl(id);
} }
#[cfg(feature = "jemalloc")]
fn set_worker_mallctl(id: usize) {
use conduwuit::alloc::je::{
is_affine_arena,
this_thread::{set_arena, set_muzzy_decay},
};
if is_affine_arena() {
set_arena(id).log_debug_err().ok();
}
let muzzy_option = GC_MUZZY
.get()
.expect("GC_MUZZY initialized by runtime::new()");
let muzzy_auto_disable = available_parallelism() >= DISABLE_MUZZY_THRESHOLD;
if matches!(muzzy_option, Some(false) | None if muzzy_auto_disable) {
set_muzzy_decay(-1).log_debug_err().ok();
}
}
#[cfg(not(feature = "jemalloc"))]
fn set_worker_mallctl(_: usize) {}
#[tracing::instrument( #[tracing::instrument(
name = "join", name = "join",
level = "debug", level = "debug",
@ -157,7 +190,9 @@ fn thread_park() {
fn gc_on_park() { fn gc_on_park() {
#[cfg(feature = "jemalloc")] #[cfg(feature = "jemalloc")]
conduwuit::alloc::je::this_thread::decay().log_err().ok(); conduwuit::alloc::je::this_thread::decay()
.log_debug_err()
.ok();
} }
#[cfg(tokio_unstable)] #[cfg(tokio_unstable)]