##// END OF EJS Templates
perf: add command to benchmark bundle reading...
perf: add command to benchmark bundle reading Upcoming commits will be refactoring bundle2 I/O code. This commit establishes a `hg perfbundleread` command that measures how long it takes to read a bundle using various mechanisms. As a baseline, here's output from an uncompressed bundle1 bundle of my Firefox repo (7,098,622,890 bytes): ! read(8k) ! wall 0.763481 comb 0.760000 user 0.160000 sys 0.600000 (best of 6) ! read(16k) ! wall 0.644512 comb 0.640000 user 0.110000 sys 0.530000 (best of 16) ! read(32k) ! wall 0.581172 comb 0.590000 user 0.060000 sys 0.530000 (best of 18) ! read(128k) ! wall 0.535183 comb 0.530000 user 0.010000 sys 0.520000 (best of 19) ! cg1 deltaiter() ! wall 0.873500 comb 0.880000 user 0.840000 sys 0.040000 (best of 12) ! cg1 getchunks() ! wall 6.283797 comb 6.270000 user 5.570000 sys 0.700000 (best of 3) ! cg1 read(8k) ! wall 1.097173 comb 1.100000 user 0.400000 sys 0.700000 (best of 10) ! cg1 read(16k) ! wall 0.810750 comb 0.800000 user 0.200000 sys 0.600000 (best of 13) ! cg1 read(32k) ! wall 0.671215 comb 0.670000 user 0.110000 sys 0.560000 (best of 15) ! cg1 read(128k) ! wall 0.597857 comb 0.600000 user 0.020000 sys 0.580000 (best of 15) And from an uncompressed bundle2 bundle (6,070,036,163 bytes): ! read(8k) ! wall 0.676997 comb 0.680000 user 0.160000 sys 0.520000 (best of 15) ! read(16k) ! wall 0.592706 comb 0.590000 user 0.080000 sys 0.510000 (best of 17) ! read(32k) ! wall 0.529395 comb 0.530000 user 0.050000 sys 0.480000 (best of 16) ! read(128k) ! wall 0.491270 comb 0.490000 user 0.010000 sys 0.480000 (best of 19) ! bundle2 forwardchunks() ! wall 2.997131 comb 2.990000 user 2.270000 sys 0.720000 (best of 4) ! bundle2 iterparts() ! wall 12.247197 comb 10.670000 user 8.170000 sys 2.500000 (best of 3) ! bundle2 part seek() ! wall 11.761675 comb 10.500000 user 8.240000 sys 2.260000 (best of 3) ! bundle2 part read(8k) ! wall 9.116163 comb 9.110000 user 8.240000 sys 0.870000 (best of 3) ! bundle2 part read(16k) ! wall 8.984362 comb 8.970000 user 8.110000 sys 0.860000 (best of 3) ! bundle2 part read(32k) ! wall 8.758364 comb 8.740000 user 7.860000 sys 0.880000 (best of 3) ! bundle2 part read(128k) ! wall 8.749040 comb 8.730000 user 7.830000 sys 0.900000 (best of 3) We already see some interesting data. Notably that bundle2 has significant overhead compared to bundle1. This matters for e.g. stream clone bundles, which can be applied at >1Gbps. Differential Revision: https://phab.mercurial-scm.org/D1385

File last commit:

r29210:984c4d23 default
r35108:e9661304 default
Show More
revsetbenchmarks.py
325 lines | 10.2 KiB | text/x-python | PythonLexer
/ contrib / revsetbenchmarks.py
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848 #!/usr/bin/env python
# Measure the performance of a list of revsets against multiple revisions
# defined by parameter. Checkout one by one and run perfrevset with every
# revset in the list to benchmark its performance.
#
Pierre-Yves David
revsetbenchmarks: fix argument parsing...
r25535 # You should run this from the root of your mercurial repository.
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848 #
Pierre-Yves David
revsetbenchmarks: fix argument parsing...
r25535 # call with --help for details
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 from __future__ import absolute_import, print_function
import math
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 import optparse # cannot use argparse, python 2.7 only
Pierre-Yves David
revsetbenchmark: automatically finds the perf extension...
r21548 import os
Pierre-Yves David
revsetbenchmarks: parse perfrevset output into actual number...
r25530 import re
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 import subprocess
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 import sys
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287
Pierre-Yves David
revsetbenchmarks: use combination variants in default set...
r25544 DEFAULTVARIANTS = ['plain', 'min', 'max', 'first', 'last',
'reverse', 'reverse+first', 'reverse+last',
'sort', 'sort+first', 'sort+last']
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540
Durham Goode
revsetbenchmark: remove python 2.7 dependency...
r20893 def check_output(*args, **kwargs):
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 kwargs.setdefault('stderr', subprocess.PIPE)
kwargs.setdefault('stdout', subprocess.PIPE)
proc = subprocess.Popen(*args, **kwargs)
Durham Goode
revsetbenchmark: remove python 2.7 dependency...
r20893 output, error = proc.communicate()
if proc.returncode != 0:
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 raise subprocess.CalledProcessError(proc.returncode, ' '.join(args[0]))
Durham Goode
revsetbenchmark: remove python 2.7 dependency...
r20893 return output
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pierre-Yves David
revsetbenchmark: convert update to proper subprocess call
r20850 def update(rev):
"""update the repo to a revision"""
try:
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 subprocess.check_call(['hg', 'update', '--quiet', '--check', str(rev)])
Yuya Nishihara
revsetbenchmarks: run make after update so that C extensions are built
r26034 check_output(['make', 'local'],
stderr=None) # suppress output except for error/warning
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 except subprocess.CalledProcessError as exc:
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print('update to revision %s failed, aborting'%rev, file=sys.stderr)
Pierre-Yves David
revsetbenchmark: convert update to proper subprocess call
r20850 sys.exit(exc.returncode)
Pierre-Yves David
revsetbenchmarks: extract call to mercurial into a function...
r25528
def hg(cmd, repo=None):
"""run a mercurial command
<cmd> is the list of command + argument,
<repo> is an optional repository path to run this command in."""
fullcmd = ['./hg']
if repo is not None:
fullcmd += ['-R', repo]
fullcmd += ['--config',
'extensions.perf=' + os.path.join(contribdir, 'perf.py')]
fullcmd += cmd
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 return check_output(fullcmd, stderr=subprocess.STDOUT)
Pierre-Yves David
revsetbenchmarks: extract call to mercurial into a function...
r25528
Gregory Szorc
revsetbenchmarks: support benchmarking changectx loading...
r27073 def perf(revset, target=None, contexts=False):
Pierre-Yves David
revsetbenchmark: convert performance call to proper subprocess call
r20851 """run benchmark for this very revset"""
try:
Gregory Szorc
revsetbenchmarks: support benchmarking changectx loading...
r27073 args = ['perfrevset', revset]
if contexts:
args.append('--contexts')
output = hg(args, repo=target)
Pierre-Yves David
revsetbenchmarks: parse perfrevset output into actual number...
r25530 return parseoutput(output)
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 except subprocess.CalledProcessError as exc:
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print('abort: cannot run revset benchmark: %s'%exc.cmd, file=sys.stderr)
Durham Goode
revsetbenchmark: handle exception case...
r28073 if getattr(exc, 'output', None) is None: # no output before 2.7
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print('(no output)', file=sys.stderr)
Pierre-Yves David
revsetbenchmarks: improve error output in case of failure...
r25529 else:
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print(exc.output, file=sys.stderr)
Pierre-Yves David
revsetbenchmark: do not abort on failure to run a revset...
r25646 return None
Pierre-Yves David
revsetbenchmark: convert performance call to proper subprocess call
r20851
Pierre-Yves David
revsetbenchmarks: parse perfrevset output into actual number...
r25530 outputre = re.compile(r'! wall (\d+.\d+) comb (\d+.\d+) user (\d+.\d+) '
'sys (\d+.\d+) \(best of (\d+)\)')
def parseoutput(output):
"""parse a textual output into a dict
We cannot just use json because we want to compare with old
versions of Mercurial that may not support json output.
"""
match = outputre.search(output)
if not match:
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print('abort: invalid output:', file=sys.stderr)
print(output, file=sys.stderr)
Pierre-Yves David
revsetbenchmarks: parse perfrevset output into actual number...
r25530 sys.exit(1)
return {'comb': float(match.group(2)),
'count': int(match.group(5)),
'sys': float(match.group(3)),
'user': float(match.group(4)),
'wall': float(match.group(1)),
}
Pierre-Yves David
revsetbenchmark: convert revision display to proper subprocesscall
r20852 def printrevision(rev):
"""print data about a revision"""
Pierre-Yves David
revsetbenchmarks: improve revision printing...
r25538 sys.stdout.write("Revision ")
Pierre-Yves David
revsetbenchmark: convert revision display to proper subprocesscall
r20852 sys.stdout.flush()
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 subprocess.check_call(['hg', 'log', '--rev', str(rev), '--template',
'{if(tags, " ({tags})")} '
'{rev}:{node|short}: {desc|firstline}\n'])
Pierre-Yves David
revsetbenchmark: convert revision display to proper subprocesscall
r20852
Pierre-Yves David
revsetbenchmarks: ensure all indexes have the same width...
r25532 def idxwidth(nbidx):
"""return the max width of number used for index
Augie Fackler
revsetbenchmarks: clarify comment based on irc discussion
r25533 This is similar to log10(nbidx), but we use custom code here
because we start with zero and we'd rather not deal with all the
extra rounding business that log10 would imply.
"""
Pierre-Yves David
revsetbenchmarks: ensure all indexes have the same width...
r25532 nbidx -= 1 # starts at 0
idxwidth = 0
while nbidx:
idxwidth += 1
nbidx //= 10
if not idxwidth:
idxwidth = 1
return idxwidth
Pierre-Yves David
revsetbenchmarks: display relative change when meaningful...
r25539 def getfactor(main, other, field, sensitivity=0.05):
"""return the relative factor between values for 'field' in main and other
Mads Kiilerich
spelling: trivial spell checking
r26781 Return None if the factor is insignificant (less than <sensitivity>
Pierre-Yves David
revsetbenchmarks: display relative change when meaningful...
r25539 variation)."""
factor = 1
if main is not None:
factor = other[field] / main[field]
low, high = 1 - sensitivity, 1 + sensitivity
if (low < factor < high):
return None
return factor
def formatfactor(factor):
"""format a factor into a 4 char string
22%
156%
x2.4
x23
x789
x1e4
x5x7
"""
if factor is None:
return ' '
elif factor < 2:
return '%3i%%' % (factor * 100)
elif factor < 10:
return 'x%3.1f' % factor
elif factor < 1000:
return '%4s' % ('x%i' % factor)
else:
order = int(math.log(factor)) + 1
while 1 < math.log(factor):
factor //= 0
return 'x%ix%i' % (factor, order)
Pierre-Yves David
revsetbenchmarks: display even more compact timing result...
r25541 def formattiming(value):
"""format a value to strictly 8 char, dropping some precision if needed"""
if value < 10**7:
return ('%.6f' % value)[:8]
else:
# value is HUGE very unlikely to happen (4+ month run)
return '%i' % value
Pierre-Yves David
revsetbenchmarks: display relative change when meaningful...
r25539 _marker = object()
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 def printresult(variants, idx, data, maxidx, verbose=False, reference=_marker):
Pierre-Yves David
revsetbenchmarks: factor out result output into a function...
r25531 """print a line of result to stdout"""
Pierre-Yves David
revsetbenchmarks: ensure all indexes have the same width...
r25532 mask = '%%0%ii) %%s' % idxwidth(maxidx)
Pierre-Yves David
revsetbenchmark: do not abort on failure to run a revset...
r25646
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 out = []
for var in variants:
Pierre-Yves David
revsetbenchmark: do not abort on failure to run a revset...
r25646 if data[var] is None:
out.append('error ')
out.append(' ' * 4)
continue
Pierre-Yves David
revsetbenchmarks: display even more compact timing result...
r25541 out.append(formattiming(data[var]['wall']))
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 if reference is not _marker:
factor = None
if reference is not None:
factor = getfactor(reference[var], data[var], 'wall')
out.append(formatfactor(factor))
if verbose:
Pierre-Yves David
revsetbenchmarks: display even more compact timing result...
r25541 out.append(formattiming(data[var]['comb']))
out.append(formattiming(data[var]['user']))
out.append(formattiming(data[var]['sys']))
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 out.append('%6d' % data[var]['count'])
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print(mask % (idx, ' '.join(out)))
Pierre-Yves David
revsetbenchmarks: parse perfrevset output into actual number...
r25530
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 def printheader(variants, maxidx, verbose=False, relative=False):
header = [' ' * (idxwidth(maxidx) + 1)]
for var in variants:
if not var:
var = 'iter'
Pierre-Yves David
revsetbenchmarks: display even more compact timing result...
r25541 if 8 < len(var):
var = var[:3] + '..' + var[-3:]
header.append('%-8s' % var)
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 if relative:
header.append(' ')
if verbose:
Pierre-Yves David
revsetbenchmarks: display even more compact timing result...
r25541 header.append('%-8s' % 'comb')
header.append('%-8s' % 'user')
header.append('%-8s' % 'sys')
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 header.append('%6s' % 'count')
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print(' '.join(header))
Pierre-Yves David
revsetbenchmarks: parse perfrevset output into actual number...
r25530
Pierre-Yves David
revsetbenchmark: get revision to benchmark in a function...
r20853 def getrevs(spec):
"""get the list of rev matched by a revset"""
try:
out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec])
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 except subprocess.CalledProcessError as exc:
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("abort, can't get revision from %s"%spec, file=sys.stderr)
Pierre-Yves David
revsetbenchmark: get revision to benchmark in a function...
r20853 sys.exit(exc.returncode)
return [r for r in out.split() if r]
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 def applyvariants(revset, variant):
if variant == 'plain':
return revset
Pierre-Yves David
revsetbenchmarks: support combining variants with "+"...
r25543 for var in variant.split('+'):
revset = '%s(%s)' % (var, revset)
return revset
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540
Pierre-Yves David
revsetbenchmarks: add main documention for the script...
r25607 helptext="""This script will run multiple variants of provided revsets using
different revisions in your mercurial repository. After the benchmark are run
Mads Kiilerich
spelling: trivial spell checking
r26781 summary output is provided. Use it to demonstrate speed improvements or pin
Pierre-Yves David
revsetbenchmarks: add main documention for the script...
r25607 point regressions. Revsets to run are specified in a file (or from stdin), one
revsets per line. Line starting with '#' will be ignored, allowing insertion of
comments."""
Yuya Nishihara
py3: make contrib/revsetbenchmarks.py not import symbols from stdlib modules
r29210 parser = optparse.OptionParser(usage="usage: %prog [options] <revs>",
description=helptext)
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287 parser.add_option("-f", "--file",
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23139 help="read revset from FILE (stdin if omitted)",
Pierre-Yves David
revsetbenchmark: make it clear that revsets may be read from stdin
r22555 metavar="FILE")
Pierre-Yves David
revsetbenchmark: support for running on other repo...
r21549 parser.add_option("-R", "--repo",
help="run benchmark on REPO", metavar="REPO")
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287
Pierre-Yves David
revsetbenchmarks: hide most timing under a --verbose flag...
r25537 parser.add_option("-v", "--verbose",
action='store_true',
help="display all timing data (not just best total time)")
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 parser.add_option("", "--variants",
default=','.join(DEFAULTVARIANTS),
help="comma separated list of variant to test "
"(eg: plain,min,sorted) (plain = no modification)")
Gregory Szorc
revsetbenchmarks: support benchmarking changectx loading...
r27073 parser.add_option('', '--contexts',
action='store_true',
help='obtain changectx from results instead of integer revs')
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287 (options, args) = parser.parse_args()
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pierre-Yves David
revsetbenchmarks: fix argument parsing...
r25535 if not args:
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287 parser.print_help()
Pierre-Yves David
revsetbenchmark: add a usage message when no arguments are passed...
r21286 sys.exit(255)
Pierre-Yves David
revsetbenchmark: automatically finds the perf extension...
r21548 # the directory where both this script and the perf.py extension live.
contribdir = os.path.dirname(__file__)
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848 revsetsfile = sys.stdin
Pierre-Yves David
revsetbenchmark: use optparse to retrieve argument...
r21287 if options.file:
revsetsfile = open(options.file)
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pierre-Yves David
revsetbenchmark: allow comments ('#' prefix) in the revset input
r22556 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')]
Pierre-Yves David
revsetbenchmarks: ignore empty lines...
r25642 revsets = [l for l in revsets if l]
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("Revsets to benchmark")
print("----------------------------")
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
for idx, rset in enumerate(revsets):
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("%i) %s" % (idx, rset))
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("----------------------------")
print()
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pierre-Yves David
revsetbenchmarks: fix argument parsing...
r25535 revs = []
for a in args:
revs.extend(getrevs(a))
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 variants = options.variants.split(',')
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855 results = []
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848 for r in revs:
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("----------------------------")
Pierre-Yves David
revsetbenchmark: convert revision display to proper subprocesscall
r20852 printrevision(r)
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("----------------------------")
Pierre-Yves David
revsetbenchmark: convert update to proper subprocess call
r20850 update(r)
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855 res = []
results.append(res)
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 printheader(variants, len(revsets), verbose=options.verbose)
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848 for idx, rset in enumerate(revsets):
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 varres = {}
for var in variants:
varrset = applyvariants(rset, var)
Gregory Szorc
revsetbenchmarks: support benchmarking changectx loading...
r27073 data = perf(varrset, target=options.repo, contexts=options.contexts)
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 varres[var] = data
res.append(varres)
printresult(variants, idx, varres, len(revsets),
verbose=options.verbose)
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855 sys.stdout.flush()
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("----------------------------")
Pierre-Yves David
revsetbenchmark: simplify and convert the script to python...
r20848
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("""
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855
Result by revset
================
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 """)
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print('Revision:')
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855 for idx, rev in enumerate(revs):
sys.stdout.write('%i) ' % idx)
sys.stdout.flush()
printrevision(rev)
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print()
print()
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855
for ridx, rset in enumerate(revsets):
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print("revset #%i: %s" % (ridx, rset))
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 printheader(variants, len(results), verbose=options.verbose, relative=True)
Pierre-Yves David
revsetbenchmarks: display relative change when meaningful...
r25539 ref = None
Pierre-Yves David
revsetbenchmark: add a summary at the end of execution...
r20855 for idx, data in enumerate(results):
Pierre-Yves David
revsetbenchmarks: allow running multiple variants per revset...
r25540 printresult(variants, idx, data[ridx], len(results),
verbose=options.verbose, reference=ref)
Pierre-Yves David
revsetbenchmarks: display relative change when meaningful...
r25539 ref = data[ridx]
Pulkit Goyal
contrib: revsetbenchmarks use absolute_import and print_function
r28564 print()