GeezerVison is showing github.com — Mimalloc differences from upstream · Issue #113141 · python/cpython
Mimalloc differences from upstream #113141DescriptionThis issue tracks divergence in our copy of mimalloc from upstream https://github.com/microsoft/mimalloc. The purpose is to help with upstreaming fixes and also so that our modifications are not lost as we pull in new mimalloc versions.
base version: v2.1.2
Simple bug fixes
Mimalloc changes to support GC
Mimalloc changes for lock-free reads (using QSBR)
UWP (Universal Windows Platform) Support
Linked PRs
Reacted by Erlend E. Aasland, Kirill Podoprigora, Furkan Onder, Itamar Oren, Sam James, Julien Jerphanion, Grechko Sergii and EDM115 Activitycorona10 commented on Dec 18, 2023 Hmm, how about generating patchfiles and storing them somewhere in the repository for records? :)
I also need feedback about the solution from @vstinner since people in Red Hat is the best expert for this area. corona10 commented on Dec 18, 2023 And which commit is our mimalloc baseline code?
I can spend time for implementing a tool that generating diff files and comparing them on the CI level if it looks okay :) corona10 commented on Dec 18, 2023 vstinner commented on Dec 18, 2023
Hmm, how about generating patchfiles and storing them somewhere in the repository for records? :)
That sounds painful to maintain. I would prefer to have a process to upstream changes first (mimalloc), and then apply them downstream (Python).
Here we wanted to move quickly to add mimalloc and fix all portability issues. But we should be more "upstream first" for following changes. corona10 commented on Dec 18, 2023 Ah okay if they accept our changes, it looks reasonable. colesbury commented on Dec 18, 2023 ContributorAuthor More actions I created this issue in anticipation of further mimalloc changes. There will be changes to support GC in the --disable-gil builds and a few for the optimistic dict accesses. We are going to need to continue to move quickly. An upstream first model is not going to work yet.
I don't think it will be too hard to pull in new mimalloc versions. I've done it a few times in the nogil forks: diff vs. base to get patch file, patch new version, fix merge conflicts. We can write up the workflow, but I think automation is overkill at this point.
I had a nice chat with Daan (mimalloc author) on Friday and we got a chance to discuss some (but not all) of the mimalloc changes to support GC. I think we'll be able to get changes merged upstream, but for non-trivial changes I think it's helpful to understand and be able to explain how APIs are used in CPython. Even if time were not an issue, I would not want to be in a situation where we propose something upstream and then it turns out we needed something slightly different. Reacted by Donghee Na, Carl Meyer, Itamar Oren, Erlend E. Aasland and Eric Snow ericsnowcurrently commented on Jan 4, 2024 Reacted by Erlend E. Aasland daanx commented on May 19, 2024 I have started upstreaming mimalloc specific modifications into mimalloc to make it easier to downstream future releases of mimalloc. The ideal goal would be to enable Python to pull in mimalloc with no further modifications -- let's see how far we can get.
For now, I am documenting here which commits fix particular issues together with the version of mimalloc that will have these commits. (last update: 2024-06-03)
Planned for v2.1.8
Mimalloc changes to support GC
Mimalloc changes for lock-free reads (using QSBR)
upstreamed in v2.1.7:
Simple bug fixes
Mimalloc changes to support GC
Reacted by Eli Schwartz, Sam James, Erlend E. Aasland, GalaxySnail, Sam Gross, Eric Snow, Itamar Oren, IceCodeNew, Yupei Liu, eihqnh and 4 more cdce8p commented on Jul 8, 2024 For reference, just opened #121487 to address a deprecation warning with ATOMIC_VAR_INIT which was already fixed upstream in v1.8.4. StanFromIreland commented on Jun 25, 2026 We have four CodeQL warnings for bundled mimalloc:
I wrote a PR upstream (microsoft/mimalloc#1321) to fix two and note the other two. corona10 commented on Oct 15, 2026 Just for record: #159111 is about upgrading mimalloc from 2.1.2 to 2.5.2 daanx commented on Oct 17, 2026 Just an update: recently started working on integrating mimalloc v3 in CPython (sorry, I hope this does not interfere with your work @corona10 -- I wasn't aware this effort was started).
I am hoping mimalloc v3 can become a "drop-in" without needing any patches from the CPython side -- so we can easily upgrade mimalloc in the future if there are security fixes or new features (like the recent profiler hooks) etc.
The main item that I added is a good interface for the quiescent-state-based-reclamation (QSBR), and I think I found a good
abstraction for this where all lifetime management is now done on the mimalloc side (and part of the regular release).
Hope this design can work as the interface is now more general and abstract (so maybe it can work for other runtimes as well).
With the new first-class heaps in mimalloc v3, we can make heaps that are QSBR enabled and for those the pages are
tracked so we never free the memory if there might still be outstanding readers. The CPython side only needs to define
helpers for the actual QSBR goals etc. This is quite minimal now (https://github.com/microsoft/mimalloc/blob/dev3/include/mimalloc/qsbr.h):
typedef struct mi_page_qsbr_s mi_page_qsbr_t;
typedef struct mi_heap_qsbr_s mi_heap_qsbr_t;
void mi_heap_qsbr_init(mi_heap_qsbr_t* heap);
void mi_page_qsbr_clear(mi_page_qsbr_t* page);
bool mi_page_qsbr_is_clear(const mi_page_qsbr_t* page);
bool mi_page_qsbr_should_delay(mi_page_qsbr_t* page, mi_heap_qsbr_t* heap, size_t page_bytes);
I implemented all this on the mimalloc side where this gets enabled by defining MI_QSBR=1.
Now, I don't know CPython very well, so I used an agent to interface CPython with the new v3 API and
created a fork here: https://github.com/daanx/cpython/tree/mimalloc-v3 (this is temporary and more
a proof of concept and to help others/Sam to integrate this; I can make it into a PR if that is better?)
The actual changes to CPython are in commit e253ecc2, while the other commits are just including the new vendored mimalloc v3 sources.
It all seems to work already :-) -- but again, I am not an expert here so someone needs
to review the changes and see if it indeed is all ok; @colesbury: hope you can take a look?
Here are some other more minor parts that were addressed as summarized by an agent:
CPython-side changes in my fork
The following are in commit e253ecc2 in the cpython fork.
-
Heaps per interpreter instead of tags. Create 4 mi_heap_t* per interpreter (mem, object, gc, gc_pre). Each thread state gets its per-thread heaps once with mi_heap_theap() and allocates through mi_theap_* . Pages never leave their heap and reclaim only happens within a heap. So tag, the per-interpreter abandoned pools and _mi_heap_init_ex / _mi_tld_init all go away. Subprocs aren't needed, and wouldn't fit: one OS thread can run thread states from several interpreters, but can only belong to one subproc.
-
Simpler GC walk. It becomes mi_heap_visit_blocks(gc_heap, ...) per interpreter, covering all threads and abandoned pages. No loop over thread states, no lock, no separate abandoned visit.
-
Smaller installed headers. pycore_mimalloc.h then only needs opaque pointers, so only mimalloc.h has to be installed.
-
Cache the per-thread theaps in the thread state. mi_heap_malloc has a single cache slot, so switching between 4 heaps would keep hitting the slow lookup. Get a mi_theap_t with mi_heap_theap(heap) in each thread state and use mi_theap_malloc.
(or mi_theap_malloc_csize if the size is statically known).
-
Already upstream in v3: the MI_PRIM_THREAD_ID macro, the configurable debug fill bytes, MI_TSAN, the UWP fix and the Fix format specifiers for long err in numa bind warning message microsoft/mimalloc#1321 CodeQL fixes. The Crash in Py_Initialize in non-main thread in free-threading build #123022 patch no longer applies.
Mimalloc v3 changes
These are already committed in the latest dev3 in mimalloc.
-
Per-heap debug-fill offset. v3 fills whole blocks on alloc and free. Py_DEBUG free-threaded builds need the object header
bytes left intact. Fixed in commit fc238b62.
-
Public "page owner thread id" call. gc_restore_tid needs it. Abandoned pages in v3 report 0 or 4, so it should return 0 for both. CPython would replace the segment lookup with: op->ob_tid = mi_thread_id_of(op);.
Added size_t mi_thread_id_of(const void* p) in commit 9dac1fec.
-
Explicit "abandon this thread's per-heap state" call. The internal version, _mi_theap_delete, is commented out. CPython needs one on the owning thread (thread state cleared while the OS thread lives on), and one usable from another thread when the owner is dead (PyThreadState_Clear of other threads, fork child).
Add mi_theap_delete and mi_theap_quiescent_abandon (commit 318f6817). Not sure in-how-far this is still needed
with first-class heaps and heap-destroy/delete. Hope we can avoid calling this.
-
Fork safety. v2 was nearly lock-free. v3 uses some pthread mutexes and has no fork handlers, so a fork can deadlock the child. In the child, dead threads' pages also stay "owned", and a new thread can reuse the same thread id. Suggest mi_fork_prepare/parent/child() or pthread_atfork. Fixed in commit 1b7e73f3, 7ea8bfc0, and c084f15a.
-
Heap-walk bug (reproduced). Live blocks in OS-allocated pages that a live thread still owns are not visited. With mi_option_disallow_arena_alloc, only 1 of 2 blocks was visited. The CPython GC would miss objects whenever mimalloc falls back to OS pages. Also, area->used over-counts blocks freed by other threads but not yet collected (fixed in 85f49529ca). fixed in commit b85f2d06, a9d447d3, f9262ecb.
-
Nice to have:
- Public size-class thresholds or stats for
_debugmallocstats. (not sure what to do?)
- Fix the one
-Wstatic-in-inline warning (mi_forall_arenas). Otherwise static.c compiles cleanly with CPython-like flags. Fixed in commit cee18e1c.
Running
This is how I tested CPython on macOS from my fork:
git clone --depth 1 --single-branch --branch mimalloc-v3 \
https://github.com/daanx/cpython.git cpython-mimalloc-v3
cd cpython-mimalloc-v3
mkdir -p build/nogil
cd build/nogil
../../configure --disable-gil
make -j8
./python.exe -VV
The version output should contain free-threading build. (On Linux, use ./python instead of ./python.exe
Simple no-GIL test
Run this from the same build directory. It checks that the GIL is actually disabled and runs four CPU-bound workers—not merely sleeping threads.
./python.exe -I - <<'PY'
import sys
import sysconfig
import threading
from concurrent.futures import ThreadPoolExecutor
assert sysconfig.get_config_var("Py_GIL_DISABLED") == 1, "Not a free-threaded build"
assert not sys._is_gil_enabled(), "GIL is enabled"
n = 1_000_000
barrier = threading.Barrier(4)
def work(_):
barrier.wait(timeout=30)
return sum(i * i for i in range(n))
with ThreadPoolExecutor(max_workers=4) as pool:
results = list(pool.map(work, range(4)))
expected = n * (n - 1) * (2 * n - 1) // 6
assert results == [expected] * 4
assert not sys._is_gil_enabled()
print("GIL enabled:", sys._is_gil_enabled())
print("PASS: four CPU-bound worker threads completed correctly")
PY
Repeat the regression tests:
./python.exe -m test -j4 --timeout 180 \
test_gc test_threading test_capi.test_mem test_free_threading corona10 commented on Oct 17, 2026 @daanx I think that if we can move on to v3 directly, it would be better solution. I think that we need to run freethreading benchmark via Meta infrastructure by @mpage. Matt could help you. corona10 commented on Oct 17, 2026 Could you send a draft PR for this? daanx commented on Oct 17, 2026
26. @daanx I think that if we can move on to v3 directly, it would be better solution. I think that we need to run freethreading benchmark via Meta infrastructure by @mpage. Matt could help you
Thank you @corona10 -- hope this didn't cause too much work. Yes, my current fork seems to work but I am not a CPython/NoGIL expert so better testing is needed and an expert needs to look at the CPython side changes at least. The mimalloc side changes are also well contained in one or two commits so that should help. daanx commented on Oct 17, 2026
27. Could you send a draft PR for this?
Yes.. but maybe it is better if some people first look at the current fork? It would perhaps be better if someone from CPython would create such PR -- I am not sure if I vendored mimalloc correctly etc. added a commit that references this issue on Nov 2, 2026gh-113141: Tune MI_SEGMENT_SLICE_SHIFT to 14 for using 16 Kib pages (g…
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|