pax_global_header 0000666 0000000 0000000 00000000064 15225717161 0014521 g ustar 00root root 0000000 0000000 52 comment=beef233c176b235587c0145f765e4316e5ca60d1
mjansson-rpmalloc-beef233/ 0000775 0000000 0000000 00000000000 15225717161 0015653 5 ustar 00root root 0000000 0000000 mjansson-rpmalloc-beef233/.clang-format 0000664 0000000 0000000 00000003013 15225717161 0020223 0 ustar 00root root 0000000 0000000 BasedOnStyle: Google
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: 'false'
AlignConsecutiveDeclarations: 'false'
AlignOperands: 'true'
AlignTrailingComments: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: 'false'
AllowShortLoopsOnASingleLine: 'false'
AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: TopLevel
AlwaysBreakBeforeMultilineStrings: 'true'
AlwaysBreakTemplateDeclarations: 'true'
BinPackArguments: 'true'
BinPackParameters: 'true'
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: 'false'
ColumnLimit: '120'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
DerivePointerAlignment: 'false'
ExperimentalAutoDetectBinPacking: 'false'
IndentCaseLabels: 'true'
IndentWidth: '4'
IndentWrappedFunctionNames: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'false'
MaxEmptyLinesToKeep: '1'
NamespaceIndentation: None
ObjCSpaceAfterProperty: 'true'
ObjCSpaceBeforeProtocolList: 'true'
PointerAlignment: Left
SortIncludes: 'false'
SpaceAfterCStyleCast: 'false'
SpaceAfterTemplateKeyword: 'true'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: 'false'
SpacesInAngles: 'false'
SpacesInCStyleCastParentheses: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
TabWidth: '4'
UseTab: ForIndentation
mjansson-rpmalloc-beef233/.gitattributes 0000664 0000000 0000000 00000001374 15225717161 0020553 0 ustar 00root root 0000000 0000000 # Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
# Ignore build scripts, benchmark tooling and test cases in language stats.
# Use ** so the patterns recurse into subdirectories (build/ninja/, build/msvs/, ...).
build/** linguist-vendored
configure.py linguist-vendored
benchmark/*.py linguist-vendored
test/** linguist-vendored
mjansson-rpmalloc-beef233/.github/ 0000775 0000000 0000000 00000000000 15225717161 0017213 5 ustar 00root root 0000000 0000000 mjansson-rpmalloc-beef233/.github/FUNDING.yml 0000664 0000000 0000000 00000000102 15225717161 0021021 0 ustar 00root root 0000000 0000000 # These are supported funding model platforms
github: [mjansson]
mjansson-rpmalloc-beef233/.github/workflows/ 0000775 0000000 0000000 00000000000 15225717161 0021250 5 ustar 00root root 0000000 0000000 mjansson-rpmalloc-beef233/.github/workflows/ci.yml 0000664 0000000 0000000 00000027640 15225717161 0022377 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches: [develop, main, ci-multiplatform]
pull_request:
branches: [develop, main]
workflow_dispatch:
# Cancel superseded runs on the same ref
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Shared compile settings for the direct (non-ninja) builds used by the
# sanitizer, Android and iOS jobs. The C-only source set (no main-override.cc)
# builds the library without the libc override, so it never collides with the
# sanitizer/runtime allocators, and runs the full first-class-heap / statistics
# test suite.
env:
RP_SRC: rpmalloc/rpmalloc.c test/thread.c test/main.c
RP_INC: -Irpmalloc -Itest
RP_DEFS: -DENABLE_OVERRIDE=0 -DENABLE_ASSERTS=1 -DENABLE_STATISTICS=1 -DRPMALLOC_FIRST_CLASS_HEAPS=1
jobs:
# ---------------------------------------------------------------------------
# Linux native (x86-64 and ARM64), gcc + clang, build and run the ninja suite
# ---------------------------------------------------------------------------
linux:
name: linux-${{ matrix.arch }}-${{ matrix.toolchain }}
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, arch: x86-64, toolchain: gcc }
- { os: ubuntu-latest, arch: x86-64, toolchain: clang }
- { os: ubuntu-24.04-arm, arch: arm64, toolchain: gcc }
- { os: ubuntu-24.04-arm, arch: arm64, toolchain: clang }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Install ninja
run: sudo apt-get update && sudo apt-get install -y ninja-build
- name: Configure and build
run: |
python3 configure.py -c release -a ${{ matrix.arch }} --toolchain ${{ matrix.toolchain }}
ninja
- name: Run tests
run: |
set -eu
found=0
for b in $(find bin -type f -name 'rpmalloc-test*'); do
found=1; echo "::group::$b"; "$b"; echo "::endgroup::"
done
[ "$found" -eq 1 ] || { echo "no test binaries found"; exit 1; }
# ---------------------------------------------------------------------------
# Alpine Linux (musl libc), gcc + clang, build and run the ninja suite in a
# container (checkout on the host; build in the container via docker).
# ---------------------------------------------------------------------------
alpine-musl:
name: alpine-musl-${{ matrix.toolchain }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain: [gcc, clang]
steps:
- uses: actions/checkout@v5
- name: Build and run the ninja suite in Alpine (musl)
run: |
docker run --rm -v "$PWD":/work -w /work alpine:latest sh -euc '
apk add --no-cache build-base clang python3 samurai
ln -sf /usr/bin/samu /usr/local/bin/ninja
python3 configure.py -c release -a x86-64 --toolchain ${{ matrix.toolchain }}
ninja
found=0
for b in $(find bin -type f -name "rpmalloc-test*"); do
found=1; echo "::group::$b"; "$b"; echo "::endgroup::"
done
[ "$found" -eq 1 ] || { echo "no test binaries found"; exit 1; }
'
# ---------------------------------------------------------------------------
# Sanitizers on Linux x64 + arm64.
# UBSAN (gating): the main test suite.
# ASAN (gating): the stress harness (ASAN-clean). The main suite trips an ASAN-internal
# stacktrace CHECK in glibc threading on Linux - not an rpmalloc bug; the
# same suite passes under ASAN on macOS. The harness is capped in size so
# ASAN shadow overhead on the huge band does not throttle the run.
# TSAN is intentionally not run in CI: it serializes badly on rpmalloc's atomic-dense /
# cross-thread free hot paths, so throughput is unusable. Use test/stress.sh for TSAN runs.
# ---------------------------------------------------------------------------
linux-sanitizers:
name: linux-${{ matrix.arch }}-${{ matrix.san }}
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, arch: x64, san: ubsan, flags: "-fsanitize=undefined -fno-sanitize-recover=all", gating: true, src: "test/main.c", run: "RPMALLOC_TEST_SCALE=50 ./san-bin" }
- { os: ubuntu-latest, arch: x64, san: asan, flags: "-fsanitize=address,undefined", gating: true, src: "test/stress.c", run: "RPMALLOC_STRESS_SECONDS=12 RPMALLOC_STRESS_THREADS=2 RPMALLOC_STRESS_MAX_SIZE=65536 ./san-bin" }
- { os: ubuntu-24.04-arm, arch: arm64, san: ubsan, flags: "-fsanitize=undefined -fno-sanitize-recover=all", gating: true, src: "test/main.c", run: "RPMALLOC_TEST_SCALE=50 ./san-bin" }
- { os: ubuntu-24.04-arm, arch: arm64, san: asan, flags: "-fsanitize=address,undefined", gating: true, src: "test/stress.c", run: "RPMALLOC_STRESS_SECONDS=12 RPMALLOC_STRESS_THREADS=2 RPMALLOC_STRESS_MAX_SIZE=65536 ./san-bin" }
runs-on: ${{ matrix.os }}
continue-on-error: ${{ !matrix.gating }}
timeout-minutes: 15
env:
ASAN_OPTIONS: "use_sigaltstack=0:detect_leaks=1"
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
steps:
- uses: actions/checkout@v5
- name: Build (${{ matrix.san }})
run: |
clang -g -O1 -fno-omit-frame-pointer ${{ matrix.flags }} \
-D_GNU_SOURCE $RP_DEFS $RP_INC rpmalloc/rpmalloc.c test/thread.c ${{ matrix.src }} -lpthread -o san-bin
- name: Run (${{ matrix.san }})
run: ${{ matrix.run }}
# ---------------------------------------------------------------------------
# macOS native (Apple Silicon + Intel), build and run
# ---------------------------------------------------------------------------
macos:
name: macos-${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- { os: macos-14, arch: arm64 }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Install ninja
run: brew install ninja
- name: Configure and build
run: |
python3 configure.py -c release -a ${{ matrix.arch }} --toolchain clang
ninja
- name: Run tests
run: |
set -eu
found=0
for b in $(find bin -type f -name 'rpmalloc-test*'); do
found=1; echo "::group::$b"; "$b"; echo "::endgroup::"
done
[ "$found" -eq 1 ] || { echo "no test binaries found"; exit 1; }
# ---------------------------------------------------------------------------
# macOS sanitizers (ASAN+UBSAN)
# ---------------------------------------------------------------------------
macos-sanitizers:
name: macos-asan-ubsan
runs-on: macos-14
env:
RPMALLOC_TEST_SCALE: "50"
ASAN_OPTIONS: "use_sigaltstack=0:detect_leaks=0"
steps:
- uses: actions/checkout@v5
- name: Build (asan+ubsan)
run: |
clang -g -O1 -fno-omit-frame-pointer -fsanitize=address,undefined \
$RP_DEFS $RP_INC $RP_SRC -o rpmalloc-test-asan
- name: Run
run: ./rpmalloc-test-asan
# ---------------------------------------------------------------------------
# Windows native (x64 + ARM64) via MSVC
# ---------------------------------------------------------------------------
windows:
name: windows-${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- { os: windows-latest, arch: x86-64, msvc_arch: x64, allow_failure: false }
- { os: windows-11-arm, arch: arm64, msvc_arch: arm64, allow_failure: false }
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.allow_failure }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Install ninja
run: python -m pip install ninja
- name: Set up MSVC
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.msvc_arch }}
- name: Configure and build
shell: bash
run: |
python configure.py -c release -a ${{ matrix.arch }} --toolchain msvc
ninja
- name: Run tests
shell: bash
run: |
set -eu
found=0
for b in $(find bin -type f -name 'rpmalloc-test*.exe'); do
found=1; echo "::group::$b"; "$b"; echo "::endgroup::"
done
[ "$found" -eq 1 ] || { echo "no test binaries found"; exit 1; }
# ---------------------------------------------------------------------------
# Android: build with the modern unified NDK clang, run on an x86_64 emulator
# with reduced scaling so the emulator is not overwhelmed.
# ---------------------------------------------------------------------------
android:
name: android-x86_64-emulator
runs-on: ubuntu-latest
env:
ANDROID_API: "29"
ANDROID_ABI: x86_64
steps:
- uses: actions/checkout@v5
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Build Android test (NDK clang)
run: |
set -euo pipefail
NDK="${ANDROID_NDK_LATEST_HOME:-$ANDROID_NDK_ROOT}"
TC="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
"$TC/x86_64-linux-android${ANDROID_API}-clang" -O2 -D_GNU_SOURCE \
$RP_DEFS $RP_INC $RP_SRC -o rpmalloc-test-android
file rpmalloc-test-android
- name: Run on emulator
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
arch: x86_64
target: default
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim
disable-animations: true
script: |
adb push rpmalloc-test-android /data/local/tmp/rpmalloc-test
adb shell chmod 755 /data/local/tmp/rpmalloc-test
adb shell "RPMALLOC_TEST_THREADS=2 RPMALLOC_TEST_SCALE=10 /data/local/tmp/rpmalloc-test; echo RP_EXIT=\$?" | tee out.txt
grep -q "RP_EXIT=0" out.txt
# ---------------------------------------------------------------------------
# iOS: build for the simulator SDK and run in a booted simulator with reduced
# scaling. simctl propagates SIMCTL_CHILD_* env vars to the spawned process.
# ---------------------------------------------------------------------------
ios:
name: ios-simulator
runs-on: macos-14
steps:
- uses: actions/checkout@v5
- name: Build for iOS simulator
run: |
set -euo pipefail
xcrun -sdk iphonesimulator clang -arch arm64 -mios-simulator-version-min=15.0 -O2 \
-DRPMALLOC_TEST_FORCE_MAIN $RP_DEFS $RP_INC $RP_SRC -o rpmalloc-test-ios
file rpmalloc-test-ios
- name: Boot simulator and run
run: |
set -euo pipefail
# Use a pre-created, available iPhone simulator (guaranteed compatible
# device+runtime pair, unlike creating one from the newest device type).
UDID=$(xcrun simctl list devices available -j | python3 -c "
import sys, json
for runtime, devices in json.load(sys.stdin)['devices'].items():
if 'iOS' not in runtime:
continue
for dev in devices:
if dev.get('isAvailable') and 'iPhone' in dev['name']:
print(dev['udid']); sys.exit(0)
sys.exit('no available iPhone simulator found')
")
echo "Using simulator $UDID"
xcrun simctl boot "$UDID"
SIMCTL_CHILD_RPMALLOC_TEST_THREADS=2 SIMCTL_CHILD_RPMALLOC_TEST_SCALE=10 \
xcrun simctl spawn "$UDID" "$PWD/rpmalloc-test-ios"
xcrun simctl shutdown "$UDID" || true
xcrun simctl shutdown "$UDID" || true
mjansson-rpmalloc-beef233/.gitignore 0000664 0000000 0000000 00000005621 15225717161 0017647 0 ustar 00root root 0000000 0000000 *.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
.ninja*
build.ninja
.vs
.vscode
# Generated version
version.c
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#Android local build files
build/android/assets
build/android/libs
#Xcode build
build/xcode/foundation/build
#Doxygen generated
doc/html
#Coverity scan
cov-int/
cov-int.*
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
[Pp]rofile/
[Dd]eploy/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pchi
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
*.dotCover
*.lastbuildstate
*.unsuccessfulbuild
*.opendb
*.vc
*.VC.db
*.db-shm
*.db-wal
## TODO: If you have NuGet Package Restore enabled, uncomment this
#packages/
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
sql
TestResults
*.Cache
ClientBin
stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
eggs
parts
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store
###############
## Generic
###############
#Project builds
lib/**
bin/**
dist/**
libs/**
#Log files
*.log
*.tlog
#Scons build
.sconsign.dblite
build/scons/debug*
build/scons/release*
build/scons/profi*
build/scons/deploy*
#Backups
*~
#Object files
*.o
#XCode
xcuserdata
*.xccheckout
#SublimeText local workspace
*.sublime-workspace
#Never store keystores in version control!
*.keystore
#Do not store local build prefs
build.json
codesign.json
coveralls.json
coverallsreport.json
codecov.json
codecovreport.json
benchmark-*.txt
# Python
__pycache__/
*.pyc
# Stress test helper output (test/stress.sh)
/rpmalloc-stress-*
# Init race test helper output (test/init-race.sh)
/rpmalloc-init-race-*
mjansson-rpmalloc-beef233/BENCHMARKS.md 0000664 0000000 0000000 00000012324 15225717161 0017614 0 ustar 00root root 0000000 0000000 # Benchmarks
These results are produced with the [mimalloc-bench](https://github.com/daanx/mimalloc-bench)
allocator benchmark suite, running its `allt` set of benchmarks across a wide range of
allocators. The raw captured results and the plotting script live in the [benchmark](benchmark)
directory, so the graphs below can be regenerated and extended (see
[Reproducing](#reproducing) below).
All numbers were collected on a single machine:
- 13th Gen Intel Core i7-13800H, 14 cores / 20 threads
- 30 GiB RAM
- Ubuntu 26.04, glibc 2.43
- allocators and benchmarks built with `-march=native`
As with any allocator benchmark, the absolute numbers are specific to this machine and
workload mix and should be read as relative comparisons, not absolute performance figures.
## Per-benchmark results
The `allt` benchmarks are deliberately diverse - real programs, servers and synthetic stress
tests - so they are best read per benchmark rather than reduced to a single aggregate number.
Across the suite rpmalloc sits in the leading throughput group, competitive with snmalloc and
the three mimalloc generations and ahead of jemalloc and tcmalloc, while trading some peak
memory for the larger page geometry and free page retention that drive that throughput. Where
the smaller footprint matters more than peak throughput, the `disable_decommit` configuration
is off by default and unused pages are returned to the OS.
Elapsed wall-clock time per benchmark, lower is better. Allocators more than 4x slower than
rpmalloc on a given benchmark are omitted from that benchmark's chart for readability (the
captured result files contain the complete data).

Peak resident memory per benchmark, lower is better.

## Benchmarks
The `allt` set exercises a mix of real programs and synthetic stress tests:
- **cfrac, espresso, barnes, lean, lua, gs (ghostscript)** - real single and lightly
threaded programs, dominated by realistic allocation patterns.
- **redis, rocksdb** - real servers driven by their own benchmark clients.
- **larson, mstress, rptest, xmalloc-test, alloc-test, sh6bench, sh8bench, cache-scratch** -
multithreaded synthetic stress tests covering producer/consumer hand-off, cross-thread
frees, sharing and contention.
- **glibc-simple, glibc-thread** - the glibc malloc microbenchmarks.
rptest is the same benchmark used for the throughput-versus-threads graph in the
[README](README.md); the suite runs it at a single fixed configuration.
## Allocators
The following allocators were built and run. The `Key` column is the short name used by
mimalloc-bench and in the captured CSV files (the graphs spell out the full names). Versions
are those pinned by mimalloc-bench at the time of the run:
| Key | Allocator | Version |
|-----|-----------|---------|
| rp | rpmalloc | develop |
| mi, mi2, mi3 | mimalloc | 1.8.2, 2.1.2, 3.3.2 |
| je | jemalloc | 5.3.0 |
| tc | tcmalloc (gperftools minimal) | 2.18 |
| sn, sn-sec | snmalloc | 0.7.4 |
| hd | Hoard | 2025-07 |
| tbb | Intel TBB malloc | 2023.0.0 |
| sm | SuperMalloc | master |
| scudo | scudo (LLVM standalone) | main |
| lt | ltalloc | master |
| mng | musl mallocng | master |
| iso | isoalloc | 1.2.5 |
| hm, hml | hardened_malloc, light | 11 |
| sg | SlimGuard | master |
| ff, fg, gd | ffmalloc, FreeGuard, Guarder | master |
| mesh, nomesh | Mesh | master |
| lf | lockfree-malloc | master |
| lp | libpas (WebKit bmalloc) | main |
| yal | yalloc | main |
| rmalloc | rmalloc | master |
| sys | system glibc malloc | 2.43 |
## Reproducing
The graphs are generated from two captured result files in
[benchmark/results](benchmark/results):
- `mimalloc-bench-allt.csv` - the merged `allt` suite results, in the mimalloc-bench
`benchres.csv` format (`benchmark allocator elapsed rss user sys page-faults page-reclaims`).
- `rptest-threads.csv` - the rptest thread sweep used by the README graph.
To regenerate the `allt` data, build the allocators and benchmarks in a mimalloc-bench
checkout and run the suite from `out/bench`:
```
../../bench.sh sys rp mi mi2 mi3 je tc sn sn-sec hd sm tbb lt iso scudo \
ff gd hm hml lf lp mesh nomesh mng sg fg yal rmalloc allt
```
The resulting `benchres.csv` replaces `benchmark/results/mimalloc-bench-allt.csv`. Then
regenerate the graphs:
```
python3 benchmark/plot.py
```
`plot.py` requires only `matplotlib`. The graph filtering (the 4x omission rule and dropping
allocators that failed many benchmarks) is applied in the plotting step; the captured CSV
files are always complete and unfiltered.
In the captured `mimalloc-bench-allt.csv` a benchmark that an allocator crashed on is recorded
with a zero or blank elapsed time, or - when the process died after starting but before doing
any real work - with a short elapsed time but zero user and system CPU time (for example
rocksdb under mallocng, which exits immediately with an empty report). `plot.py` treats all of
these as failures rather than as instant (fastest/smallest) results: an allocator that failed
more than two benchmarks is dropped from the graphs entirely (on this run Guarder, Hoard and
FreeGuard - `gd`, `hd`, `fg`), and isolated failures such as mallocng on rocksdb simply leave a
missing data point.
mjansson-rpmalloc-beef233/CHANGELOG 0000664 0000000 0000000 00000031375 15225717161 0017076 0 ustar 00root root 0000000 0000000 2.0.1
Fixed a data race in concurrent lazy initialization where simultaneous first allocations from
multiple threads could race on the global init state and return a block backed by a partially
written configuration. Initialization is now serialized through an atomic state machine.
Spin waits now escalate to an OS scheduler yield after a spin threshold, avoiding throughput
collapse when a preempted lock holder is starved by spinners under high thread contention.
2.0.0
Major redesign of the allocator core. The most significant changes in design from the 1.4 series:
Three level memory hierarchy. Memory is now organized as span -> page -> block instead of
span -> block. Each span is a fixed 256MiB aligned region partitioned into fixed size pages, and
each page is partitioned into equally sized blocks of a single size class. As before, the fixed
span and page alignment lets free locate the owning span and page headers in constant time by
masking the block address.
Four page types by block size. Blocks up to 2MiB are served from one of four page types: small
(blocks up to 4KiB, 64KiB pages), medium-small (up to 32KiB, 1MiB pages), medium-large (up to
256KiB, 4MiB pages) and large (up to 2MiB, 16MiB pages). Blocks larger than 2MiB are mapped and
unmapped directly as huge allocations. The smallest classes keep a 16 byte granularity and larger
classes use a variable interval bounded to a fixed overhead ratio.
Reserve-then-commit memory model. The allocator reserves span address space up front and commits
and decommits physical memory on demand per page, returning unused ranges to the OS where
supported. The custom memory interface gains commit and decommit callbacks alongside map and unmap
(memory_map now also takes an alignment and reports the mapped size). This replaces the previous
per-thread and global span cache subsystem, which has been removed.
Per-page thread ownership and deferred cross-thread free. Each span is owned by a single heap and
each page is owned exclusively by the allocating thread, so the hot allocation and free paths are
lock free. Blocks freed by a non-owning thread are pushed to a per-page atomic deferred free list
and reclaimed by the owner.
Heap packing. Multiple heaps are packed into a single mapping and pristine heaps are reused,
reducing per-thread setup cost and memory overhead.
Other notable changes:
Thread and per-heap allocation statistics, and finalize-time leak detection gated on
ENABLE_LEAK_DETECTION (built on ENABLE_STATISTICS).
Huge and transparent huge page support on Windows, Linux and macOS, with runtime probing of huge
page availability and hardening of initialization against re-entrancy.
New architecture support for LoongArch and arm64ec, plus assorted Windows and Visual Studio fixes.
Licensing is now SPDX/REUSE compliant (Unlicense OR MIT). Added RPMALLOC_VERSION defines to
rpmalloc.h, a multi-platform GitHub Actions CI (Linux, macOS and Windows on x64 and arm64, plus
Android and iOS), and a long-running multithreaded stress test (test/stress.c).
Note: the custom memory-mapping interface is not source compatible with 1.4. Code that supplies a
custom memory_map/memory_unmap implementation must be updated to the new map/commit/decommit/unmap
signatures.
1.4.5
Fix for issue where span cache was not properly freed on heap free all
Add rpmalloc_get_heap_for_ptr function to get heap for a given allocation
Fixed medium size block limit and alignment handling when span size is reconfigured
Fixed SIGILL on macOS rosetta mode (x86_64)
Update compatibility with newer clang versions and use intrinsics for memcpy
1.4.4
Fixed an issue where an external thread concurrently freeing a block to the deferred list of
a heap at the same time as owner thread freeing the last used block could cause a race condition
ending in spen being freed multiple time.
Added fallback path when huge page allocation fails to allocate and promote new pages as a
transparent huge page
Added option to name pages on Linux and Android.
Compilation compatibility updates for MSYS2, FreeBSD, MacOS/clang and tinycc.
1.4.3
Fixed an issue where certain combinations of memory page size and span map counts could cause
a deadlock in the mapping of new memory pages.
Tweaked cache levels and avoid setting spans as reserved in a heap when the heap already has
spans in the thread cache to improve cache usage.
Prefer flags to more actively evict physical pages in madvise calls when partially unmapping
span ranges on POSIX systems.
1.4.2
Fixed an issue where calling _exit might hang the main thread cleanup in rpmalloc if another
worker thread was terminated while holding exclusive access to the global cache.
Improved caches to prioritize main spans in a chunk to avoid leaving main spans mapped due to
remaining subspans in caches.
Improve cache reuse by allowing large blocks to use caches from slightly larger cache classes.
Fixed an issue where thread heap statistics would go out of sync when a free span was deferred
to another thread heap
API breaking change - added flag to rpmalloc_thread_finalize to avoid releasing thread caches.
Pass nonzero value to retain old behaviour of releasing thread caches to global cache.
Add option to config to set a custom error callback for assert failures (if ENABLE_ASSERT)
1.4.1
Dual license as both released to public domain or under MIT license
Allow up to 4GiB memory page sizes
Fix an issue where large page sizes in conjunction with many threads waste a lot of memory (previously
each heap occupied an entire memory page, now heaps can now share a memory page)
Fixed compilation issue on macOS when ENABLE_PRELOAD is set but not ENABLE_OVERRIDE
New first class heap API allowing explicit heap control and release of entire heap in a single call
Added rpaligned_calloc function for aligned and zero intialized allocations
Fixed natural alignment check in rpaligned_realloc to 16 bytes (check was 32, which is wrong)
Minor performance improvements for all code paths by simplified span handling
Minor performance improvements and for aligned allocations with alignment less or equal to 128 bytes
by utilizing natural block alignments
Refactor finalization to be compatible with global scope data causing dynamic allocations and frees, like
C++ objects with custom ctors/dtors
Refactor thread and global cache to be array based instead of list based for improved performance
and cache size control
Added missing C++ operator overloads with ENABLE_OVERRIDE when using Microsoft C++ runtimes
Fixed issue in pvalloc override that could return less than a memory page in usable size
Added a missing null check in the non-hot allocation code paths
1.4.0
Improved cross thread deallocations by using per-span atomic free list to minimize thread
contention and localize free list processing to actual span
Change span free list to a linked list, conditionally initialized one memory page at a time
Reduce number of conditionals in the fast path allocation and avoid touching heap structure
at all in best case
Avoid realigning block in deallocation unless span marked as used by alignment > 32 bytes
Revert block granularity and natural alignment to 16 bytes to reduce memory waste
Bugfix for preserving data when reallocating a previously aligned (>32 bytes) block
Use compile time span size by default for improved performance, added build time RPMALLOC_CONFIGURABLE
preprocessor directive to reenable configurability of span and page size
More detailed statistics
Disabled adaptive thread cache by default
Fixed an issue where reallocations of large blocks could read outsize of memory page boundaries
Tag mmap requests on macOS with tag 240 for identification with vmmap tool
1.3.2
Support for alignment equal or larger than memory page size, up to span size
Added adaptive thread cache size based on thread allocation load
Fix 32-bit MSVC Windows builds using incorrect 64-bit pointer CAS
Updated compatibility with clang toolchain and Python 3
Support preconfigured huge pages
Moved active heap counter to statistics
Moved repository to https://github.com/mjansson/rpmalloc
1.3.1
Support for huge pages
Bugfix to old size in aligned realloc and usable size for aligned allocs when alignment > 32
Use C11 atomics for non-Microsoft compilers
Remove remaining spin-lock like control for caches, all operations are now lock free
Allow large deallocations to cross thread heaps
1.3.0
Make span size configurable and all spans equal in size, removing span size classes and streamlining the thread cache.
Allow super spans to be reserved in advance and split up in multiple used spans to reduce number of system calls. This will not increase committed physical pages, only reserved virtual memory space.
Allow super spans to be reused for allocations of lower size, breaking up the super span and storing remainder in thread cache in order to reduce load on global cache and reduce cache overhead.
Fixed an issue where an allocation of zero bytes would cause a segmentation fault from indexing size class array with index -1.
Fixed an issue where an allocation of maximum large block size (2097120 bytes) would index the heap cache array out of bounds and potentially cause a segmentation fault depending on earlier allocation patterns.
Fixed an issue where memory pages at start of aligned span run was not completely unmapped on POSIX systems.
Fixed an issue where spans were not correctly marked as owned by the heap after traversing the global span cache.
Added function to access the allocator configuration after initialization to find default values.
Removed allocated and reserved statistics to reduce code complexity.
1.2.2
Add configurable memory mapper providing map/unmap of memory pages. Default to VirtualAlloc/mmap if none provided. This allows rpmalloc to be used in contexts where memory is provided by internal means.
Avoid using explicit memory map addresses to mmap on POSIX systems. Instead use overallocation of virtual memory space to gain 64KiB alignment of spans. Since extra pages are never touched this should have no impact on real memory usage and remove the possibility of contention in virtual address space with other uses of mmap.
Detect system memory page size at initialization, and allow page size to be set explicitly in initialization. This allows the allocator to be used as a sub-allocator where the page granularity should be lower to reduce risk of wasting unused memory ranges, and adds support for modern iOS devices where page size is 16KiB.
Add build time option to use memory guards, surrounding each allocated block with a dead zone which is checked for consistency when block is freed.
Always finalize thread on allocator finalization, fixing issue when re-initializing allocator in the same thread.
Add basic allocator test cases
1.2.1
Split library into rpmalloc only base library and preloadable malloc wrapper library.
Add arg validation to valloc and pvalloc.
Change ARM memory barrier instructions to dmb ish/ishst for compatibility.
Improve preload compatibility on Apple platforms by using pthread key for TLS in wrapper library.
Fix ABA issue in orphaned heap linked list
1.2
Dual license under MIT
Fix init/fini checks in malloc entry points for preloading into binaries that does malloc/free in init or fini sections
Fixed an issue where freeing a block which had been realigned during allocation due to alignment request greater than 16 caused the free block link to be written in the wrong place in the block, causing next allocation from the size class to return a bad pointer
Improve mmap 64KiB granularity enforcement loop to avoid excessive iterations
Fix undersized adaptive cache counter array for large block in heap structure, causing potential abort on exit
Avoid hysteresis in realloc by overallocating on small size increases
Add entry point for realloc with alignment and optional flags to avoid preserving content
Add valloc/pvalloc/cfree wrappers
Add C++ new/delete wrappers
1.1
Add four cache presets (unlimited, performance priority, size priority and no cache)
Slight performance improvement by dependent class index lookup for merged size classes
Adaptive cache size per thread and per size class for improved memory efficiency, and release thread caches to global cache in fixed size batches
Merged caches for small/medium classes using 64KiB spans with 64KiB large blocks
Require thread initialization with rpmalloc_thread_initialize, add pthread hooks for automatic init/fini
Added rpmalloc_usable_size query entry point
Fix invalid old size in memory copy during realloc
Optional statistics and integer overflow guards
Optional asserts for easier debugging
Provide malloc entry point replacements and automatic init/fini hooks, and a LD_PRELOAD:able dynamic library build
Improve documentation and additional code comments
Move benchmarks to separate repo, https://github.com/mjansson/rpmalloc-benchmark
1.0
Initial release
mjansson-rpmalloc-beef233/LICENSE 0000664 0000000 0000000 00000002060 15225717161 0016656 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2017 Mattias Jansson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
mjansson-rpmalloc-beef233/LICENSES/ 0000775 0000000 0000000 00000000000 15225717161 0017060 5 ustar 00root root 0000000 0000000 mjansson-rpmalloc-beef233/LICENSES/MIT.txt 0000664 0000000 0000000 00000002060 15225717161 0020250 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2017 Mattias Jansson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
mjansson-rpmalloc-beef233/LICENSES/Unlicense.txt 0000664 0000000 0000000 00000002274 15225717161 0021553 0 ustar 00root root 0000000 0000000 This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to
mjansson-rpmalloc-beef233/README.md 0000664 0000000 0000000 00000046403 15225717161 0017141 0 ustar 00root root 0000000 0000000 # rpmalloc - General Purpose Memory Allocator
This library provides a public domain cross platform lock free thread caching 16-byte aligned memory allocator implemented in C. The latest source code is always available at https://github.com/mjansson/rpmalloc
Created by Mattias Jansson ([@maniccoder](https://twitter.com/maniccoder)) - Discord server for discussions at https://discord.gg/M8BwTQrt6c
Platforms currently supported:
- Windows
- Linux
- MacOS
- iOS
- Android
The code should be easily portable to any platform with atomic operations and an mmap-style virtual memory management API. The API used to map/unmap memory pages can be configured in runtime to a custom implementation, as can the memory page size.
This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. Or, if you choose, you can use it under the MIT license. The license is declared as the SPDX expression `Unlicense OR MIT`.
# Performance
We believe rpmalloc is faster than most popular memory allocators like tcmalloc, hoard, ptmalloc3 and others without causing extra allocated memory overhead in the thread caches compared to these allocators. We also believe the implementation to be easier to read and modify compared to these allocators, as it is a single source file of ~3300 lines of C code. All allocations have a natural 16-byte alignment.
The chart below shows throughput on the `rptest` benchmark, allocating randomly sized blocks in the `[16, 8000]` bytes range with a linear falloff distribution and cross-thread frees, as the number of threads scales from 1 to 16. Higher is better.
![rptest throughput, random [16,8000] bytes, 1-16 threads](benchmark/images/rptest-perf.png)
Peak resident memory for the same benchmark, lower is better:
![rptest peak memory, random [16,8000] bytes, 1-16 threads](benchmark/images/rptest-memory.png)
The numbers were collected on a 13th Gen Intel Core i7-13800H (14 cores / 20 threads) running Ubuntu 26.04, comparing rpmalloc against the current releases of several popular allocators. The actual numbers are not to be interpreted as absolute performance figures, but rather as relative comparisons between the different allocators. For a full allocator comparison across the mimalloc-bench benchmark suite, including peak memory usage, see the [BENCHMARKS](BENCHMARKS.md) file. The captured results and the script that produces these graphs live in the [benchmark](benchmark) directory.
# Required functions
The allocator is initialized automatically with default configuration on the first allocation request, so no explicit initialization is required. If you want to provide a custom memory interface or configuration you __MUST__ call the initialization function, either __rpmalloc_initialize__ or __rpmalloc_initialize_config__, before any other call into the allocator.
Before terminating your use of the allocator, you __SHOULD__ call __rpmalloc_finalize__ in order to release caches and unmap virtual memory, as well as prepare the allocator for global scope cleanup at process exit or dynamic library unload depending on your use case.
# Using
The easiest way to use the library is simply adding __rpmalloc.[h|c]__ to your project and compile them along with your sources. The allocator is completely self contained, you are not required to call the init/fini functions from your own code, but can do so in order to initialize and finalize the allocator in specific places or provide your own memory interface and/or configuration:
__rpmalloc_initialize__ : Call at process start to initialize the allocator, optionally passing a custom memory interface with functions to map, commit, decommit and unmap memory pages (pass null to use the default OS backed implementation)
__rpmalloc_initialize_config__ : Optional entry point to call at process start to initialize the allocator with a custom memory interface and/or a configuration of memory page size, huge page or transparent huge page usage, decommit behaviour, page naming and unmap-on-finalize behaviour
__rpmalloc_finalize__: Call at process exit to finalize the allocator
__rpmalloc_thread_initialize__: Call at each thread start to initialize the thread local data for the allocator (done automatically on the first allocation in a thread)
__rpmalloc_thread_finalize__: Call at each thread exit to finalize and release the thread heap for reuse by other threads (done automatically at thread exit on platforms supporting thread destructors)
__rpmalloc_config__: Get the current runtime configuration of the allocator
Then simply use the __rpmalloc__/__rpfree__ and the other malloc style replacement functions. Remember all allocations are 16-byte aligned, so no need to call the explicit rpmemalign/rpaligned_alloc/rpposix_memalign functions unless you need greater alignment, they are simply wrappers to make it easier to replace in existing code.
If you wish to override the standard library malloc family of functions and have automatic initialization/finalization of process and threads, define __ENABLE_OVERRIDE__ to non-zero (default is 1) which will include the `malloc.c` file in compilation of __rpmalloc.c__, and then rebuild the library or your project where you added the rpmalloc source. If you compile rpmalloc as a separate static library and use only the overridden libc entry points (you call plain `malloc`/`free` but never any `rp*` function directly), the linker may not pull the rpmalloc object out of the archive, and the overrides will silently not take effect. To force its inclusion you must reference at least one symbol from the library: the easiest way is to include `rpmalloc.h` in at least one source file and call `rpmalloc_linker_reference` somewhere - it's a dummy empty function provided for exactly this purpose. This is not needed if you call `rpmalloc`/`rpfree` (or any other `rp*` function) directly, link the dynamic library, or use `LD_PRELOAD`/`DYLD_INSERT_LIBRARIES`. For C++ operator overrides the behaviour is platform specific. On Windows you must `#include ` in exactly one source file (it defines the `new`/`delete` operators with external linkage and has no include guard, so including it in more than one translation unit causes duplicate symbol errors). On other platforms the `new`/`delete` operators are overridden automatically by the library - do not include `rpnew.h` there, as it will conflict with the operators already provided. The list of libc entry points replaced may not be complete, use libc/stdc++ replacement only as a convenience for testing the library on an existing code base, not a final solution.
For explicit first class heaps, see the __rpmalloc_heap_*__ API under [first class heaps](#first-class-heaps) section, requiring __RPMALLOC_FIRST_CLASS_HEAPS__ to be defined to 1 - default is 0, as it imposes a very slight performance hit in deallocation path from an extra conditinal instruction.
# Building
To compile as a static library run the configure python script which generates a Ninja build script, then build using ninja. The ninja build produces both a static and a dynamic library named `rpmalloc`.
By default the dynamic library can be used with `LD_PRELOAD`/`DYLD_INSERT_LIBRARIES` to inject in a preexisting binary, replacing any malloc/free family of function calls (when __ENABLE_OVERRIDE__ is defined to 1). This is only implemented for Linux and macOS targets. The list of libc entry points replaced may not be complete, use preloading as a convenience for testing the library on an existing binary, not a final solution.
The latest stable release is available in the main branch. For latest development code, use the develop branch.
# Configuration options
Detailed statistics are available if __ENABLE_STATISTICS__ is defined to 1 (default is 0, or disabled), either on compile command line or by setting the value in `rpmalloc.c`. This will cause a slight overhead in runtime to collect statistics for memory page and huge block operations.
Detection of allocations still outstanding at `rpmalloc_finalize` is enabled if __ENABLE_LEAK_DETECTION__ is defined to 1. It requires __ENABLE_STATISTICS__ for the allocation counters and follows it by default, except when building with __ENABLE_OVERRIDE__ where it defaults to 0: with the standard library override rpmalloc is the backing store for the C runtime's own allocations (such as per-thread TLS), which are still live at finalize and indistinguishable from application leaks. When __ENABLE_ASSERTS__ is also enabled a detected leak triggers an assert.
Integer safety checks on all calls are enabled if __ENABLE_VALIDATE_ARGS__ is defined to 1 (default is 0, or disabled), either on compile command line or by setting the value in `rpmalloc.c`. If enabled, size arguments to the global entry points are verified not to cause integer overflows in calculations.
Asserts are enabled if __ENABLE_ASSERTS__ is defined to 1 (default is 0, or disabled), either on compile command line or by setting the value in `rpmalloc.c`.
To include __malloc.c__ in compilation and provide overrides of standard library malloc entry points define __ENABLE_OVERRIDE__ to 1 (this is the default).
To enable support for first class heaps, define __RPMALLOC_FIRST_CLASS_HEAPS__ to 1 (default is 0, or disabled).
# Huge pages
The allocator has support for huge/large pages on Windows, Linux and MacOS. To enable it, pass a non-zero value in the config value `enable_huge_pages` when initializing the allocator with `rpmalloc_initialize_config`. Explicit huge pages require system configuration (a preallocated huge page pool on Linux, the `SeLockMemoryPrivilege` on Windows). At initialization rpmalloc checks whether huge pages are actually available (on Linux by probing a real huge page mapping, not just reading the configured huge page size). If huge pages were requested but are not available on the system, `rpmalloc_initialize`/`rpmalloc_initialize_config` returns a non-zero value and leaves the allocator uninitialized (the effective `enable_huge_pages` is cleared), so you can detect this and re-initialize without huge pages if desired. On Linux, if an individual huge page backed mapping cannot later be served from the pool the allocator falls back to a normal mapping promoted to transparent huge pages. On Windows, large pages cannot be decommitted, so unused memory ranges are kept committed until unmapped when huge pages are enabled - on Linux unused memory ranges are decommitted if the kernel supports it (5.18 or later for huge page backed mappings).
On Linux and Android the allocator can alternatively use transparent huge pages without requiring any system configuration or preallocated huge page pool. Pass a non-zero value in the config value `enable_thp` and the allocator will advise the kernel to back the memory mappings with transparent huge pages using `madvise(MADV_HUGEPAGE)`. Unlike `enable_huge_pages` this does not affect page size or commit/decommit behaviour. The two modes are mutually exclusive, with `enable_huge_pages` taking precedence.
# Quick overview
The allocator uses separate heaps for each thread and partitions memory blocks according to a preconfigured set of size classes, up to 2MiB. Huge blocks above this limit are mapped and unmapped directly. Blocks are allocated from a `page` of multiple blocks, all of the same size class. Each `page` is one of four page types, small, medium-small, medium-large or large, depending on the size class. Each `page` belongs to an even larger `span` of pages, each of the same page type.
# Implementation details
The allocator is based on a fixed span alignment of 256MiB, a fixed page alignment per page type, and 16 byte block alignment within the page. The span alignment is achieved by oversizing the memory mapping and aligning the returned virtual memory address to the required boundary (on POSIX systems the excess address range is immediately unmapped). By aligning to fixed sizes the free operation can locate the span and page headers without having to do a table lookup by simply masking out the low bits of the block address.
Memory blocks are divided into the four page types. Small pages have blocks in [0, 4096] bytes, medium-small pages blocks in (4096, 32768] bytes, medium-large pages blocks in (32768, 262144] bytes, and large pages blocks in (262144, 2097152] bytes. The page types use page sizes of 64KiB, 1MiB, 4MiB and 16MiB respectively, and are further divided in block size classes, where the smallest block sizes have a fixed granularity and interval of 16 bytes, and larger blocks have a variable interval to limit overhead to a fixed ratio.
Each span belongs to a single heap that owns all contained pages and blocks. To avoid locks, each page is completely owned by the allocating thread, and all cross-thread deallocations will be deferred to the owner thread through a separate atomic free list per page.
# Memory mapping
By default the allocator uses OS APIs to map virtual memory pages as needed, either `VirtualAlloc` on Windows or `mmap` on POSIX systems. If you want to use your own custom memory mapping provider you can use __rpmalloc_initialize__ or __rpmalloc_initialize_config__ and pass a memory interface with function pointers to map, commit, decommit and unmap virtual memory. The map function should reserve the requested number of bytes, the commit and decommit functions control whether a range of the mapping is backed by physical memory, and the unmap function should release the entire mapped range. If you provide either a map or unmap function you must provide both, or the default implementation will be used for all functions.
The returned memory address from the memory map function MUST be aligned to the requested alignment, which is either zero or the span size. The map function can store an alignment offset and the actual mapped size in the provided output parameters, which will be passed back to the unmap function when the entire mapped range is released.
Memory mapping requests are always done in multiples of the memory page size. You can specify a custom page size when initializing rpmalloc with __rpmalloc_initialize_config__, or pass 0 to let rpmalloc determine the system memory page size using OS APIs. The page size MUST be a power of two.
On macOS mmap requests are tagged with tag 240 for easy identification with the vmmap tool.
# Memory fragmentation
There is no memory fragmentation by the allocator in the sense that it will not leave unallocated and unusable "holes" in the memory pages by calls to allocate and free blocks of different sizes. This is due to the fact that the memory pages allocated for each size class is split up in perfectly aligned blocks which are not reused for a request of a different size. The block freed by a call to `rpfree` will always be immediately available for an allocation request within the same size class.
However, there is memory fragmentation in the meaning that a request for x bytes followed by a request of y bytes where x and y are at least one size class different in size will return blocks that are at least one memory page apart in virtual address space. Only blocks of the same size will potentially be within the same memory page span.
rpmalloc keeps an "active page" and free list for each size class. This means back-to-back allocations will most likely be served from within the same memory page (unless the page runs out of free blocks). The rpmalloc implementation will also use any "holes" in semi-filled memory pages before initializing blocks in a new page.
# First class heaps
rpmalloc provides a first class heap type with explicit heap control API. Heaps are maintained with calls to __rpmalloc_heap_acquire__ and __rpmalloc_heap_release__ and allocations/frees are done with __rpmalloc_heap_alloc__ and __rpmalloc_heap_free__. See the `rpmalloc.h` documentation for the full list of functions in the heap API. The main use case of explicit heap control is to scope allocations in a heap and release everything with a single call to __rpmalloc_heap_free_all__ without having to maintain ownership of memory blocks. Note that the heap API is not thread-safe, the caller must make sure that each heap is only used in a single thread at any given time.
# Producer-consumer scenario
Compared to the some other allocators, rpmalloc does not suffer as much from a producer-consumer thread scenario where one thread allocates memory blocks and another thread frees the blocks. In some allocators the free blocks need to traverse both the thread cache of the thread doing the free operations as well as the global cache before being reused in the allocating thread. In rpmalloc the freed blocks are deferred to the owning thread and will be reused as soon as that thread needs more blocks. This enables faster release of completely freed memory pages as blocks in a memory page will not be aliased between different owning threads.
# Best case scenarios
Threads that keep ownership of allocated memory blocks within the thread and free the blocks from the same thread will have optimal performance.
Threads that have allocation patterns where the difference in memory usage high and low water marks fit within the free page retention thresholds in the allocator will never decommit or recommit memory pages and have optimal performance.
# Worst case scenarios
Since each heap maps a span of memory pages per page type, a thread that allocates just a few blocks of each size class (16, 32, ...) for many size classes will commit a memory page for each used size class, while only using a small fraction of the committed memory. However, memory pages are committed on demand and blocks are initialized only as needed, so the actual amount of touched physical memory is far lower than the mapped address space. Free memory pages are reused by all size classes of the same page type.
Threads that perform a lot of allocations and deallocations in a pattern that have a large difference in high and low water marks, and that difference is larger than the free page retention thresholds, will cause memory pages to be repeatedly decommitted and recommitted. This can be mitigated by setting `disable_decommit` in the configuration (at the cost of higher average memory overhead).
# Caveats
To guarantee the fixed span alignment the implementation oversizes the memory mapping and aligns the returned address to the span size boundary. On POSIX systems the excess address range is immediately unmapped, while on Windows the entire mapping is retained as the OS does not support partial release. Since the extra memory pages are never touched the oversized mapping will not result in extra physical memory usage, but rather only increase virtual memory address space.
All entry points assume the passed values are valid, for example passing an invalid pointer to free would most likely result in a segmentation fault. __The library does not try to guard against errors!__.
# Other languages
[Johan Andersson](https://github.com/repi) at Embark has created a Rust wrapper available at [rpmalloc-rs](https://github.com/EmbarkStudios/rpmalloc-rs)
[Stas Denisov](https://github.com/nxrighthere) has created a C# wrapper available at [Rpmalloc-CSharp](https://github.com/nxrighthere/Rpmalloc-CSharp)
# License
This project is licensed under the SPDX expression:
```
SPDX-License-Identifier: Unlicense OR MIT
```
You may choose to use it either as public domain software under
[The Unlicense](LICENSES/Unlicense.txt), or under the [MIT License](LICENSES/MIT.txt),
at your option. The full license texts are available in the [`LICENSES/`](LICENSES)
directory.
mjansson-rpmalloc-beef233/benchmark/ 0000775 0000000 0000000 00000000000 15225717161 0017605 5 ustar 00root root 0000000 0000000 mjansson-rpmalloc-beef233/benchmark/README.md 0000664 0000000 0000000 00000002755 15225717161 0021075 0 ustar 00root root 0000000 0000000 # Benchmark results and graphs
This directory holds the captured benchmark results and the script that turns them into the
graphs shown in the [README](../README.md) and [BENCHMARKS](../BENCHMARKS.md) documents.
- `results/rptest-threads.csv` - rptest throughput sweep (random `[16, 8000]` bytes, linear
falloff, cross-thread frees) for 1 to 16 threads, one row per allocator and thread count.
The exact rptest command is recorded as a comment on the first line.
- `results/mimalloc-bench-allt.csv` - the [mimalloc-bench](https://github.com/daanx/mimalloc-bench)
`allt` suite results, in the mimalloc-bench `benchres.csv` format
(`benchmark allocator elapsed rss user sys page-faults page-reclaims`).
- `images/` - the generated PNG graphs.
- `plot.py` - regenerates the graphs from the result files. Requires `matplotlib`.
Both result files are complete and unfiltered. For readability the graphs omit allocators
that are more than 4x slower than rpmalloc on a given benchmark, and drop allocators that
failed many benchmarks entirely; this filtering happens in `plot.py`, not in the data. In the
`allt` data a benchmark an allocator crashed on is recorded with a zero or blank elapsed time,
or with zero user+sys CPU time when it died before doing any work; `plot.py` treats both as
failures rather than instant results.
```
python3 plot.py
```
See [BENCHMARKS.md](../BENCHMARKS.md) for the machine configuration, the allocator list and
versions, and instructions for regenerating the underlying data.
mjansson-rpmalloc-beef233/benchmark/images/ 0000775 0000000 0000000 00000000000 15225717161 0021052 5 ustar 00root root 0000000 0000000 mjansson-rpmalloc-beef233/benchmark/images/allt-rss.png 0000664 0000000 0000000 00003243435 15225717161 0023337 0 ustar 00root root 0000000 0000000 ‰PNG
IHDR Ê
ˆ®gÉ :tEXtSoftware Matplotlib version3.11.0, https://matplotlib.org/•Ç¥o pHYs gŸÒR IDATxœìÝw\VåÿÇñ7Ó�ŠD' ¸Å‘3GŽ2µ™šYîÊ´]6¾ee–YÙ°,Ì´¬4Íi¹Å-î�Š\€ Cežß<8?nY7‚ñz><ò¾Îu®ës®ûÜ÷9�ó¹¯cc†! à?ζ îe P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €"�D $Ê H Q E‰2 (H” @‘@¢ Še P$�( €{Ú¹sçäëë+___íÚµ« ÃÉ•ì¶å¿²{öì1·ãĉN�¹—Æ¡¨ì›…Õ½´¯ä·1cÆÈ××W¯½öÚ·oŽçÒ¥Kó0: àåÅg¤°+ÌÛ¸{÷nùúúªoß¾2#ÝòÂû½¨ dzÿþòõõÕG}t×ûþ¯IHHP·nÝÔ¢Et8 ("ì: 7nÞ¼©={öH’¢¢¢
8šÜÉn[þ+ÛmnÇ�7
8š‚s/�CQÙ7«{i_Éo���Ú³g�jÔ¨qÇm$''›ãšG‘…C^|FòÂ;ï¼£•+Wš¯Ÿ{î9
><ÛõF�½{÷š¯ßÿ}õìÙÓ¢NvÛx{ßi9::ªlÙ²òññQ¯^½Ô±cÇì7&^xáíÙ³GO?ý´lllÒ-/,ïÏEAŽçáÇ(__ß»Þ÷�ƒƒƒ:tè ·ß~[/¿ü²–/_^Ð! Q �¯Ž;¦'žxB’4{öl5nܸ€#
¿aÆéèÑ£zðÁ5yòä‚ȑӧO› i’4cÆŒleBBB4{öl%''›eááá¹î;#«VÒ§Ÿ~ªN�:iÁ‚ªT©RŽû¹Ý¢E‹´eËU¯^]#GŽÌu{€5rr¬(ÌÇ• &è‹/¾ÐŠ+´nÝ:uéÒ¥ C À‰2 ¸§Õ¨QC’$//¯Ž@FbccÍ›–ÑÑÑ
îe³fÍRtt´Ê—/_С代G�jÏž=òöö.èPp)lŸ{{{%%%iÿþý:xð 5j”iÝŸþYÉÉÉrttT|||¦õr²�;vì����ù:22RgΜ‘ŸŸŸvíÚ¥
6èá‡Ö¶mÛdk{çO¨7Co½õ–¤”Yeï¸-X¯°íï!'ÇŠÂ|\)]º´ÆŽ«?üP“&MÒÎ�;:$ üÇ‘( €{ZñâÅ™ò Š"�¬¶Ïˆ“““š7o®õë×kîܹš>}z¦uçÎ�+Izøá‡µxñâLëåd›7o.{ûô—?GŽ©‡zH«VÒÎ�;µaÆ\Í`±víZÊÞÞ^ƒ¾ãv�3…mGî6L~ø¡víڥݻwóÿx ÈWwþS ÈBê#—æÏŸ¯ÄÄÄëìØ±CÇ�—ƒƒƒ†’ï1ÙØØhܸqæëýû÷窽o¿ýV’Ô[7U®\9WmE•§§§Z¶l)éÿ?S @~aF ä‹={öh̘1’¤_ýUžžžÚ³g�fÏžÀÀ@/^\ZµÒ„ äììl®§yóæiíÚµ
‘‹‹‹úõë§Ç\666éú9wîœúõë'Iš9s¦yqõnÇ!IIIIÚ±c‡V¯^ ]¼xQ‰‰‰ruuU›6m4tèPU¬X1wk¥Ã‡kÁ‚Ú»w¯"""äìì,
0@Zµ²ª�¨¨(-\¸Pþþþ
Vbb¢ªU«¦zõêé±ÇSݺuó,Þ�;wjΜ9:qâ„lmmÕ¨Q#=õÔSjذa¶ëFFFê÷ßצM›téÒ%†!wwwuíÚU�?þ¸Š/žázíG�Õ¬Y³täÈ%%%©~ýúzê©§¬úEk^ŒWnúÏJAî›[·VTT”ùzäÈ‘*Uª”E�Ù³g«qãÆyÚoFïoAìgÒÝÿ+W®è©§žÒÕ«Wåìì¬Ù³g«F�V¯ŸÑxø &Ožœ£m¹=¦ˆˆÍž=[›7oVdd¤êÔ©£±cǪE‹ëhöìÙæç&õ˜iM2@^�_~Ç«_ýUëׯ×Å‹ecc£jÕª©k×®zì±Ç²ü|ßkTT”æÍ›§�7*$$DuëÖÕO<¡7ÞxC’ußw©m¶iÓF_~ùe–uo—Ýg$7ûæ�êׯŸž}öY]¹rEÿþû¯zè¡tu~úé'I)ûv¥J•rµ�Ö*W®œùïÜ<*)22RË—/—$=úè£wÜNZw²OÆÄĨsçÎJNNÖäÉ“õàƒ¦«óä“OêÈ‘#’¤o¾ù&ÃsÂ^½zéòåË:t¨&Nœ˜a|yõÙÎèó2oÞ<+Gɺ}áNŽ5w"<<\?üðƒ¶mÛ¦k×®ÉÝÝ]½{÷Ö Aƒ¬z¬WNÇ4'ÇŠÜWîæûýÈ#�h×®]úý÷ßõí·ßò3 ä È6l0$’Œ}ûöS¦L1lll̲ԿªU«çÎ�3Ã0Ο?oÔ«W/]IFÿþý�¤¤¤tý;v̬³fÍš‹Ã0£|ùò®“úWªT)ã÷ßÏt̲ۖ얆a$$$Ï>û¬akk›i�=ö˜›i†a~~~FÙ²e³Üž™3gfÙFfnOÞ~ûíÛ·³³3¦L™’e[sæÌÉ2ÎjÕª;wî´*Žo¾ùư³³K׆���ñÙgŸåùxåeÿÙ)È}3£mºýoóæÍ¹Ú¾Œ–ýÌ0r?þ·oËíNœ8aÔ¬YÓ�d¸¹¹°zœ2ëãÃ?L÷]ùÃ?˜õÏœ9c´nÝ:Óm²³³3Þzë-#999Ãþf̘a8::f9.cÆŒI·^ÇŽ
IF¿~ý2Ý–¹sç%J”ȰÍ'žxÂˆŠŠÊp›ÒÊíöyyy™ÛpòäI£nݺ¶ÓªU+#22ÒbÝgŸ}6ÛÏÌ�!C2Ýþ̤�éàÁƒ†»»{†ß7_}õ•¹Î+¯¼’aÿ+V4>œi_y9~ùë¿ÿþk¸¸¸dùùÞ¾}»UãzàÀ£Zµjë7oÞ܈��5œ��
IƸqã²z›ŒÝ»w›ëÎ�;7˺Éî3’›}3'†bH2œ��
Ã0Œ§Ÿ~Ú�RΣnwóæMó;vÉ’%ÆæÍ›Í8~þùçocjß’Œ„„„Lcœ2eŠYoýúõw¶¡†a,[¶Ìl'«}ÍšØ
#wûdƒ
IÆèÑ£Ó-‹‰‰1ÌvÞyç�tuΟ?o._¼xqºåyùÙÎìó’Ù�ç�k¬‘v[öìÙ“é{ÖªU+#444ÓvîtLsr¬¸ÓãJA¼ß7n4—ûûûçä- r„D ä‹´7]'MšdØÚÚ#GŽ4V®\ilٲŘ2eŠQ²dIC’qß}÷·nÝ2¼½½�êÕ«_~ù¥áïïo¬X±ÂèÞ½»Ùηß~›®Ÿœ$Êäg†aF�=ŒéÓ§,0üýý�eË–|ð�Q©R%ó‚ò–-[2\?/e†
fÖiÛ¶ñË/¿[·n5.\h±
<ð@¦ ?ü±YÏÕÕÕxï½÷Œÿýר¼y³ñ믿o¿ý¶áééi|ôÑG®Ÿ�´ïÉË/¿lH)É;K—.5¶lÙbÌš5ËðôôÌv¼g̘aq¡ýÛo¿5üýý
cúôéFÕªU
)% áøñãYÆñÞ{ï666Æã�?n,Y²Äغu«ñã�?5jÔ0$¶¶¶™&BÜéxåUÿÖ(È}s÷îÝÆ¼yóÌå~~~F@@€Å_ttôo[f
Ë~f¹ÿ¬evìØaT¬XÑ�dx{{gÏžÍõx½öÚkæx-Y²Äعs§`Þè;þ¼yCÐÙÙÙxõÕW�•+WÛ¶m3.\h<ôÐCf[o¿ývº¾üýýÍåM›65fΜi¬^½Úð÷÷7~ûí7ãƒ>0š5kf4(ݺÙÝýûï¿ÍDÁ*UªŸþ¹áïïoüóÏ?Ƹqã;;;ãÕW_5ûÏ(Q&·ÛgÿspèС†§§§áååe|ûí·ÆæÍ›�•+W�=ö˜ÙÆÈ‘#-Ö=wîœ`&löìÙ3ÝgæôéÓÙ¾§™Å4xð`£F�FãÆ��9sæ[·n5þøã£yóææ÷;}ûŒ©S§vvvƘ1cŒ•+W›7o6&Ožl+VÌ|ï2º9š—ã—ß±nÚ´ÉL([¶¬ñþûï6l0Ö[g¼ùæ›æùA‰%2M@K�õñÇ7ªU«fÔ[ט9s¦±yóf# À8zô¨a†ñÜsÏ™c’U²ê˜1cIF™2e²Mj͈µ‰2w²oæÄí‰2›6m2$ÅŠ3®]»fQ÷·ß~3¤”¤¦øøø|O”‰��7~úé'£xñâ†$£uëÖw¼�†a˜Ç˜2eÊdznemì¹Ý'ŸþyC’Q»vítËVZe‘¤Ð¶mÛtuæÌ™c~¶ÂÃÃ-–ååg;»Ï‹µ²ÏÜk¬‘v[ÜÝÝ�Úµkß}÷�±eËcÙ²eÆàÁƒÍþ}}}3Üs3¦99VÜÉq¥ ÞïØØXÃÞÞÞ�dLž<ùŽÞ À$Ê _¤½éjgggÌ›7/]�_~ùŬӣGÃÓÓÓ³¨“��`øúúš7�o—“D™üŒÃ0#"""ÓñˆŒŒ4š4ibH2:vì˜a�Ü&Êüý÷ßæòÁƒgxS0õŠ$ã»ï¾K·|÷îÝæMæÖ[§»™•Öõë×3]–•Ûß“�?þ8]�èèh£Y³f†$ÃÉÉ)Ý/q:dÞHzæ™g2¼1j&BtëÖ-Û8fÍš•®Îùóç�R¥J™cz»ÜŒW^ôo‚Þ7Ìåù1{LF
Ë~f¹ÿÌe–/_nÞ4mÓ¦Mº›š9qûx͘1#Óº÷ß¿!Éððð0Μ9“a�7ÞxüÙzòäI‹e#GŽ4$µjÕ2nܸ‘i?Íf‘ÕMÑøøxsæãâÅ‹éê,Z´È¼—Y¢Ln·Ï0þÿæ ���ѶmÛ
dH2ÒsÃ0“Aîdö˜Œ¤�©S§NÆ[·,–GEEnnn†$ãþûï7J”(a,\¸0];ß}÷�9~7nL·<¯Ç/¿bMLL4gbªT©R†q˜Ÿ±¦M›f¸-icmÑ¢E¦‰‡2cùé§Ÿ2¬c”)SÆ�î|–ker³oZãöDÃ0ŒÚµkRúÖzôèaH2žþyÃ0Œ