Skip to content

Commit

Permalink
Collect specialization statistics from running benchmarks (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdboom authored Nov 3, 2022
1 parent 0ef21a1 commit 93cac74
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 1 deletion.
12 changes: 12 additions & 0 deletions doc/run_benchmark.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,16 @@ mentioned above. Running an arbitrary number of warmup values may also make
the benchmark less reliable since two runs may use a different number of warmup
values.

Specializer statistics (``pystats``)
==================================

``pyperf`` has built-in support for `specializer statistics (``pystats``) <https://docs.python.org/dev/using/configure.html#cmdoption-enable-pystats>`_.
If running benchmarks on a CPython built with the ``--enable-pystats`` flag, pyperf will automatically collect ``pystats`` on the benchmark code by calling ``sys._stats_on`` immediately before the benchmark and calling ``sys._stats_off`` immediately after.
Stats are not collected when running ``pyperf``'s own code or when warming up or calibrating the benchmarks.

Due to the overhead of collecting the statistics, the timing results will be meaningless.

The `Tools/scripts/summarize_stats.py <https://github.com/python/cpython/blob/main/Tools/scripts/summarize_stats.py>`_ script can be used to summarize the statistics in a human-readable form.

Statistics are not cleared between runs.
If you need to delete statistics from a previous run, remove the files in ``/tmp/py_stats`` (Unix) or ``C:\temp\py_stats`` (Windows).
4 changes: 4 additions & 0 deletions pyperf/_collect_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ def collect_python_metadata(metadata):
if not gc.isenabled():
metadata['python_gc'] = 'disabled'

# pystats enabled?
if hasattr(sys, "_stats_clear"):
metadata['pystats'] = 'enabled'


def read_proc(path):
path = proc_path(path)
Expand Down
7 changes: 7 additions & 0 deletions pyperf/_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ def __init__(self, values=None, warmups=None, processes=None,
program_args=None, add_cmdline_args=None,
_argparser=None):

# Reset the stats collection if running a --enable-pystats build
try:
sys._stats_off()
sys._stats_clear()
except AttributeError:
pass

# Watchdog: ensure that only once instance of Runner (or a Runner
# subclass) is created per process to prevent bad suprises
cls = self.__class__
Expand Down
18 changes: 17 additions & 1 deletion pyperf/_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ def _compute_values(self, values, nvalue,
else:
value_name = 'Value'

task_func = self.task_func

# If we are on a pystats build, turn on stats collection around the
# actual work, but only if we aren't warming up or calibrating.
if hasattr(sys, "_stats_on") and not is_warmup and not calibrate_loops:
core_task_func = task_func

def stats_func(*args):
sys._stats_on()
try:
return core_task_func(*args)
finally:
sys._stats_off()

task_func = stats_func

index = 1
inner_loops = self.inner_loops
if not inner_loops:
Expand All @@ -64,7 +80,7 @@ def _compute_values(self, values, nvalue,
if index > nvalue:
break

raw_value = self.task_func(self, self.loops)
raw_value = task_func(self, self.loops)
raw_value = float(raw_value)
value = raw_value / (self.loops * inner_loops)

Expand Down

0 comments on commit 93cac74

Please sign in to comment.