From: Dietmar Maurer Date: Wed, 26 Jan 2022 06:10:59 +0000 (+0100) Subject: Set MMAP_THRESHOLD to a fixed value (128K) X-Git-Tag: v2.2.1~446 X-Git-Url: https://git.proxmox.com/?p=proxmox-backup.git;a=commitdiff_plain;h=d91a0f9fc90aecabc4f359d968f716a14562ce78 Set MMAP_THRESHOLD to a fixed value (128K) glibc's malloc has a misguided heuristic to detect transient allocations that will just result in allocation sizes below 32 MiB never using mmap. That it turn means that those relatively big allocations are on the heap where cleanup and returning memory to the OS is harder to do and easier to be blocked by long living, small allocations at the top (end) of the heap. Observing the malloc size distribution in a file-level backup run: @size: [0] 14 | | [1] 25214 |@@@@@ | [2, 4) 9090 |@ | [4, 8) 12987 |@@ | [8, 16) 93453 |@@@@@@@@@@@@@@@@@@@@ | [16, 32) 30255 |@@@@@@ | [32, 64) 237445 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 32692 |@@@@@@@ | [128, 256) 22296 |@@@@ | [256, 512) 16177 |@@@ | [512, 1K) 5139 |@ | [1K, 2K) 3352 | | [2K, 4K) 214 | | [4K, 8K) 1568 | | [8K, 16K) 95 | | [16K, 32K) 3457 | | [32K, 64K) 3175 | | [64K, 128K) 161 | | [128K, 256K) 453 | | [256K, 512K) 93 | | [512K, 1M) 74 | | [1M, 2M) 774 | | [2M, 4M) 319 | | [4M, 8M) 700 | | [8M, 16M) 93 | | [16M, 32M) 18 | | We see that all allocations will be on the heap, and that while most allocations are small, the relatively few big ones will still make up most of the RSS and if blocked from being released back to the OS result in much higher peak and average usage for the program than actually required. Avoiding the "dynamic" mmap-threshold increasement algorithm and fixing it at the original default of 128 KiB reduces RSS size by factor 10-20 when running backups. As with memory mappings other mappings or the heap can never block freeing the memory fully back to the OS. But, the drawback of using mmap is more wasted space for unaligned or small allocation sizes, and the fact that the kernel allegedly zeros out the data before giving it to user space. The former doesn't really matter for us when using it only for allocations bigger than 128 KiB, and the latter is a trade-off, using 10 to 20 times less memory brings its own performance improvement possibilities for the whole system after all ;-) Signed-off-by: Dietmar Maurer [ Thomas: added to comment & commit message + extra-empty-line fixes ] Signed-off-by: Thomas Lamprecht --- diff --git a/pbs-tools/src/lib.rs b/pbs-tools/src/lib.rs index 053b3c0c..939ba7e6 100644 --- a/pbs-tools/src/lib.rs +++ b/pbs-tools/src/lib.rs @@ -8,3 +8,22 @@ pub mod sha; pub mod ticket; pub mod async_lru_cache; + +/// Set MMAP_THRESHOLD to a fixed value (128 KiB) +/// +/// This avoids the "dynamic" mmap-treshold logic from glibc's malloc, which seems misguided and +/// effectively avoids using mmap for all allocations smaller than 32 MiB. Which, in combination +/// with the allocation pattern from our/tokio's complex async machinery, resulted in very large +/// RSS sizes due to defragmentation and long-living (smaller) allocation on top of the heap +/// avoiding that the (big) now free'd allocations below couldn't get given back to the OS. This is +/// not an issue with mmap'd memeory chunks, those can be given back at any time. +/// +/// Lowering effective MMAP treshold to 128 KiB allows freeing up memory to the OS better and with +/// lower latency, which reduces the peak *and* average RSS size by an order of magnitude when +/// running backup jobs. We measured a reduction by a factor of 10-20 in experiments and see much +/// less erratic behavior in the overall's runtime RSS size. +pub fn setup_libc_malloc_opts() { + unsafe { + libc::mallopt(libc::M_MMAP_THRESHOLD, 4096*32); + } +} diff --git a/proxmox-backup-client/src/main.rs b/proxmox-backup-client/src/main.rs index 50682637..406f2d18 100644 --- a/proxmox-backup-client/src/main.rs +++ b/proxmox-backup-client/src/main.rs @@ -1464,6 +1464,7 @@ impl ReadAt for BufferedDynamicReadAt { } fn main() { + pbs_tools::setup_libc_malloc_opts(); let backup_cmd_def = CliCommand::new(&API_METHOD_CREATE_BACKUP) .arg_param(&["backupspec"]) diff --git a/proxmox-restore-daemon/src/main.rs b/proxmox-restore-daemon/src/main.rs index c2be942b..47790a7d 100644 --- a/proxmox-restore-daemon/src/main.rs +++ b/proxmox-restore-daemon/src/main.rs @@ -38,6 +38,8 @@ lazy_static! { /// This is expected to be run by 'proxmox-file-restore' within a mini-VM fn main() -> Result<(), Error> { + pbs_tools::setup_libc_malloc_opts(); + if !Path::new(VM_DETECT_FILE).exists() { bail!( "This binary is not supposed to be run manually, use 'proxmox-file-restore' instead." diff --git a/src/bin/proxmox-backup-api.rs b/src/bin/proxmox-backup-api.rs index e6fc5f23..ee037a3b 100644 --- a/src/bin/proxmox-backup-api.rs +++ b/src/bin/proxmox-backup-api.rs @@ -19,6 +19,8 @@ use proxmox_backup::auth_helpers::*; use proxmox_backup::config; fn main() { + pbs_tools::setup_libc_malloc_opts(); + proxmox_backup::tools::setup_safe_path_env(); if let Err(err) = proxmox_async::runtime::main(run()) { diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs index 523966cf..30b730ef 100644 --- a/src/bin/proxmox-backup-proxy.rs +++ b/src/bin/proxmox-backup-proxy.rs @@ -73,6 +73,8 @@ use proxmox_backup::server::do_verification_job; use proxmox_backup::server::do_prune_job; fn main() -> Result<(), Error> { + pbs_tools::setup_libc_malloc_opts(); + proxmox_backup::tools::setup_safe_path_env(); let backup_uid = pbs_config::backup_user()?.uid;