Skip to content

Commit 3c7609a

Browse files
authored
[3.9] bpo-40630: Add tracemalloc.reset_peak (GH-20102) (GH-20545)
* bpo-40630: Add tracemalloc.reset_peak (GH-20102, cherrypick 8b62644) The reset_peak function sets the peak memory size to the current size, representing a resetting of that metric. This allows for recording the peak of specific sections of code, ignoring other code that may have had a higher peak (since the most recent `tracemalloc.start()` or tracemalloc.clear_traces()` call). * Adjust docs to point to 3.9
1 parent 410b730 commit 3c7609a

File tree

7 files changed

+138
-1
lines changed

7 files changed

+138
-1
lines changed

Doc/library/tracemalloc.rst

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,47 @@ Example of output of the Python test suite::
249249

250250
See :meth:`Snapshot.statistics` for more options.
251251

252+
Record the current and peak size of all traced memory blocks
253+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
254+
255+
The following code computes two sums like ``0 + 1 + 2 + ...`` inefficiently, by
256+
creating a list of those numbers. This list consumes a lot of memory
257+
temporarily. We can use :func:`get_traced_memory` and :func:`reset_peak` to
258+
observe the small memory usage after the sum is computed as well as the peak
259+
memory usage during the computations::
260+
261+
import tracemalloc
262+
263+
tracemalloc.start()
264+
265+
# Example code: compute a sum with a large temporary list
266+
large_sum = sum(list(range(100000)))
267+
268+
first_size, first_peak = tracemalloc.get_traced_memory()
269+
270+
tracemalloc.reset_peak()
271+
272+
# Example code: compute a sum with a small temporary list
273+
small_sum = sum(list(range(1000)))
274+
275+
second_size, second_peak = tracemalloc.get_traced_memory()
276+
277+
print(f"{first_size=}, {first_peak=}")
278+
print(f"{second_size=}, {second_peak=}")
279+
280+
Output::
281+
282+
first_size=664, first_peak=3592984
283+
second_size=804, second_peak=29704
284+
285+
Using :func:`reset_peak` ensured we could accurately record the peak during the
286+
computation of ``small_sum``, even though it is much smaller than the overall
287+
peak size of memory blocks since the :func:`start` call. Without the call to
288+
:func:`reset_peak`, ``second_peak`` would still be the peak from the
289+
computation ``large_sum`` (that is, equal to ``first_peak``). In this case,
290+
both peaks are much higher than the final memory usage, and which suggests we
291+
could optimise (by removing the unnecessary call to :class:`list`, and writing
292+
``sum(range(...))``).
252293

253294
API
254295
---
@@ -289,6 +330,24 @@ Functions
289330
:mod:`tracemalloc` module as a tuple: ``(current: int, peak: int)``.
290331

291332

333+
.. function:: reset_peak()
334+
335+
Set the peak size of memory blocks traced by the :mod:`tracemalloc` module
336+
to the current size.
337+
338+
Do nothing if the :mod:`tracemalloc` module is not tracing memory
339+
allocations.
340+
341+
This function only modifies the recorded peak size, and does not modify or
342+
clear any traces, unlike :func:`clear_traces`. Snapshots taken with
343+
:func:`take_snapshot` before a call to :func:`reset_peak` can be
344+
meaningfully compared to snapshots taken after the call.
345+
346+
See also :func:`get_traced_memory`.
347+
348+
.. versionadded:: 3.9
349+
350+
292351
.. function:: get_tracemalloc_memory()
293352

294353
Get the memory usage in bytes of the :mod:`tracemalloc` module used to store

Doc/whatsnew/3.9.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,12 @@ Previously, :attr:`sys.stderr` was block-buffered when non-interactive. Now
562562
``stderr`` defaults to always being line-buffered.
563563
(Contributed by Jendrik Seipp in :issue:`13601`.)
564564

565+
tracemalloc
566+
-----------
567+
568+
Added :func:`tracemalloc.reset_peak` to set the peak size of traced memory
569+
blocks to the current size, to measure the peak of specific pieces of code.
570+
(Contributed by Huon Wilson in :issue:`40630`.)
565571

566572
typing
567573
------

Lib/test/test_tracemalloc.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,30 @@ def test_clear_traces(self):
246246
traceback2 = tracemalloc.get_object_traceback(obj)
247247
self.assertIsNone(traceback2)
248248

249+
def test_reset_peak(self):
250+
# Python allocates some internals objects, so the test must tolerate
251+
# a small difference between the expected size and the real usage
252+
tracemalloc.clear_traces()
253+
254+
# Example: allocate a large piece of memory, temporarily
255+
large_sum = sum(list(range(100000)))
256+
size1, peak1 = tracemalloc.get_traced_memory()
257+
258+
# reset_peak() resets peak to traced memory: peak2 < peak1
259+
tracemalloc.reset_peak()
260+
size2, peak2 = tracemalloc.get_traced_memory()
261+
self.assertGreaterEqual(peak2, size2)
262+
self.assertLess(peak2, peak1)
263+
264+
# check that peak continue to be updated if new memory is allocated:
265+
# peak3 > peak2
266+
obj_size = 1024 * 1024
267+
obj, obj_traceback = allocate_bytes(obj_size)
268+
size3, peak3 = tracemalloc.get_traced_memory()
269+
self.assertGreaterEqual(peak3, size3)
270+
self.assertGreater(peak3, peak2)
271+
self.assertGreaterEqual(peak3 - peak2, obj_size)
272+
249273
def test_is_tracing(self):
250274
tracemalloc.stop()
251275
self.assertFalse(tracemalloc.is_tracing())

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,7 @@ Alex Willmer
18631863
David Wilson
18641864
Geoff Wilson
18651865
Greg V. Wilson
1866+
Huon Wilson
18661867
J Derek Wilson
18671868
Paul Winkler
18681869
Jody Winston
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added :func:`tracemalloc.reset_peak` to set the peak size of traced memory
2+
blocks to the current size, to measure the peak of specific pieces of code.

Modules/_tracemalloc.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,30 @@ _tracemalloc_get_traced_memory_impl(PyObject *module)
16431643
return Py_BuildValue("nn", size, peak_size);
16441644
}
16451645

1646+
/*[clinic input]
1647+
_tracemalloc.reset_peak
1648+
1649+
Set the peak size of memory blocks traced by tracemalloc to the current size.
1650+
1651+
Do nothing if the tracemalloc module is not tracing memory allocations.
1652+
1653+
[clinic start generated code]*/
1654+
1655+
static PyObject *
1656+
_tracemalloc_reset_peak_impl(PyObject *module)
1657+
/*[clinic end generated code: output=140c2870f691dbb2 input=18afd0635066e9ce]*/
1658+
{
1659+
if (!_Py_tracemalloc_config.tracing) {
1660+
Py_RETURN_NONE;
1661+
}
1662+
1663+
TABLES_LOCK();
1664+
tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
1665+
TABLES_UNLOCK();
1666+
1667+
Py_RETURN_NONE;
1668+
}
1669+
16461670

16471671
static PyMethodDef module_methods[] = {
16481672
_TRACEMALLOC_IS_TRACING_METHODDEF
@@ -1654,6 +1678,7 @@ static PyMethodDef module_methods[] = {
16541678
_TRACEMALLOC_GET_TRACEBACK_LIMIT_METHODDEF
16551679
_TRACEMALLOC_GET_TRACEMALLOC_MEMORY_METHODDEF
16561680
_TRACEMALLOC_GET_TRACED_MEMORY_METHODDEF
1681+
_TRACEMALLOC_RESET_PEAK_METHODDEF
16571682
/* sentinel */
16581683
{NULL, NULL}
16591684
};

Modules/clinic/_tracemalloc.c.h

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)