Skip to content

Offer mimalloc as the process allocator of the solo stub - #14

Open
buyemune-sys wants to merge 2 commits into
pg83:mainfrom
buyemune-sys:feat/mimalloc
Open

buyemune-sys wants to merge 2 commits into
pg83:mainfrom
buyemune-sys:feat/mimalloc

Conversation

@buyemune-sys

@buyemune-sys buyemune-sys commented Oct 17, 2026 •

Copy link
Copy Markdown

Why

Under solo, all of the process's malloc is served by musl's mallocng, which takes a single lock for every call. Multithreaded guests hit it first. Measured on a glibc program with N threads, 4M malloc/free per thread, 2 cores:

threads glibc native solo, mallocng solo, mimalloc
1 0.153 s 0.283 s 0.048 s
2 0.158 s 1.103 s 0.051 s
4 0.323 s 1.799 s 0.097 s

On Tarantool 3.8.1 (a glibc binary in a solo bundle), an ffi malloc/free loop is 2× slower than native with mallocng and 4× faster than native with mimalloc. Other workloads (memtx, net.box, LuaJIT) stay within noise either way.

Why in the stub and not in the guest

An allocator linked into the guest (MI_OVERRIDE inside a glibc program) breaks under solo. musl's internal allocations (strdup, realpath(NULL), asprintf, getaddrinfo, …) bypass it and later reach its free():

strdup in mimalloc heap: 1, realpath: 0, asprintf: 0
mimalloc: error: mi_free: invalid pointer: 0x7AEEE26610B0
Segmentation fault

Natively on glibc this works, because glibc itself calls malloc through the PLT. When mimalloc replaces malloc in the stub itself, musl, the bridge and every guest share one heap.

What's done

  • -Dmimalloc (opt-in; the default build is unchanged) links the vendored mimalloc 3.5.3 (ext/mimalloc, tag v3.5.3 = d4881d33) into the stub in place of mallocng.
  • mimalloc is compiled as C++ (upstream's standard MI_USE_CXX mode). Its atomics then come from the vendored libc++ <atomic>, so the -nostdinc world does not need the compiler's <stdatomic.h>.
  • Transparent huge pages are off (MI_DEFAULT_ALLOW_THP=0): that is the host's decision.
  • README: mimalloc is added to the list of vendored versions.

Test

  • tst/glibc_guest_test.c gains a check of a property that must hold for any allocator the stub links: the guest frees memory musl allocated on its behalf (realpath(NULL), asprintf) and memory allocated by another thread. Needle: guest heap shared. It passes on main too (one mallocng there); its job is to catch a wrongly wired allocator.
  • The Alpine job gains a Test with mimalloc step (./build -Dmimalloc test).
  • Locally both ./build test and ./build -Dmimalloc test pass. In the stub, nm shows malloc == mi_malloc and no mallocng symbols.

@buyemune-sys

buyemune-sys commented Oct 17, 2026 •

Copy link
Copy Markdown
Author

This one is meh, I'll understand if you'll close it as 'wontfix'.

buyemune-sys and others added 2 commits October 20, 2026 15:42
Security updates replaced libapparmor1 4.0.1really4.0.1-0ubuntu0.24.04.7
with .9 and libexpat1 2.6.1-2ubuntu0.4 with ubuntu0.6, and the pool kept
only the new files, so the cold-cache Fetch x86_64 job answers 404 and
every x86-64 job behind it is skipped. Both pins move to the versions
noble-updates ships now; their SHA256s match the archive's Packages index.
The other twelve pins are still served.
With -Dmimalloc the stub links the vendored mimalloc 3.5.3 in place of
musl's mallocng. It replaces malloc for the whole process: musl's own
callers, the glibc bridge and every guest share one heap, so memory
still crosses the bridge in either direction — which an allocator
linked into a guest could not offer, since musl's internal allocations
(strdup, realpath, asprintf, getaddrinfo) would bypass it and come back
to its free().

mallocng takes one lock for every call. On a glibc program that churns
malloc/free from N threads, 4M operations per thread on two cores:

  threads  glibc native  solo, mallocng  solo, mimalloc
  1        0.153 s       0.283 s         0.048 s
  2        0.158 s       1.103 s         0.051 s
  4        0.323 s       1.799 s         0.097 s

mimalloc compiles as C++ (upstream's MI_USE_CXX) so its atomics come
from the vendored libc++ rather than a compiler <stdatomic.h> the
-nostdinc world cannot see. Transparent huge pages stay off, the host's
decision. The default build is unchanged.

The guest test now frees memory musl allocated for it and memory
another thread allocated, which must hold for any allocator the stub
links, and the Alpine job runs the tests once more with -Dmimalloc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant