From 3a1020526d1675148823bc32896d25789febda98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sun, 17 Sep 2023 14:33:45 +0200 Subject: [PATCH] perf: bring `musl` binaries performance in par with `glibc` ones After profiling, I have confirmed that the cause of the relative slowdown of `musl` binaries with respect to `glibc` was the usage of a slower memory allocator in the `musl` case, which critically affects the performance of Zopfli compression, especially on CPUs with lots of physical threads. --- CHANGELOG.md | 12 +++++++++++ Cargo.lock | 20 +++++++++++++++++++ packages/packsquash_cli/Cargo.toml | 9 +++++++++ .../packsquash_cli/src/bin/packsquash/main.rs | 9 +++++++++ 4 files changed, 50 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a702d65..a8b75402a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,18 @@ Versioning](https://semver.org/spec/v2.0.0.html). to their decentralized and free nature, the PackSquash project will most likely not pursue signing binaries with code signing certificates. +#### Performance + +- The statically-linked PackSquash CLI Linux binaries now use the + [`mimalloc`](https://github.com/microsoft/mimalloc) memory allocator, instead + of the default `musl` C library allocator. + - This brings the performance of such binaries roughly in line with those + packaged for Debian, which dynamically link against `glibc`. Previously, + slowdowns of 5x or more could be expected, depending on the number of + threads used. + - Because they use statically-linked binaries, the PackSquash GitHub + Action and Docker container also saw performance improvements. + #### Fixed - The UTF-8 BOM is no longer automatically stripped from properties files, as diff --git a/Cargo.lock b/Cargo.lock index b188ba2f4..e65fe4edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1345,6 +1345,16 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "libmimalloc-sys" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "libz-sys" version = "1.1.12" @@ -1429,6 +1439,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mimalloc" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" +dependencies = [ + "libmimalloc-sys", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1688,6 +1707,7 @@ dependencies = [ "env_logger", "getopts", "log", + "mimalloc", "packsquash", "serde_path_to_error", "tokio", diff --git a/packages/packsquash_cli/Cargo.toml b/packages/packsquash_cli/Cargo.toml index 854322149..f858069e3 100644 --- a/packages/packsquash_cli/Cargo.toml +++ b/packages/packsquash_cli/Cargo.toml @@ -26,6 +26,15 @@ env_logger = { version = "0.10.0", default-features = false, features = ["color" winapi = { version = "0.3.9", default-features = false, features = ["consoleapi"] } winapi-util = "0.1.5" +[target.'cfg(all(target_os = "linux", target_env = "musl"))'.dependencies] +# The mimalloc C malloc override feature is troublesome on macOS and Windows, but +# luckily the Linux musl C library should support this well. See: +# https://github.com/purpleprotocol/mimalloc_rust/issues/41 +# We are interested in overriding malloc because we depend on C libraries, and doing +# so buys us a safety net against mismatching memory managed by different allocators, +# in addition to keep binary sizes down +mimalloc = { version = "0.1.39", default-features = false, features = ["override"] } + [target.'cfg(windows)'.build-dependencies] winresource = "0.1.17" diff --git a/packages/packsquash_cli/src/bin/packsquash/main.rs b/packages/packsquash_cli/src/bin/packsquash/main.rs index 32e6aa814..03c5cda33 100644 --- a/packages/packsquash_cli/src/bin/packsquash/main.rs +++ b/packages/packsquash_cli/src/bin/packsquash/main.rs @@ -30,6 +30,15 @@ const LOG_TARGET: Target = Target::Stderr; /// A producer of the [`IsTerminal`] implementation that matches the [`LOG_TARGET`] constant. const LOG_TARGET_STREAM: fn() -> Stderr = io::stderr; +/// Zopfli compression is pretty intensive on the memory allocator, and musl +/// is known to use a simple but prone to extreme thread contention allocator. +/// Replace its allocator with one whose performance is close to glibc so that +/// the PackSquash CLI musl binaries have a similar performance to glibc +/// binaries; otherwise, slowdowns of 5x or more are to be expected. +#[cfg(all(target_os = "linux", target_env = "musl"))] +#[global_allocator] +static GLOBAL_ALLOCATOR: mimalloc::MiMalloc = mimalloc::MiMalloc; + fn main() { process::exit(run(TerminalTitleController::new())); }