summaryrefslogtreecommitdiffstats
path: root/Objects/mimalloc/segment.c
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2024-01-22 21:10:21 (GMT)
committerGitHub <noreply@github.com>2024-01-22 21:10:21 (GMT)
commit412920a41efc6f3307e710d5ce61bfe00c0f3c11 (patch)
tree578dadd87be637f110278f79f15b0ea0fa2eefb9 /Objects/mimalloc/segment.c
parente45bae7a45e5696c3ebdf477ecc948374cf8ebff (diff)
downloadcpython-412920a41efc6f3307e710d5ce61bfe00c0f3c11.zip
cpython-412920a41efc6f3307e710d5ce61bfe00c0f3c11.tar.gz
cpython-412920a41efc6f3307e710d5ce61bfe00c0f3c11.tar.bz2
gh-112532: Improve mimalloc page visiting (#114133)
This adds support for visiting abandoned pages in mimalloc and improves the performance of the page visiting code. Abandoned pages contain memory blocks from threads that have exited. At some point, they may be later reclaimed by other threads. We still need to visit those pages in the free-threaded GC because they contain live objects. This also reduces the overhead of visiting mimalloc pages: * Special cases for full, empty, and pages containing only a single block. * Fix free_map to use one bit instead of one byte per block. * Use fast integer division by a constant algorithm when computing block offset from block size and index.
Diffstat (limited to 'Objects/mimalloc/segment.c')
-rw-r--r--Objects/mimalloc/segment.c50
1 files changed, 50 insertions, 0 deletions
diff --git a/Objects/mimalloc/segment.c b/Objects/mimalloc/segment.c
index d9b39b0..584233b 100644
--- a/Objects/mimalloc/segment.c
+++ b/Objects/mimalloc/segment.c
@@ -1614,3 +1614,53 @@ mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t pag
mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld));
return page;
}
+
+/* -----------------------------------------------------------
+ Visit blocks in abandoned segments
+----------------------------------------------------------- */
+
+static bool mi_segment_visit_page(mi_segment_t* segment, mi_page_t* page, bool visit_blocks, mi_block_visit_fun* visitor, void* arg)
+{
+ mi_heap_area_t area;
+ _mi_heap_area_init(&area, page);
+ if (!visitor(NULL, &area, NULL, area.block_size, arg)) return false;
+ if (visit_blocks) {
+ return _mi_heap_area_visit_blocks(&area, page, visitor, arg);
+ }
+ else {
+ return true;
+ }
+}
+
+static bool mi_segment_visit_pages(mi_segment_t* segment, uint8_t page_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
+ const mi_slice_t* end;
+ mi_slice_t* slice = mi_slices_start_iterate(segment, &end);
+ while (slice < end) {
+ if (mi_slice_is_used(slice)) {
+ mi_page_t* const page = mi_slice_to_page(slice);
+ if (page->tag == page_tag) {
+ if (!mi_segment_visit_page(segment, page, visit_blocks, visitor, arg)) return false;
+ }
+ }
+ slice = slice + slice->slice_count;
+ }
+ return true;
+}
+
+// Visit all blocks in a abandoned segments
+bool _mi_abandoned_pool_visit_blocks(mi_abandoned_pool_t* pool, uint8_t page_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
+ // Note: this is not safe in any other thread is abandoning or claiming segments from the pool
+ mi_segment_t* segment = mi_tagged_segment_ptr(pool->abandoned);
+ while (segment != NULL) {
+ if (!mi_segment_visit_pages(segment, page_tag, visit_blocks, visitor, arg)) return false;
+ segment = segment->abandoned_next;
+ }
+
+ segment = pool->abandoned_visited;
+ while (segment != NULL) {
+ if (!mi_segment_visit_pages(segment, page_tag, visit_blocks, visitor, arg)) return false;
+ segment = segment->abandoned_next;
+ }
+
+ return true;
+}