@@ -73,6 +73,9 @@ typedef struct
7373 memalloc_heap_map_t * allocs_m ;
7474 /* Bytes allocated since the last sample was collected */
7575 uint64_t allocated_memory ;
76+ /* The factor that controls how often pointers are eligible to sample
77+ * TODO more description here */
78+ uintptr_t sample_mask ;
7679 /* True if we are exporting the current heap profile */
7780 bool frozen ;
7881 /* Contains the ongoing heap allocation/deallocation while frozen */
@@ -87,12 +90,20 @@ typedef struct
8790 /* Debug guard to assert that GIL-protected critical sections are maintained
8891 * while accessing the profiler's state */
8992 memalloc_gil_debug_check_t gil_guard ;
93+ // Whether we are currently in a run where we should try to collect a sample
94+ bool collect_sample ;
9095} heap_tracker_t ;
9196
9297static heap_tracker_t global_heap_tracker ;
9398
99+ static bool
100+ is_eligible_to_sample_no_cpython (void * ptr )
101+ {
102+ return (uintptr_t )ptr % global_heap_tracker .sample_mask == 0 ;
103+ }
104+
94105static uint32_t
95- heap_tracker_next_sample_size (uint32_t sample_size )
106+ heap_tracker_next_sample_size_no_cpython (uint32_t sample_size )
96107{
97108 /* We want to draw a sampling target from an exponential distribution with
98109 average sample_size. We use the standard technique of inverse transform
@@ -114,10 +125,12 @@ heap_tracker_init(heap_tracker_t* heap_tracker)
114125 heap_tracker -> freezer .allocs_m = memalloc_heap_map_new ();
115126 ptr_array_init (& heap_tracker -> freezer .frees );
116127 traceback_array_init (& heap_tracker -> unreported_samples );
128+ heap_tracker -> sample_mask = 1 ;
117129 heap_tracker -> allocated_memory = 0 ;
118130 heap_tracker -> frozen = false;
119131 heap_tracker -> sample_size = 0 ;
120132 heap_tracker -> current_sample_size = 0 ;
133+ heap_tracker -> collect_sample = false;
121134 memalloc_gil_debug_check_init (& heap_tracker -> gil_guard );
122135}
123136
@@ -189,8 +202,11 @@ void
189202memalloc_heap_tracker_init (uint32_t sample_size )
190203{
191204 heap_tracker_init (& global_heap_tracker );
205+ // TODO take as a param
206+ // Should be prime
207+ global_heap_tracker .sample_mask = 5 ;
192208 global_heap_tracker .sample_size = sample_size ;
193- global_heap_tracker .current_sample_size = heap_tracker_next_sample_size (sample_size );
209+ global_heap_tracker .current_sample_size = heap_tracker_next_sample_size_no_cpython (sample_size );
194210}
195211
196212void
@@ -218,6 +234,13 @@ memalloc_heap_untrack_no_cpython(heap_tracker_t* heap_tracker, void* ptr)
218234 MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
219235 return NULL ;
220236 }
237+
238+ // If this is not a pointer that could be sampled, then no need to do anything with it.
239+ if (!is_eligible_to_sample_no_cpython (ptr )) {
240+ MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
241+ return NULL ;
242+ }
243+
221244 if (!heap_tracker -> frozen ) {
222245 traceback_t * tb = memalloc_heap_map_remove (heap_tracker -> allocs_m , ptr );
223246 if (tb && !tb -> reported ) {
@@ -259,7 +282,7 @@ memalloc_heap_untrack(void* ptr)
259282 * shared state, and must be called with the GIL held and without making any C
260283 * Python API calls. */
261284static bool
262- memalloc_heap_should_sample_no_cpython (heap_tracker_t * heap_tracker , size_t size )
285+ memalloc_heap_should_sample_no_cpython (heap_tracker_t * heap_tracker , void * ptr , size_t size )
263286{
264287 MEMALLOC_GIL_DEBUG_CHECK_ACQUIRE (& heap_tracker -> gil_guard );
265288 /* Heap tracking is disabled */
@@ -270,12 +293,6 @@ memalloc_heap_should_sample_no_cpython(heap_tracker_t* heap_tracker, size_t size
270293
271294 heap_tracker -> allocated_memory += size ;
272295
273- /* Check if we have enough sample or not */
274- if (heap_tracker -> allocated_memory < heap_tracker -> current_sample_size ) {
275- MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
276- return false;
277- }
278-
279296 if (memalloc_heap_map_size (heap_tracker -> allocs_m ) + memalloc_heap_map_size (heap_tracker -> freezer .allocs_m ) >
280297 TRACEBACK_ARRAY_MAX_COUNT ) {
281298 /* TODO(nick) this is vestigial from the original array-based
@@ -287,10 +304,39 @@ memalloc_heap_should_sample_no_cpython(heap_tracker_t* heap_tracker, size_t size
287304 return false;
288305 }
289306
307+ /* Check if we have enough sample or not.
308+ * If so, start a run of possible sample collection */
309+ if (heap_tracker -> allocated_memory >= heap_tracker -> current_sample_size ) {
310+ heap_tracker -> collect_sample = true;
311+ }
312+
313+ /* If we are in a situation where we could collect samples, and the pointer is elibible,
314+ * collect a sample */
315+ if (heap_tracker -> collect_sample && is_eligible_to_sample_no_cpython (ptr )) {
316+ heap_tracker -> collect_sample = false;
317+ MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
318+ return true;
319+ }
320+
321+ /* Otherwise, leave collect_sample untouched, and try next time */
290322 MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
291323 return true;
292324}
293325
326+ /* Updates the threshold for */
327+ static void
328+ memalloc_heap_update_threshold_no_cpython (heap_tracker_t * heap_tracker )
329+ {
330+ MEMALLOC_GIL_DEBUG_CHECK_ACQUIRE (& heap_tracker -> gil_guard );
331+ /* Reset the counter to 0 */
332+ heap_tracker -> allocated_memory = 0 ;
333+
334+ /* Compute the new target sample size */
335+ heap_tracker -> current_sample_size = heap_tracker_next_sample_size_no_cpython (heap_tracker -> sample_size );
336+
337+ MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
338+ }
339+
294340/* Track an allocation that we decided to sample. This updates shared state and
295341 * must be called with the GIL held and without making any C Python API calls.
296342 * If the allocation could not be added because the profiler was stopped,
@@ -312,11 +358,7 @@ memalloc_heap_add_sample_no_cpython(heap_tracker_t* heap_tracker, traceback_t* t
312358 old = memalloc_heap_map_insert (heap_tracker -> allocs_m , tb -> ptr , tb );
313359 }
314360
315- /* Reset the counter to 0 */
316- heap_tracker -> allocated_memory = 0 ;
317-
318- /* Compute the new target sample size */
319- heap_tracker -> current_sample_size = heap_tracker_next_sample_size (heap_tracker -> sample_size );
361+ memalloc_heap_update_threshold_no_cpython (heap_tracker );
320362
321363 MEMALLOC_GIL_DEBUG_CHECK_RELEASE (& heap_tracker -> gil_guard );
322364 return old ;
@@ -326,7 +368,7 @@ memalloc_heap_add_sample_no_cpython(heap_tracker_t* heap_tracker, traceback_t* t
326368void
327369memalloc_heap_track (uint16_t max_nframe , void * ptr , size_t size , PyMemAllocatorDomain domain )
328370{
329- if (!memalloc_heap_should_sample_no_cpython (& global_heap_tracker , size )) {
371+ if (!memalloc_heap_should_sample_no_cpython (& global_heap_tracker , ptr , size )) {
330372 return ;
331373 }
332374
0 commit comments