summaryrefslogtreecommitdiffstats
path: root/Lib/test/support
diff options
context:
space:
mode:
authorPetr Viktorin <encukou@gmail.com>2024-02-28 11:53:48 (GMT)
committerGitHub <noreply@github.com>2024-02-28 11:53:48 (GMT)
commit7acf1fb5a70776429bd99e741d69471eb2d1c1bb (patch)
tree4b2f0bd7f2aaa03f84f3b77638de09eac892f150 /Lib/test/support
parent3b63d0769f49171f53e9cecc686fa01a383bd4b1 (diff)
downloadcpython-7acf1fb5a70776429bd99e741d69471eb2d1c1bb.zip
cpython-7acf1fb5a70776429bd99e741d69471eb2d1c1bb.tar.gz
cpython-7acf1fb5a70776429bd99e741d69471eb2d1c1bb.tar.bz2
gh-114911: Add CPUStopwatch test helper (GH-114912)
A few of our tests measure the time of CPU-bound operation, mainly to avoid quadratic or worse behaviour. Add a helper to ignore GC and time spent in other processes.
Diffstat (limited to 'Lib/test/support')
-rw-r--r--Lib/test/support/__init__.py40
1 files changed, 40 insertions, 0 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 1d03ec0..401b2ce 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2381,6 +2381,46 @@ def sleeping_retry(timeout, err_msg=None, /,
delay = min(delay * 2, max_delay)
+class CPUStopwatch:
+ """Context manager to roughly time a CPU-bound operation.
+
+ Disables GC. Uses CPU time if it can (i.e. excludes sleeps & time of
+ other processes).
+
+ N.B.:
+ - This *includes* time spent in other threads.
+ - Some systems only have a coarse resolution; check
+ stopwatch.clock_info.rseolution if.
+
+ Usage:
+
+ with ProcessStopwatch() as stopwatch:
+ ...
+ elapsed = stopwatch.seconds
+ resolution = stopwatch.clock_info.resolution
+ """
+ def __enter__(self):
+ get_time = time.process_time
+ clock_info = time.get_clock_info('process_time')
+ if get_time() <= 0: # some platforms like WASM lack process_time()
+ get_time = time.monotonic
+ clock_info = time.get_clock_info('monotonic')
+ self.context = disable_gc()
+ self.context.__enter__()
+ self.get_time = get_time
+ self.clock_info = clock_info
+ self.start_time = get_time()
+ return self
+
+ def __exit__(self, *exc):
+ try:
+ end_time = self.get_time()
+ finally:
+ result = self.context.__exit__(*exc)
+ self.seconds = end_time - self.start_time
+ return result
+
+
@contextlib.contextmanager
def adjust_int_max_str_digits(max_digits):
"""Temporarily change the integer string conversion length limit."""