Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mimalloc: additional integration and changes for --disable-gil builds #112532

Open
Tracked by #108219
colesbury opened this issue Nov 29, 2023 · 0 comments
Open
Tracked by #108219

mimalloc: additional integration and changes for --disable-gil builds #112532

colesbury opened this issue Nov 29, 2023 · 0 comments
Assignees
Labels
3.13 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-free-threading type-feature A feature request or enhancement

Comments

@colesbury
Copy link
Contributor

colesbury commented Nov 29, 2023

Feature or enhancement

Mimalloc was added as an allocator in #90815. The --disable-gil builds need further integration with mimalloc, as well as some modifications to mimalloc to support thread-safe garbage collection in --disable-gil builds and the dictionary accesses that mostly avoid locking.

These changes can be split up across multiple PRs.

  • Currently, when mimalloc is enabled, all allocations go to the default heap. This is fine for PyMem_Malloc calls, but we need separate heaps for PyObject_Malloc and PyObject_GC_New. We should associate some mi_heap_ts with each PyThreadState. Every PyThreadState needs four heaps: one for PyMem_Malloc, one for non-GC objects (via PyObject_Malloc), one for GC objects with managed dicts (extra pre-header) and one for GC objects without a managed dict. We need some way to know which heap to use in _PyObject_MiMalloc. There's not a great way to do this, but I suggest adding something like a "current pyobject heap" variable to PyThreadState. It should generally point to the PyObject_Malloc heap, but PyObject_GC_New should temporarily override it to point to the correct GC heap when called
  • --disable-gil should imply --with-mimalloc and require mimalloc (i.e., disallow changing the allocator with PYTHONMALLOC).
  • We should tag each mi_heap_t and mi_page_t with a number identifying which type of allocation it's associated with. This is important for when pages are abandoned (i.e., when a thread exits with live blocks remaining) and the page is no longer associated with a heap. The GC still needs to identify which of those pages store GC-enabled objects. (see colesbury/nogil-3.12@d447b69)
  • When claiming a page from an abandoned segment, mimalloc should associate it with the correct heap from the current thread. In other words, pages that store GC-enabled objects should only be put back in the correct GC heap.

cc @DinoV

Linked PRs

@colesbury colesbury added type-feature A feature request or enhancement interpreter-core (Objects, Python, Grammar, and Parser dirs) 3.13 new features, bugs and security fixes topic-free-threading labels Nov 29, 2023
colesbury added a commit to colesbury/cpython that referenced this issue Dec 8, 2023
In `--disable-gil` builds, the default allocator is now "mimalloc" and
the "malloc" and "pymalloc" allocators are disabled.
colesbury added a commit to colesbury/cpython that referenced this issue Dec 18, 2023
In `--disable-gil` builds, we now use four separate heaps in anticipation
of using mimalloc to find GC objects when the GIL is disabled. To support
this, we also make a few changes to mimalloc:

* Heap and mi_tld_t initialization is split from allocation. This allows
  us to have a per-PyThreadState mi_tld_t, which is important to keep
  interpreter isolation, since the same OS thread may run in multiple
  interpreters (using different PyThreadStates.)
* The pool of abandoned segments is refactored into its own struct. This
  allows us to use different pools for different interpreters so that
  we can preserve interpreter isolation.
* Heap abandoning (mi_heap_collect_ex) can now be called from a different
  thread than the one that created the heap. This is necessary because
  we may clear and delete the containing PyThreadStates from a different
  thread during finalization and after fork().
colesbury added a commit to colesbury/cpython that referenced this issue Dec 18, 2023
In `--disable-gil` builds, we now use four separate heaps in anticipation
of using mimalloc to find GC objects when the GIL is disabled. To support
this, we also make a few changes to mimalloc:

* Heap and mi_tld_t initialization is split from allocation. This allows
  us to have a per-PyThreadState mi_tld_t, which is important to keep
  interpreter isolation, since the same OS thread may run in multiple
  interpreters (using different PyThreadStates.)
* The pool of abandoned segments is refactored into its own struct. This
  allows us to use different pools for different interpreters so that
  we can preserve interpreter isolation.
* Heap abandoning (mi_heap_collect_ex) can now be called from a different
  thread than the one that created the heap. This is necessary because
  we may clear and delete the containing PyThreadStates from a different
  thread during finalization and after fork().
colesbury added a commit to colesbury/cpython that referenced this issue Dec 18, 2023
In `--disable-gil` builds, we now use four separate heaps in anticipation
of using mimalloc to find GC objects when the GIL is disabled. To support
this, we also make a few changes to mimalloc:

* Heap and mi_tld_t initialization is split from allocation. This allows
  us to have a per-PyThreadState mi_tld_t, which is important to keep
  interpreter isolation, since the same OS thread may run in multiple
  interpreters (using different PyThreadStates.)

* Heap abandoning (mi_heap_collect_ex) can now be called from a different
  thread than the one that created the heap. This is necessary because
  we may clear and delete the containing PyThreadStates from a different
  thread during finalization and after fork().
colesbury added a commit to colesbury/cpython that referenced this issue Dec 18, 2023
In `--disable-gil` builds, we now use four separate heaps in
anticipation of using mimalloc to find GC objects when the GIL is
disabled. To support this, we also make a few changes to mimalloc:

* `mi_heap_t` and `mi_tld_t` initialization is split from allocation.
  This allows us to have a `mi_tld_t` per-`PyThreadState`, which is
  important to keep interpreter isolation, since the same OS thread may
  run in multiple interpreters (using different PyThreadStates.)

* Heap abandoning (mi_heap_collect_ex) can now be called from a
  different thread than the one that created the heap. This is necessary
  because we may clear and delete the containing PyThreadStates from a
  different thread during finalization and after fork().
colesbury added a commit to colesbury/cpython that referenced this issue Dec 19, 2023
Mimalloc segments are data structures that contain memory allocations along
with metadata. Each segment is "owned" by a thread. When a thread exits,
it abandons its segments to a global pool to be later reclaimed by other
threads. This changes the pool to be per-interpreter instead of process-wide.

This will be important for when we use mimalloc to find GC objects in the
`--disable-gil` builds. We want heaps to only store Python objects from a
single interpreter. Absent this change, the abandoning and reclaiming process
could break this isolation.
corona10 pushed a commit that referenced this issue Dec 26, 2023
* gh-112532: Use separate mimalloc heaps for GC objects

In `--disable-gil` builds, we now use four separate heaps in
anticipation of using mimalloc to find GC objects when the GIL is
disabled. To support this, we also make a few changes to mimalloc:

* `mi_heap_t` and `mi_tld_t` initialization is split from allocation.
  This allows us to have a `mi_tld_t` per-`PyThreadState`, which is
  important to keep interpreter isolation, since the same OS thread may
  run in multiple interpreters (using different PyThreadStates.)

* Heap abandoning (mi_heap_collect_ex) can now be called from a
  different thread than the one that created the heap. This is necessary
  because we may clear and delete the containing PyThreadStates from a
  different thread during finalization and after fork().

* Use enum instead of defines and guard mimalloc includes.

* The enum typedef will be convenient for future PRs that use the type.
* Guarding the mimalloc includes allows us to unconditionally include
  pycore_mimalloc.h from other header files that rely on things like
  `struct _mimalloc_thread_state`.

* Only define _mimalloc_thread_state in Py_GIL_DISABLED builds
corona10 added a commit to corona10/cpython that referenced this issue Dec 26, 2023
corona10 added a commit that referenced this issue Dec 26, 2023
colesbury added a commit to colesbury/cpython that referenced this issue Jan 4, 2024
Mimalloc segments are data structures that contain memory allocations along
with metadata. Each segment is "owned" by a thread. When a thread exits,
it abandons its segments to a global pool to be later reclaimed by other
threads. This changes the pool to be per-interpreter instead of process-wide.

This will be important for when we use mimalloc to find GC objects in the
`--disable-gil` builds. We want heaps to only store Python objects from a
single interpreter. Absent this change, the abandoning and reclaiming process
could break this isolation.
DinoV pushed a commit that referenced this issue Jan 4, 2024
* gh-112532: Isolate abandoned segments by interpreter

Mimalloc segments are data structures that contain memory allocations along
with metadata. Each segment is "owned" by a thread. When a thread exits,
it abandons its segments to a global pool to be later reclaimed by other
threads. This changes the pool to be per-interpreter instead of process-wide.

This will be important for when we use mimalloc to find GC objects in the
`--disable-gil` builds. We want heaps to only store Python objects from a
single interpreter. Absent this change, the abandoning and reclaiming process
could break this isolation.

* Add missing '&_mi_abandoned_default' to 'tld_empty'
colesbury added a commit to colesbury/cpython that referenced this issue Jan 5, 2024
Mimalloc pages are data structures that contain contiguous allocations
of the same block size. Note that they are distinct from operating
system pages. Mimalloc pages are contained in segments.

When a thread exits, it abandons any segments and contained pages that
have live allocations. These segments and pages may be later reclaimed
by another thread. To support GC and certain thread-safety guarantees in
free-threaded builds, we want pages to only be reclaimed by the
corresponding heap in the claimant thread. For example, we want pages
containing GC objects to only be claimed by GC heaps.

This allows heaps and pages to be tagged with an integer tag that is
used to ensure that abandoned pages are only claimed by heaps with the
same tag. Heaps can be initialized with a tag (0-15); any page allocated
by that heap copies the corresponding tag.
@colesbury colesbury self-assigned this Jan 5, 2024
DinoV pushed a commit that referenced this issue Jan 5, 2024
* gh-112532: Tag mimalloc heaps and pages

Mimalloc pages are data structures that contain contiguous allocations
of the same block size. Note that they are distinct from operating
system pages. Mimalloc pages are contained in segments.

When a thread exits, it abandons any segments and contained pages that
have live allocations. These segments and pages may be later reclaimed
by another thread. To support GC and certain thread-safety guarantees in
free-threaded builds, we want pages to only be reclaimed by the
corresponding heap in the claimant thread. For example, we want pages
containing GC objects to only be claimed by GC heaps.

This allows heaps and pages to be tagged with an integer tag that is
used to ensure that abandoned pages are only claimed by heaps with the
same tag. Heaps can be initialized with a tag (0-15); any page allocated
by that heap copies the corresponding tag.

* Fix conversion warning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-free-threading type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

1 participant