perf.py
410 lines
| 11.5 KiB
| text/x-python
|
PythonLexer
/ contrib / perf.py
Matt Mackall
|
r7366 | # perf.py - performance test routines | ||
Dirkjan Ochtman
|
r8873 | '''helper extension to measure performance''' | ||
Matt Mackall
|
r7366 | |||
Simon Heimberg
|
r19322 | from mercurial import cmdutil, scmutil, util, commands, obsolete | ||
Siddharth Agarwal
|
r18877 | from mercurial import repoview, branchmap, merge, copies | ||
Matt Mackall
|
r7366 | import time, os, sys | ||
Pierre-Yves David
|
r18237 | cmdtable = {} | ||
command = cmdutil.command(cmdtable) | ||||
Patrick Mezard
|
r9826 | def timer(func, title=None): | ||
Matt Mackall
|
r7366 | results = [] | ||
begin = time.time() | ||||
count = 0 | ||||
Martin Geisler
|
r14494 | while True: | ||
Matt Mackall
|
r7366 | ostart = os.times() | ||
cstart = time.time() | ||||
r = func() | ||||
cstop = time.time() | ||||
ostop = os.times() | ||||
count += 1 | ||||
a, b = ostart, ostop | ||||
results.append((cstop - cstart, b[0] - a[0], b[1]-a[1])) | ||||
if cstop - begin > 3 and count >= 100: | ||||
break | ||||
if cstop - begin > 10 and count >= 3: | ||||
break | ||||
Patrick Mezard
|
r9826 | if title: | ||
sys.stderr.write("! %s\n" % title) | ||||
Matt Mackall
|
r7366 | if r: | ||
sys.stderr.write("! result: %s\n" % r) | ||||
m = min(results) | ||||
sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n" | ||||
% (m[0], m[1] + m[2], m[1], m[2], count)) | ||||
Pierre-Yves David
|
r18237 | @command('perfwalk') | ||
Matt Mackall
|
r7366 | def perfwalk(ui, repo, *pats): | ||
try: | ||||
Matt Mackall
|
r14671 | m = scmutil.match(repo[None], pats, {}) | ||
Augie Fackler
|
r10176 | timer(lambda: len(list(repo.dirstate.walk(m, [], True, False)))) | ||
Brodie Rao
|
r16689 | except Exception: | ||
Matt Mackall
|
r7366 | try: | ||
Matt Mackall
|
r14671 | m = scmutil.match(repo[None], pats, {}) | ||
Matt Mackall
|
r10282 | timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)])) | ||
Brodie Rao
|
r16689 | except Exception: | ||
Matt Mackall
|
r7366 | timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) | ||
Durham Goode
|
r19292 | @command('perfannotate') | ||
def perfannotate(ui, repo, f): | ||||
fc = repo['.'][f] | ||||
timer(lambda: len(fc.annotate(True))) | ||||
Pierre-Yves David
|
r18237 | @command('perfstatus', | ||
[('u', 'unknown', False, | ||||
'ask status to look for unknown files')]) | ||||
Siddharth Agarwal
|
r18033 | def perfstatus(ui, repo, **opts): | ||
Matt Mackall
|
r7366 | #m = match.always(repo.root, repo.getcwd()) | ||
Brodie Rao
|
r16683 | #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, | ||
# False)))) | ||||
Siddharth Agarwal
|
r18033 | timer(lambda: sum(map(len, repo.status(**opts)))) | ||
Matt Mackall
|
r7366 | |||
Siddharth Agarwal
|
r18871 | @command('perfaddremove') | ||
def perfaddremove(ui, repo): | ||||
try: | ||||
oldquiet = repo.ui.quiet | ||||
repo.ui.quiet = True | ||||
timer(lambda: scmutil.addremove(repo, dry_run=True)) | ||||
finally: | ||||
repo.ui.quiet = oldquiet | ||||
Bryan O'Sullivan
|
r16785 | def clearcaches(cl): | ||
# behave somewhat consistently across internal API changes | ||||
if util.safehasattr(cl, 'clearcaches'): | ||||
cl.clearcaches() | ||||
elif util.safehasattr(cl, '_nodecache'): | ||||
from mercurial.node import nullid, nullrev | ||||
cl._nodecache = {nullid: nullrev} | ||||
cl._nodepos = None | ||||
Pierre-Yves David
|
r18237 | @command('perfheads') | ||
Matt Mackall
|
r7366 | def perfheads(ui, repo): | ||
Bryan O'Sullivan
|
r16785 | cl = repo.changelog | ||
def d(): | ||||
len(cl.headrevs()) | ||||
clearcaches(cl) | ||||
timer(d) | ||||
Matt Mackall
|
r7366 | |||
Pierre-Yves David
|
r18237 | @command('perftags') | ||
Matt Mackall
|
r7366 | def perftags(ui, repo): | ||
import mercurial.changelog, mercurial.manifest | ||||
def t(): | ||||
repo.changelog = mercurial.changelog.changelog(repo.sopener) | ||||
repo.manifest = mercurial.manifest.manifest(repo.sopener) | ||||
Greg Ward
|
r9146 | repo._tags = None | ||
Matt Mackall
|
r7366 | return len(repo.tags()) | ||
timer(t) | ||||
Pierre-Yves David
|
r18237 | @command('perfancestors') | ||
Bryan O'Sullivan
|
r16802 | def perfancestors(ui, repo): | ||
heads = repo.changelog.headrevs() | ||||
def d(): | ||||
Bryan O'Sullivan
|
r16866 | for a in repo.changelog.ancestors(heads): | ||
Bryan O'Sullivan
|
r16802 | pass | ||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfancestorset') | ||
Siddharth Agarwal
|
r18080 | def perfancestorset(ui, repo, revset): | ||
revs = repo.revs(revset) | ||||
heads = repo.changelog.headrevs() | ||||
def d(): | ||||
Siddharth Agarwal
|
r18091 | s = repo.changelog.ancestors(heads) | ||
Siddharth Agarwal
|
r18080 | for rev in revs: | ||
rev in s | ||||
timer(d) | ||||
Bryan O'Sullivan
|
r18845 | @command('perfdirs') | ||
def perfdirs(ui, repo): | ||||
dirstate = repo.dirstate | ||||
'a' in dirstate | ||||
def d(): | ||||
dirstate.dirs() | ||||
del dirstate._dirs | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfdirstate') | ||
Matt Mackall
|
r7366 | def perfdirstate(ui, repo): | ||
"a" in repo.dirstate | ||||
def d(): | ||||
repo.dirstate.invalidate() | ||||
"a" in repo.dirstate | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfdirstatedirs') | ||
Matt Mackall
|
r7366 | def perfdirstatedirs(ui, repo): | ||
"a" in repo.dirstate | ||||
def d(): | ||||
"a" in repo.dirstate._dirs | ||||
del repo.dirstate._dirs | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfdirstatewrite') | ||
Bryan O'Sullivan
|
r16788 | def perfdirstatewrite(ui, repo): | ||
ds = repo.dirstate | ||||
"a" in ds | ||||
def d(): | ||||
ds._dirty = True | ||||
ds.write() | ||||
timer(d) | ||||
Siddharth Agarwal
|
r18817 | @command('perfmergecalculate', | ||
[('r', 'rev', '.', 'rev to merge against')]) | ||||
def perfmergecalculate(ui, repo, rev): | ||||
wctx = repo[None] | ||||
rctx = scmutil.revsingle(repo, rev, rev) | ||||
ancestor = wctx.ancestor(rctx) | ||||
# we don't want working dir files to be stat'd in the benchmark, so prime | ||||
# that cache | ||||
wctx.dirty() | ||||
def d(): | ||||
# acceptremote is True because we don't want prompts in the middle of | ||||
# our benchmark | ||||
merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False, | ||||
acceptremote=True) | ||||
timer(d) | ||||
Siddharth Agarwal
|
r18877 | @command('perfpathcopies', [], "REV REV") | ||
def perfpathcopies(ui, repo, rev1, rev2): | ||||
ctx1 = scmutil.revsingle(repo, rev1, rev1) | ||||
ctx2 = scmutil.revsingle(repo, rev2, rev2) | ||||
def d(): | ||||
copies.pathcopies(ctx1, ctx2) | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfmanifest') | ||
Matt Mackall
|
r7366 | def perfmanifest(ui, repo): | ||
def d(): | ||||
t = repo.manifest.tip() | ||||
m = repo.manifest.read(t) | ||||
repo.manifest.mapcache = None | ||||
repo.manifest._cache = None | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfchangeset') | ||
Matt Mackall
|
r16262 | def perfchangeset(ui, repo, rev): | ||
n = repo[rev].node() | ||||
def d(): | ||||
c = repo.changelog.read(n) | ||||
Matt Mackall
|
r16266 | #repo.changelog._cache = None | ||
Matt Mackall
|
r16262 | timer(d) | ||
Pierre-Yves David
|
r18237 | @command('perfindex') | ||
Matt Mackall
|
r7366 | def perfindex(ui, repo): | ||
Matt Mackall
|
r13255 | import mercurial.revlog | ||
Matt Mackall
|
r13277 | mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg | ||
Matt Mackall
|
r13254 | n = repo["tip"].node() | ||
Matt Mackall
|
r7366 | def d(): | ||
Matt Mackall
|
r16260 | cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i") | ||
cl.rev(n) | ||||
Matt Mackall
|
r7366 | timer(d) | ||
Pierre-Yves David
|
r18237 | @command('perfstartup') | ||
Matt Mackall
|
r7366 | def perfstartup(ui, repo): | ||
cmd = sys.argv[0] | ||||
def d(): | ||||
os.system("HGRCPATH= %s version -q > /dev/null" % cmd) | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfparents') | ||
Matt Mackall
|
r7366 | def perfparents(ui, repo): | ||
nl = [repo.changelog.node(i) for i in xrange(1000)] | ||||
def d(): | ||||
for n in nl: | ||||
repo.changelog.parents(n) | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perflookup') | ||
Matt Mackall
|
r7366 | def perflookup(ui, repo, rev): | ||
timer(lambda: len(repo.lookup(rev))) | ||||
Pierre-Yves David
|
r18237 | @command('perfrevrange') | ||
Bryan O'Sullivan
|
r16858 | def perfrevrange(ui, repo, *specs): | ||
revrange = scmutil.revrange | ||||
timer(lambda: len(revrange(repo, specs))) | ||||
Pierre-Yves David
|
r18237 | @command('perfnodelookup') | ||
Matt Mackall
|
r16309 | def perfnodelookup(ui, repo, rev): | ||
import mercurial.revlog | ||||
mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg | ||||
n = repo[rev].node() | ||||
Bryan O'Sullivan
|
r16414 | cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i") | ||
def d(): | ||||
cl.rev(n) | ||||
Bryan O'Sullivan
|
r16785 | clearcaches(cl) | ||
Bryan O'Sullivan
|
r16414 | timer(d) | ||
Pierre-Yves David
|
r18237 | @command('perflog', | ||
[('', 'rename', False, 'ask log to follow renames')]) | ||||
Alexander Solovyov
|
r9932 | def perflog(ui, repo, **opts): | ||
Alexander Solovyov
|
r7872 | ui.pushbuffer() | ||
Alexander Solovyov
|
r9932 | timer(lambda: commands.log(ui, repo, rev=[], date='', user='', | ||
copies=opts.get('rename'))) | ||||
Alexander Solovyov
|
r7872 | ui.popbuffer() | ||
Pierre-Yves David
|
r18237 | @command('perftemplating') | ||
Alexander Solovyov
|
r7872 | def perftemplating(ui, repo): | ||
ui.pushbuffer() | ||||
timer(lambda: commands.log(ui, repo, rev=[], date='', user='', | ||||
template='{date|shortdate} [{rev}:{node|short}]' | ||||
' {author|person}: {desc|firstline}\n')) | ||||
ui.popbuffer() | ||||
Pierre-Yves David
|
r18237 | @command('perfcca') | ||
Matt Mackall
|
r16386 | def perfcca(ui, repo): | ||
Joshua Redstone
|
r17216 | timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate)) | ||
Matt Mackall
|
r16386 | |||
Pierre-Yves David
|
r18237 | @command('perffncacheload') | ||
Bryan O'Sullivan
|
r16403 | def perffncacheload(ui, repo): | ||
Adrian Buehlmann
|
r17780 | s = repo.store | ||
Bryan O'Sullivan
|
r16403 | def d(): | ||
s.fncache._load() | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perffncachewrite') | ||
Bryan O'Sullivan
|
r16403 | def perffncachewrite(ui, repo): | ||
Adrian Buehlmann
|
r17780 | s = repo.store | ||
Bryan O'Sullivan
|
r16403 | s.fncache._load() | ||
def d(): | ||||
s.fncache._dirty = True | ||||
s.fncache.write() | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perffncacheencode') | ||
Adrian Buehlmann
|
r17553 | def perffncacheencode(ui, repo): | ||
Adrian Buehlmann
|
r17780 | s = repo.store | ||
Adrian Buehlmann
|
r17553 | s.fncache._load() | ||
def d(): | ||||
for p in s.fncache.entries: | ||||
s.encode(p) | ||||
timer(d) | ||||
Pierre-Yves David
|
r18237 | @command('perfdiffwd') | ||
Patrick Mezard
|
r9826 | def perfdiffwd(ui, repo): | ||
"""Profile diff of working directory changes""" | ||||
options = { | ||||
'w': 'ignore_all_space', | ||||
'b': 'ignore_space_change', | ||||
'B': 'ignore_blank_lines', | ||||
} | ||||
for diffopt in ('', 'w', 'b', 'B', 'wB'): | ||||
opts = dict((options[c], '1') for c in diffopt) | ||||
def d(): | ||||
ui.pushbuffer() | ||||
commands.diff(ui, repo, **opts) | ||||
ui.popbuffer() | ||||
title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none') | ||||
timer(d, title) | ||||
Pierre-Yves David
|
r18237 | @command('perfrevlog', | ||
[('d', 'dist', 100, 'distance between the revisions')], | ||||
"[INDEXFILE]") | ||||
Pradeepkumar Gayam
|
r11694 | def perfrevlog(ui, repo, file_, **opts): | ||
from mercurial import revlog | ||||
dist = opts['dist'] | ||||
def d(): | ||||
r = revlog.revlog(lambda fn: open(fn, 'rb'), file_) | ||||
for x in xrange(0, len(r), dist): | ||||
r.revision(r.node(x)) | ||||
timer(d) | ||||
Pierre-Yves David
|
r18239 | @command('perfrevset', | ||
[('C', 'clear', False, 'clear volatile cache between each call.')], | ||||
Pierre-Yves David
|
r18238 | "REVSET") | ||
Pierre-Yves David
|
r18239 | def perfrevset(ui, repo, expr, clear=False): | ||
"""benchmark the execution time of a revset | ||||
Mads Kiilerich
|
r18644 | Use the --clean option if need to evaluate the impact of build volatile | ||
Pierre-Yves David
|
r18239 | revisions set cache on the revset execution. Volatile cache hold filtered | ||
and obsolete related cache.""" | ||||
Siddharth Agarwal
|
r18062 | def d(): | ||
Pierre-Yves David
|
r18239 | if clear: | ||
repo.invalidatevolatilesets() | ||||
Siddharth Agarwal
|
r18062 | repo.revs(expr) | ||
timer(d) | ||||
Pierre-Yves David
|
r18240 | |||
@command('perfvolatilesets') | ||||
Pierre-Yves David
|
r18241 | def perfvolatilesets(ui, repo, *names): | ||
Pierre-Yves David
|
r18240 | """benchmark the computation of various volatile set | ||
Volatile set computes element related to filtering and obsolescence.""" | ||||
repo = repo.unfiltered() | ||||
def getobs(name): | ||||
def d(): | ||||
repo.invalidatevolatilesets() | ||||
obsolete.getrevs(repo, name) | ||||
return d | ||||
Pierre-Yves David
|
r18241 | allobs = sorted(obsolete.cachefuncs) | ||
if names: | ||||
allobs = [n for n in allobs if n in names] | ||||
for name in allobs: | ||||
Pierre-Yves David
|
r18240 | timer(getobs(name), title=name) | ||
def getfiltered(name): | ||||
def d(): | ||||
repo.invalidatevolatilesets() | ||||
repoview.filteredrevs(repo, name) | ||||
return d | ||||
Pierre-Yves David
|
r18241 | allfilter = sorted(repoview.filtertable) | ||
if names: | ||||
allfilter = [n for n in allfilter if n in names] | ||||
for name in allfilter: | ||||
Pierre-Yves David
|
r18240 | timer(getfiltered(name), title=name) | ||
Pierre-Yves David
|
r18304 | |||
@command('perfbranchmap', | ||||
[('f', 'full', False, | ||||
'Includes build time of subset'), | ||||
]) | ||||
def perfbranchmap(ui, repo, full=False): | ||||
"""benchmark the update of a branchmap | ||||
This benchmarks the full repo.branchmap() call with read and write disabled | ||||
""" | ||||
def getbranchmap(filtername): | ||||
"""generate a benchmark function for the filtername""" | ||||
if filtername is None: | ||||
view = repo | ||||
else: | ||||
view = repo.filtered(filtername) | ||||
def d(): | ||||
if full: | ||||
view._branchcaches.clear() | ||||
else: | ||||
view._branchcaches.pop(filtername, None) | ||||
view.branchmap() | ||||
return d | ||||
# add filter in smaller subset to bigger subset | ||||
possiblefilters = set(repoview.filtertable) | ||||
allfilters = [] | ||||
while possiblefilters: | ||||
for name in possiblefilters: | ||||
subset = repoview.subsettable.get(name) | ||||
if subset not in possiblefilters: | ||||
break | ||||
else: | ||||
assert False, 'subset cycle %s!' % possiblefilters | ||||
allfilters.append(name) | ||||
possiblefilters.remove(name) | ||||
# warm the cache | ||||
if not full: | ||||
for name in allfilters: | ||||
repo.filtered(name).branchmap() | ||||
# add unfiltered | ||||
allfilters.append(None) | ||||
oldread = branchmap.read | ||||
oldwrite = branchmap.branchcache.write | ||||
try: | ||||
branchmap.read = lambda repo: None | ||||
branchmap.write = lambda repo: None | ||||
for name in allfilters: | ||||
timer(getbranchmap(name), title=str(name)) | ||||
finally: | ||||
branchmap.read = oldread | ||||
branchmap.branchcache.write = oldwrite | ||||