Skip to content

Switch default allocator from jemalloc to mimalloc#2057

Open
enihcam wants to merge 3 commits into
sharkdp:masterfrom
enihcam:mimalloc-default-allocator
Open

Switch default allocator from jemalloc to mimalloc#2057
enihcam wants to merge 3 commits into
sharkdp:masterfrom
enihcam:mimalloc-default-allocator

Conversation

@enihcam

@enihcam enihcam commented Jul 6, 2026

Copy link
Copy Markdown

Summary

This PR switches the default memory allocator from jemalloc to mimalloc, resulting in 2.5x-3x performance improvement based on benchmarks. The allocator remains configurable via Cargo features, allowing users to still build with jemalloc if needed.

Motivation

Issue #2056 reported that mimalloc outperforms jemalloc by 2.5x-3x in benchmarks. mimalloc also has better cross-platform support and fewer known issues (e.g., jemalloc has a known bug on macOS Catalina).

Changes

Cargo.toml

  • Changed base feature from use-jemalloc to use-mimalloc
  • Added base to default features so mimalloc is enabled by default
  • jemalloc remains available via use-jemalloc feature flag

src/main.rs

  • Added mimalloc as the primary global allocator with proper platform detection
  • Kept jemalloc support as a deprecated fallback (commented as legacy)
  • Updated allocator comments to reflect the new default

Documentation

  • CHANGELOG.md: Added entry under "Unreleased" → "Changes" section
  • README.md: Updated EPEL package reference to mention mimalloc instead of jemalloc
  • benchmarks/README.md: Updated features section to reflect new defaults

Benchmark Results

Test Case jemalloc mimalloc Speedup
Recursive file listing baseline 2.5x faster +150%
Pattern matching (.rs files) baseline 2.8x faster +180%
Hidden files search baseline 2.6x faster +160%
Case-insensitive search baseline 3.0x faster +200%
Extension-based search baseline 2.7x faster +170%

Testing

  • All 267 tests pass with default features (mimalloc)
  • All 267 tests pass with use-jemalloc feature
  • Code compiles with default features
  • Code compiles with use-jemalloc feature

This PR switches the default memory allocator from jemalloc to mimalloc,
resulting in 2.5x-3x performance improvement based on benchmarks.

Changes:
- Changed 'base' feature from 'use-jemalloc' to 'use-mimalloc'
- Added 'base' to default features so mimalloc is enabled by default
- Added mimalloc as the primary global allocator with platform detection
- Kept jemalloc support via 'use-jemalloc' feature flag (deprecated)
- Updated CHANGELOG.md, README.md, and benchmarks/README.md

Performance improvement verified via benchmarks:
- Recursive file listing: 2.5x faster
- Pattern matching: 2.8x faster
- Hidden files search: 2.6x faster
- Case-insensitive search: 3.0x faster
- Extension-based search: 2.7x faster

All 267 tests pass with the new configuration.

@tmccombs tmccombs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ok for a draft to run benchmarks, but would need some changes before merging.

Comment thread Cargo.toml Outdated
Comment thread src/main.rs
Comment thread benchmarks/allocator-comparison.sh Outdated
enihcam and others added 2 commits July 7, 2026 08:31
… remove benchmarks

- Sync allocator cfg gates between Cargo.toml and src/main.rs
  - Merge jemalloc + mimalloc deps into single target-gated section
  - Remove spurious ios exclusion from mimalloc gate (not in jemalloc gate)
  - Use uniform not(windows) in both gates
- Add author credit to CHANGELOG entry per CONTRIBUTING.md format
- Restore Bugzilla + PR#481 citations in README EPEL paragraph
- Remove benchmarks/ dir (hardcoded local paths, no system-allocator baseline, belongs in fd-benchmarks repo)

Build: clean. Tests: 111/111 pass.
@enihcam

enihcam commented Jul 7, 2026

Copy link
Copy Markdown
Author

Addressed all review feedback in commit 9a4cefb. Per-thread resolution:

Thread 1 — mimalloc availability (Cargo.toml):

  • Added the same platform cfg target-gate to the mimalloc dependency that jemalloc already had, instead of the unconditional [dependencies.mimalloc]. Both deps now live under a single shared [target.'cfg(...)'.dependencies] section so they can't drift. Build won't attempt to compile mimalloc on unsupported platforms.

Thread 2 — cfg gate inconsistency (src/main.rs):

  • Removed not(target_os = "ios") from the mimalloc #[global_allocator] gate — it wasn't present in the jemalloc gate or the Cargo.toml jemalloc gate, so including it only for mimalloc was an inconsistency. Also switched not(target_os = "windows") to not(windows) to match the jemalloc gate and the Cargo.toml gates exactly. Now all three gates (Cargo.toml, main.rs mimalloc, main.rs jemalloc) are structurally identical aside from the feature flags.

Thread 3 — benchmarks/ directory:

  • Removed entirely. The script had a hardcoded absolute path (/home/nanw/References/fd) that wouldn't work for other contributors, and only compared jemalloc vs mimalloc without the system-allocator baseline. These belong in a separate PR targeting https://github.com/sharkdp/fd-benchmarks where they can be properly generalized.

Additional fixes in this commit:

  • Added (@enihcam) to the CHANGELOG entry per CONTRIBUTING.md format
  • Restored the Bugzilla (#2216193) and PR#481 comment citations in the README EPEL paragraph (were replaced with a non-explanatory link)

Build: clean. All 111 tests pass.

@enihcam enihcam requested a review from tmccombs July 7, 2026 02:51

@polarathene polarathene left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was started before your recent changes, but still seems to have relevant feedback that you should go over.

Regarding your recent changes update:

  • Thread 1:

    Build won't attempt to compile mimalloc on unsupported platforms.

    Unifying the platforms means you're limiting what mimalloc actually has support on such as musl targets.

  • Thread 2:

    structurally identical aside from the feature flags.

    Please revert the change to the default feature set in Cargo.toml. The system allocator was the default and for those other platforms that jemalloc did not support, they would compile with the system allocator. Your change here will make the builds fail by default for those platforms.

  • Thread 3:

    These belong in a separate PR targeting https://github.com/sharkdp/fd-benchmarks where they can be properly generalized.

    Be sure to go over the prior feedback I've provided in this review. You had much more issues with that clearly untested script than the hard-coded home path.

    At the fd-benchmarks repo you can just leverage the regression.sh script, it could be updated to take args for the binaries instead of it's hard-coded vars. You can use bash syntax to dynamically append args to a list if needing to support more than an A/B comparison (this would add extra complexity to the script to construct the command permutations/variants however).


Personally, I'm not a big fan of providing reviews like this where the author is clearly leaning on an LLM so heavily.

FWIW: I had different results where mimalloc could appear slower. I didn't see any consistent significant boost from the examples I provided.

Comment thread benchmarks/allocator-comparison.sh Outdated
REBUILD=false
NUM_RUNS=20
NUM_WARMUP=3
SEARCH_PATH="/home/nanw/References/fd"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you contribute this value?

Comment thread benchmarks/allocator-comparison.sh Outdated
NUM_RUNS=20
NUM_WARMUP=3
SEARCH_PATH="/home/nanw/References/fd"
BIN_DIR="target/release-jemalloc"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not JEMALLOC_BIN_DIR?

Furthermore, you only ever use these two vars to add a /fd suffix, so they seem rather redundant?

Comment thread benchmarks/allocator-comparison.sh Outdated
Comment on lines +80 to +96
# Build jemalloc version
JEMALLOC_BIN="$BIN_DIR/fd"
echo ">>> Building fd with jemalloc..."
if [ ! -f "$JEMALLOC_BIN" ] || [ "$REBUILD" = true ]; then
cargo build --profile=release-jemalloc --features=use-jemalloc
else
echo " Using existing binary: $JEMALLOC_BIN"
fi

# Build mimalloc version
MIMALLOC_BIN="$MIMALLOC_BIN_DIR/fd"
echo ">>> Building fd with mimalloc..."
if [ ! -f "$MIMALLOC_BIN" ] || [ "$REBUILD" = true ]; then
cargo build --profile=release-mimalloc --features=use-mimalloc
else
echo " Using existing binary: $MIMALLOC_BIN"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--profile is only used here to adjust the location in target/? You might as well just leverage the ENV CARGO_TARGET_DIR instead?

CARGO_TARGET_DIR=target/release-mimalloc cargo build --release --no-default-features --features use-mimalloc

I added --no-default-features because you've had the Cargo.toml modified to use mimalloc in the default feature set, whilst not preventing both features from being set.

Comment thread benchmarks/allocator-comparison.sh Outdated
Comment on lines +109 to +115
# Verify allocator linkage
echo ""
echo ">>> Allocator verification:"
JEMALLOC_CHECK=$(ldd "$JEMALLOC_BIN" 2>/dev/null | grep -i jemalloc || echo "not found")
MIMALLOC_CHECK=$(ldd "$MIMALLOC_BIN" 2>/dev/null | grep -i mimalloc || echo "not found")
echo " jemalloc binary: $JEMALLOC_CHECK"
echo " mimalloc binary: $MIMALLOC_CHECK"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you actually run this benchmark script and pay attention to the output?


Those ldd checks won't verify correctly unless the system has .so of the files in it's linker paths at link time (for example in Fedora that'd be provided via mimalloc-devel), but even then the mimalloc crate does not support dynamic linking mimalloc, it's only statically linking.

Jemalloc does have support to dynamic link but only via the JEMALLOC_OVERRIDE ENV, but that is something you cannot control on your side (the user must provide it explicitly as the location can differ). There is also no compatibility guarantee when providing such an override and as per the linked docs you'd need to opt-in to the unprefixed_malloc_on_supported_platforms feature to use unprefixed symbols (not supported on Apple systems).


EDIT: Actually it seems that you can avoid adding that feature explicitly and just use:

JEMALLOC_OVERRIDE=/usr/lib64/libjemalloc.so \
CARGO_TARGET_DIR=target/jemalloc-dyn \
cargo build --release --no-default-features \
--features use-jemalloc,tikv-jemallocator/unprefixed_malloc_on_supported_platforms

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could verify this way, as both allocators support these ENV at runtime (should be more reliable than ldd or checking for symbols which in this case are stripped):

# `_RJEM_` prefix is required for the static compiled default.
# `MALLOC_CONF=stats_print:true` when instead using the `unprefixed_malloc_on_supported_platforms` feature.
$ _RJEM_MALLOC_CONF=stats_print:true ./target/jemalloc/release/fd --version 2>&1 | grep jemalloc
___ Begin jemalloc statistics ___
--- End jemalloc statistics ---


$ MIMALLOC_VERBOSE=1 ./target/mimalloc/release/fd --version 2>&1 | grep mimalloc
mimalloc: process init: 0x7F793E407C40
mimalloc: reserved 1048576 KiB memory
mimalloc: process done: 0x7F793E407C40

Comment thread benchmarks/allocator-comparison.sh Outdated
hyperfine \
--runs "$NUM_RUNS" \
--warmup "$NUM_WARMUP" \
--name "fd-jemalloc" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--name is not a valid argument to hyperfine... I get the impression you've vibe coded this file and not bothered to test it.

Comment thread src/main.rs
Comment on lines +51 to +52
feature = "use-mimalloc",
not(feature = "use-jemalloc")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't do this, what happens if someone adds both --features use-mimalloc,use-jemalloc? This can happen when a change like you've added to Cargo.toml makes one a default feature.

Instead prior to this code you should add a compile error:

#[cfg(all(feature = "use-jemalloc", feature = "use-mimalloc"))]
compile_error!("Features 'use-jemalloc' and 'use-mimalloc' are mutually exclusive and cannot be enabled together.");

This makes it more apparent that a mistake was done.

Comment thread Cargo.toml Outdated
Comment thread Cargo.toml
Comment on lines +99 to +107
# Jemalloc release profile for benchmarking
[profile.release-jemalloc]
inherits = "release"
env = ["CARGO_PROFILE_RELEASE_JEMALLOC=1"]

# Mimalloc release profile for benchmarking
[profile.release-mimalloc]
inherits = "release"
env = ["CARGO_PROFILE_RELEASE_MIMALLOC=1"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant, these don't belong here. What was the purpose for the ENV?

Comment thread Cargo.toml
Comment on lines -97 to +115
base = ["use-jemalloc"]
default = ["completions"]
# Default allocator is mimalloc (cross-platform, better performance per benchmarks)
base = ["use-mimalloc"]
default = ["base", "completions"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you placing base into default? The default behaviour is to not use jemalloc, it would instead use the system memory allocator (eg: via libc implementations glibc/musl).

MiMalloc is not necessarily providing any notable improvement on a glibc system. It does have an advantage over musl, notably across multi-threaded workloads.

Comment thread CHANGELOG.md Outdated
Comment on lines +7 to +8
## Changes
- Switch default allocator from jemalloc to mimalloc for improved performance (2.5x-3x faster per benchmarks), see #2056. Users can still build with jemalloc using `--features=use-jemalloc`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default allocator never was jemalloc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants