Mercurial > hg
changeset 42360:3293086ff663
perf: add an option to profile the benchmark section
Running a perf command with --profile gather data for the whole command
execution, including setup and cleanup. This can significantly alter the data.
To work around this we introduce a new option, it trigger the profiling of only one
iteration of the benchmarked section.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Tue, 21 May 2019 15:26:48 +0200 |
parents | 563cd9a72682 |
children | c4b8f8637d7a |
files | contrib/perf.py tests/test-contrib-perf.t |
diffstat | 2 files changed, 43 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/perf.py Tue May 21 15:08:06 2019 +0200 +++ b/contrib/perf.py Tue May 21 15:26:48 2019 +0200 @@ -18,6 +18,10 @@ ``pre-run`` number of run to perform before starting measurement. +``profile-benchmark`` + Enable profiling for the benchmarked section. + (The first iteration is benchmarked) + ``run-limits`` Control the number of runs each benchmark will perform. The option value should be a list of `<time>-<numberofrun>` pairs. After each run the @@ -109,6 +113,10 @@ except ImportError: pass +try: + from mercurial import profiling +except ImportError: + profiling = None def identity(a): return a @@ -246,6 +254,9 @@ configitem(b'perf', b'pre-run', default=mercurial.configitems.dynamicdefault, ) + configitem(b'perf', b'profile-benchmark', + default=mercurial.configitems.dynamicdefault, + ) configitem(b'perf', b'run-limits', default=mercurial.configitems.dynamicdefault, ) @@ -257,6 +268,13 @@ return lambda x: 1 return len +class noop(object): + """dummy context manager""" + def __enter__(self): + pass + def __exit__(self, *args): + pass + def gettimer(ui, opts=None): """return a timer function and formatter: (timer, formatter) @@ -347,9 +365,14 @@ if not limits: limits = DEFAULTLIMITS + profiler = None + if profiling is not None: + if ui.configbool(b"perf", b"profile-benchmark", False): + profiler = profiling.profile(ui) + prerun = getint(ui, b"perf", b"pre-run", 0) t = functools.partial(_timer, fm, displayall=displayall, limits=limits, - prerun=prerun) + prerun=prerun, profiler=profiler) return t, fm def stub_timer(fm, func, setup=None, title=None): @@ -376,11 +399,13 @@ ) def _timer(fm, func, setup=None, title=None, displayall=False, - limits=DEFAULTLIMITS, prerun=0): + limits=DEFAULTLIMITS, prerun=0, profiler=None): gc.collect() results = [] begin = util.timer() count = 0 + if profiler is None: + profiler = noop() for i in xrange(prerun): if setup is not None: setup() @@ -389,8 +414,9 @@ while keepgoing: if setup is not None: setup() - with timeone() as item: - r = func() + with profiler: + with timeone() as item: + r = func() count += 1 results.append(item[0]) cstop = util.timer()
--- a/tests/test-contrib-perf.t Tue May 21 15:08:06 2019 +0200 +++ b/tests/test-contrib-perf.t Tue May 21 15:26:48 2019 +0200 @@ -58,6 +58,10 @@ "pre-run" number of run to perform before starting measurement. + "profile-benchmark" + Enable profiling for the benchmarked section. (The first iteration is + benchmarked) + "run-limits" Control the number of runs each benchmark will perform. The option value should be a list of '<time>-<numberofrun>' pairs. After each run the @@ -349,6 +353,15 @@ searching for changes searching for changes +test profile-benchmark option +------------------------------ + +Function to check that statprof ran + $ statprofran () { + > egrep 'Sample count:|No samples recorded' > /dev/null + > } + $ hg perfdiscovery . --config perf.stub=no --config perf.run-limits='0.000000001-1' --config perf.profile-benchmark=yes 2>&1 | statprofran + Check perf.py for historical portability ----------------------------------------