Optimise the way tracemalloc and PyRefTracer hooks work #125790
Copy link
Copy link
Open
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)performancePerformance or resource usagePerformance or resource usagetype-featureA feature request or enhancementA feature request or enhancement
Description
In #125703 @markshannon has raised that he is unhappy about the performance implications of where these hooks are placed and in a call we discussed that he has some ideas on how to make them more performant by moving them elsewhere or adapting then.
I am opening this issue to track and sync about these improvements for 3.14 and beyond.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)performancePerformance or resource usagePerformance or resource usagetype-featureA feature request or enhancementA feature request or enhancement
Activity
markshannon commentedon Oct 25, 2024
I think we can fix the performance issues by raising the level at which allocation/free goes through a function pointer.
Instead of a malloc-like interface
void *malloc(size_t size), we should be returning partially initialized objects.PyObject *obj_malloc(PyTypeObject *tp, size_t size, size_t presize)would allocate a chunk of memorysize + presize, returning aPyObject *pointing to that memory +presize, with theob_typefield set totpand theob_refcountset to one.This is low-enough level to be fully general, but with enough context to support tracemalloc.
I think we would need the following implementations, switchable at runtime:
We don't need (or want) to switch between the free-threading and default allocators, but it keeps the rest of the code simpler if they have the same interface.
markshannon commentedon Apr 7, 2025
Having played around with this a bit, I'm going with a slightly different design.
All allocations and deallocations will go through a single (inline) function taking the threadstate as well as the type and sizes.
This will allocate from per-size freelists, then fall back to the basic malloc/free functions.
This works as we can disable, or re-enable, the freelists when
PyRefTracer_SetTracerorPyMem_SetAllocatorare called.