sapling/tests/perftest.py
Durham Goode 14a161093f perftest: remove hard coded revs and paths
Summary: This removes the hard coded revs and paths used in the perf test script.

Test Plan: Ran the perf test

Reviewers: #fastmanifest, ttung

Reviewed By: ttung

Differential Revision: https://phabricator.intern.facebook.com/D3755335

Signature: t1:3755335:1471930070:e90a242a8f73d2fc32ea68bc99cae88b00af14c9
2016-08-29 16:19:52 -07:00

300 lines
9.8 KiB
Python

# perf-test.py - simple extension for perf testing manifest performance
#
# Copyright 2016 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from mercurial import cmdutil, error, manifest, scmutil
from mercurial.node import nullid
from remotefilelog import datapack, contentstore, shallowutil
import pdb, hashlib, os, time
from fastmanifest.implementation import fastmanifestcache
from fastmanifest import cachemanager
import ctreemanifest
cmdtable = {}
command = cmdutil.command(cmdtable)
testedwith = ''
@command('testtree', [
('', 'build', '', ''),
('', 'test', '', ''),
('', 'kind', '', ''),
('i', 'iterations', '10', ''),
('', 'revs', 'master + master~5', ''),
], '')
def testpackedtrees(ui, repo, *args, **opts):
packpath = shallowutil.getpackpath(repo, 'manifest')
if not os.path.exists(packpath):
os.mkdir(packpath)
opener = scmutil.vfs(packpath)
if opts.get('build'):
with datapack.mutabledatapack(opener) as newpack:
buildtreepack(repo, newpack, opts.get('build'))
newpack.close()
packstore = datapack.datapackstore(opener.base,
usecdatapack=ui.configbool('remotefilelog', 'fastdatapack'))
unionstore = contentstore.unioncontentstore(packstore)
ctxs = list(repo.set(opts.get('revs')))
profiletreepack(repo, unionstore, ctxs[0].hex(), ctxs[1].hex(), opts)
class cachestore(object):
def __init__(self):
self._cache = {}
def get(self, name, node):
return self._cache[(name, node)]
def getdeltachain(self, name, node):
data = self._cache[(name, node)]
return [(name, node, name, nullid, data)]
def add(self, name, node, data):
self._cache[(name, node)] = data
def buildtreepack(repo, pack, revs):
mf = repo.manifest
cache = cachestore()
packstore = datapack.datapackstore(pack.opener.base)
store = contentstore.unioncontentstore(cache, packstore)
ctxs = list(repo.set(revs))
for count, ctx in enumerate(ctxs):
repo.ui.progress(('manifests'), count, total=len(ctxs))
mfnode = ctx.manifestnode()
try:
store.get('', mfnode)
continue
except KeyError:
pass
try:
# if we have the parent tree, let's use it
p1, p2 = mf.parents(mfnode)
p1rev = mf.rev(p1)
mfrev = mf.rev(mfnode)
store.get('', p1)
if p2 == nullid and mf.deltaparent(mfrev) == p1rev:
mfdelta = mf.readdelta(mfnode)
adds = list((filename, n, f)
for (filename, n, f) in mfdelta.iterentries())
deletes = set(ctx.files()).difference(
filename
for filename, n, f in adds)
else:
mfctx = ctx.manifest()
mfdiff = mf.read(p1).diff(mfctx)
adds = list((f, bn, bf) for f, ((an, af), (bn, bf)) in
mfdiff.iteritems() if bn is not None)
deletes = list(f for f, ((an, af), (bn, bf)) in
mfdiff.iteritems() if bn is None)
tmfctx = read(store, '', p1).copy()
for filename in deletes:
del tmfctx[filename]
for filename, n, f in adds:
tmfctx[filename] = n
tmfctx.setflag(filename, f)
except KeyError:
mfctx = ctx.manifest()
tmfctx = manifest.treemanifest(text=mfctx.text())
p1, p2 = mf.parents(mfnode)
add(store, cache, pack, tmfctx, ctx.rev(), p1, p2,
forcenode=ctx.manifestnode())
repo.ui.progress(('manifests'), None)
def add(store, cache, pack, mf, linkrev, p1, p2, forcenode=False):
try:
store.get(mf._dir, p1)
p1mf = read(store, mf._dir, p1)
except KeyError:
p1mf = manifest.treemanifest()
try:
store.get(mf._dir, p2)
p2mf = read(store, mf._dir, p2)
except KeyError:
p2mf = manifest.treemanifest()
return _addtree(store, cache, pack, mf, linkrev,
p1mf, p2mf, forcenode=forcenode)
def read(store, dir, node):
def gettext():
return store.get(dir, node)
def readsubtree(dir, subm):
return read(store, dir, subm)
m = manifest.treemanifest(dir=dir)
m.read(gettext, readsubtree)
m.setnode(node)
return m
def _addtree(store, cache, pack, m, linkrev, m1, m2, forcenode=False):
# If the manifest is unchanged compared to one parent,
# don't write a new revision
if m.unmodifiedsince(m1) or m.unmodifiedsince(m2):
return m.node()
def writesubtree(subm, subp1, subp2):
add(store, cache, pack, subm, linkrev, subp1, subp2)
usemfv2 = False
m1._load()
m2._load()
m.writesubtrees(m1, m2, writesubtree)
text = m.dirtext(usemfv2)
# Double-check whether contents are unchanged to one parent
if text == m1.dirtext(usemfv2):
n = m1.node()
elif text == m2.dirtext(usemfv2):
n = m2.node()
else:
n = hashlib.sha1(m1.node() + m2.node() + text).digest()
# Save nodeid so parent manifest can calculate its nodeid
if forcenode:
n = forcenode
try:
store.get(m._dir, n)
except KeyError:
deltabase = nullid
delta = text
if False and m1.node() != nullid:
deltabase = m1.node()
delta = mdiff.textdiff(m1.dirtext(usemfv2), text)
pack.add(m._dir, n, deltabase, delta)
cache.add(m._dir, n, text)
m.setnode(n)
return n
def profiletreepack(repo, store, rev1, rev2, opts):
def exectest(name, count, prep, func):
elapsed = 0
elapsedprep = 0
for i in xrange(0, count):
args = []
if prep:
startprep = time.time()
args = prep()
elapsedprep += time.time() - startprep
import gc
gc.disable()
start = time.time()
func(*args)
elapsed += time.time() - start
gc.enable()
gc.collect()
repo.ui.progress(name, i, total=count)
repo.ui.progress(name, None)
total = elapsed + elapsedprep
repo.ui.status(("%0.2f (%0.2f)" % (elapsedprep, elapsedprep / (count *
1.0))).ljust(15))
repo.ui.status(("%0.2f (%0.2f)" % (elapsed, elapsed / (count *
1.0))).ljust(15))
repo.ui.status(("%0.2f (%0.2f)" % (total, total / (count *
1.0))).ljust(15))
ctx1 = list(repo.set(rev1))[0]
ctx2 = list(repo.set(rev2))[0]
cachemanager.cachemanifestfillandtrim(
repo.ui, repo, ['%s + %s' % (ctx1.rev(), ctx2.rev())])
def treeconstructor(mfnode):
return read(store, '', mfnode)
def flatconstructor(mfnode):
repo.manifest.clearcaches()
return repo.manifest.read(mfnode)._flatmanifest()
cacheopener = scmutil.vfs(repo.vfs.join('cache', 'fastmanifest_test'))
if not os.path.exists(cacheopener.base):
os.mkdir(cacheopener.base)
manager = fastmanifestcache.getinstance(cacheopener, repo.ui)
def fastconstructor(mfnode):
repo.manifest.clearcaches()
manager.inmemorycache.clear()
return repo.manifest.read(mfnode)._cachedmanifest()
class FakeStore(object):
def __init__(self):
self._cache = {}
def get(self, filename, node):
try:
return self._cache[(filename, node)]
except KeyError:
result = store.get(filename, node)
self._cache[(filename, node)] = result
return result
fakestore = FakeStore()
def ctreeconstructor(mfnode):
# Enable the fake store to remove blob lookup time
#treemf = ctreemanifest.treemanifest(fakestore, mfnode)
treemf = ctreemanifest.treemanifest(store, mfnode)
return treemf
# Test bodies
def prepone(new):
return [new(ctx1.manifestnode())]
def preptwo(new):
m1 = new(ctx1.manifestnode())
m2 = new(ctx2.manifestnode())
return m1, m2
def diff(m1, m2):
diff = m1.diff(m2)
if len(diff) < 10000:
raise error.Abort("diff only found %s items" % len(diff))
def find(m1):
findresult = m1.find('fbcode/hphp/test/run')
if findresult == (None, None):
raise error.Abort("None find result")
def fulliter(m1):
count = 0
for x in m1:
count += 1
if count < 900000:
raise error.Abort("fulliter only found %s files" % count)
kindconstructor = {
'tree': treeconstructor,
'flat': flatconstructor,
'fast': fastconstructor,
'ctree': ctreeconstructor,
}
iterations = int(opts.get('iterations'))
testopts = {
'diff': (preptwo, diff, iterations),
'find': (prepone, find, iterations),
'fulliter': (prepone, fulliter, max(1, iterations / 2)),
}
tests = opts.get('test', 'all').split(',')
if tests[0] in ['all', '']:
tests = testopts.keys()
kinds = opts.get('kind', 'all').split(',')
if kinds[0] in ['all', '']:
kinds = kindconstructor.keys()
# Prime any caches
for kind in kinds:
preptwo(kindconstructor[kind])
for test in tests:
prepfunc, func, iterations = testopts[test]
teststr = ('%s (%s)' % (test, iterations)).ljust(14)
repo.ui.status(("\n%sPrep Run Total\n") %
(teststr))
for kind in kinds:
repo.ui.status(("%s" % (kind)).ljust(14))
def prep():
return prepfunc(kindconstructor[kind])
exectest(test, iterations, prep, func)
repo.ui.status("\n")
repo.ui.status("\n")