Add per-module allocation statistics to memray stats#938
Add per-module allocation statistics to memray stats#938ivonastojanovic wants to merge 6 commits into
Conversation
e31bbd4 to
9071317
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #938 +/- ##
==========================================
- Coverage 92.38% 92.33% -0.06%
==========================================
Files 99 101 +2
Lines 11785 12748 +963
Branches 429 453 +24
==========================================
+ Hits 10888 11771 +883
- Misses 897 977 +80
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
2bca12d to
c4eb36f
Compare
|
Hey @ivonastojanovic do you have a timeline of when this might land? I started looking at #766 and I can just rebase onto this but its always easier if this is merged into main. Obviously no rush. |
| if (AllocatorType(<int>allocation.allocator) | ||
| not in (AllocatorType.FREE, AllocatorType.MUNMAP, | ||
| AllocatorType.PYMALLOC_FREE, | ||
| ObjectTrackingEvent.OBJECT_DESTROYED) | ||
| and allocation.frame_index != 0): |
There was a problem hiding this comment.
Do we actually need to check any of this? I think that allocation.frame_index != 0 implies AllocatorType(<int>allocation.allocator) not in (AllocatorType.FREE, AllocatorType.MUNMAP, AllocatorType.PYMALLOC_FREE, ObjectTrackingEvent.OBJECT_DESTROYED) - if we have a non-zero frame_index then we have a stack to look at, and if we have a stack to look at it must not be a deallocation, because we don't record stacks for deallocations.
And indeed, if I change this to if True: the tests all still pass - so I'm pretty sure we don't need this if at all, we just need to make sure to correctly handle the degenerate case where Py_GetStackFrame returns [] - and it seems like we do.
There was a problem hiding this comment.
Oh you're totally right, we don't need this check then
There was a problem hiding this comment.
@godlygeek Updated the tests, we can’t remove the if allocation.frame_index != 0 check because deallocations are now being counted under __main__.
There was a problem hiding this comment.
Ah, actually, we do want to explicitly check for deallocations rather than empty stacks, so that allocations with no Python stack can be attributed to the tracking root as well instead of being skipped (due to having allocation.frame_index == 0)
|
|
||
| @pytest.fixture(scope="module") | ||
| def empty_module_stats(): | ||
| """Stats where all allocations come from stdlib, so no modules are attributed.""" |
There was a problem hiding this comment.
Hm. I think that this shows that our fallback is wrong. I think if all of the allocations come from the stdlib, we should report the module as <root> or __main__ or <string> or something like that... something called the stdlib, after all. We might have trouble figuring out what to call it, but I don't think we should ignore it entirely...
There was a problem hiding this comment.
What do you think about __main__ then?
There was a problem hiding this comment.
__main__ is misleading since it may not be from the main module. I'm gonna flip this to do <root> instead, which matches what we use for the root of our flame graphs.
|
This looks pretty good. I have a lot of comments, but they're almost all pretty minor stuff. |
8226f08 to
e577912
Compare
Display the top N modules responsible for the most allocations in the memray stats reporter. For each allocation, walk the call stack to locate the nearest non-stdlib Python frame and extract its top-level module name. Aggregate allocation counts and allocated bytes by module, and include the top N results in both terminal and JSON output. Signed-off-by: Ivona Stojanovic <stojanovic.i@hotmail.com>
e577912 to
80d3615
Compare
|
I pushed some small fixes. At this point this PR looks semantically correct to me, but it's very slow when working with large numbers of allocations, even after I attempted to add some caching to it. I'm thinking we're going to need to push some of what's currently happening in the Python layer into the Cython or C++ layer to get this to be fast enough... Running: memray run --trace-python-allocators --no-compress -fo output.bin -m test test_setto create a capture file and then analyzing it with time memray stats -f --json output.bintakes 8 seconds on the main branch for me, and 25 seconds on this PR branch even after adding some caching, so |
|
Granted, that's a particularly stressful stress test - since the |
|
I have a few different ideas about how to make this fast enough. Roughly from easiest to hardest:
Or of course, we could just accept that it makes things take 3x as long, and put it behind a flag so people need to opt into the slower but more informative behavior. I lean towards trying both (1) and (2) though, and seeing how far those two changes get us. I'm betting that they should be enough... |
Issue number of the reported bug or feature request: #765
Describe your changes
Display the top N modules responsible for the most allocations in the memray stats reporter. For each allocation, walk the call stack to locate the nearest non-stdlib Python frame and extract its top-level module name. Aggregate allocation counts and allocated bytes by module, and include the top N results in both terminal and JSON output.
Testing performed
Unit tests + running
memray statson the example below:Output:
JSON Output:
Additional context
Add any other context about your contribution here.