perf: make perftags clear tags cache correctly
Before this patch, "hg perftags" command doesn't measure performance
of "repo.tags()" correctly, because it doesn't clear tags cache
correctly.
9dca7653b525 replaced repo._tags with repo._tagscache, but didn't
change the code path to clear tags cache in perftags() at that time.
BTW, full history of "tags cache" is:
-
d7df759d0e97 (or 0.6) introduced repo.tagscache as the first "tags cache"
-
5614a628d173 (or 1.4) replaced repo.tagscache with repo._tags
-
9dca7653b525 (or 2.0) replaced repo._tags with repo._tagscache
-
98c867ac1330 (or 2.5) made repo._tagscache filteredpropertycache
To make perftags clear tags cache correctly, and to increase
"historical portability" of perftags, this patch examines existence of
attributes in repo object, and guess appropriate procedure to clear
tags cache.
To avoid examining existence of attributes at each repetition, this
patch makes repocleartagscachefunc() return the function, which
actually clears tags cache.
mozilla-central repo (85 tags on 308365 revs) with each Mercurial
version between before and after this patch.
==== ========= =========
ver before after
==== ========= =========
1.9 0.476062 0.466464
------- *1 -------
2.0 0.346309 0.458327
2.1 0.343106 0.454489
------- *2 -------
2.2 0.069790 0.071263
2.3 0.067829 0.069340
2.4 0.068075 0.069573
------- *3 -------
2.5 0.021896 0.022406
2.6 0.021900 0.022374
2.7 0.021883 0.022379
2.8 0.021949 0.022327
2.9 0.021877 0.022330
3.0 0.021860 0.022314
3.1 0.021869 0.022669
3.2 0.021831 0.022668
3.3 0.021809 0.022691
3.4 0.021861 0.022916
3.5 0.019335 0.020749
3.6 0.019319 0.020866
3.7 0.018781 0.020251
------- *4 -------
3.8 0.068262 0.072558
3.9 0.069682 0.073773
==== ========= =========
(*1) repo._tags was replaced with repo._tagscache at this point
"repo._tags = None" in perftags "before" this patch doesn't clear
tags cache for Mercurial 2.0 or later. This causes significant
gap of "before" between 1.9 and 2.0 .
(*2) I'm not sure about significant gap at this point, but release
note of 2.2 described "a number of significant performance
improvements for large repositories"
(*3) filtered changelog was cached in repoview as repoview.changelog
at this point (by
4d92e2d75cff)
This avoids calculation of filtered changelog at each repetition
of t().
(*4) calculation of filtered changelog was included into wall time at
this point (by
332926212ef8), again
See below for detail about this significant gap:
https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-April/083410.html
# Since it's not easy to write a test that portably deals
# with files from different users/groups, we cheat a bit by
# monkey-patching some functions in the util module
from __future__ import absolute_import, print_function
import os
from mercurial import (
error,
ui as uimod,
util,
)
hgrc = os.environ['HGRCPATH']
f = open(hgrc)
basehgrc = f.read()
f.close()
def testui(user='foo', group='bar', tusers=(), tgroups=(),
cuser='foo', cgroup='bar', debug=False, silent=False,
report=True):
# user, group => owners of the file
# tusers, tgroups => trusted users/groups
# cuser, cgroup => user/group of the current process
# write a global hgrc with the list of trusted users/groups and
# some setting so that we can be sure it was read
f = open(hgrc, 'w')
f.write(basehgrc)
f.write('\n[paths]\n')
f.write('global = /some/path\n\n')
if tusers or tgroups:
f.write('[trusted]\n')
if tusers:
f.write('users = %s\n' % ', '.join(tusers))
if tgroups:
f.write('groups = %s\n' % ', '.join(tgroups))
f.close()
# override the functions that give names to uids and gids
def username(uid=None):
if uid is None:
return cuser
return user
util.username = username
def groupname(gid=None):
if gid is None:
return 'bar'
return group
util.groupname = groupname
def isowner(st):
return user == cuser
util.isowner = isowner
# try to read everything
#print '# File belongs to user %s, group %s' % (user, group)
#print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
kind = ('different', 'same')
who = ('', 'user', 'group', 'user and the group')
trusted = who[(user in tusers) + 2*(group in tgroups)]
if trusted:
trusted = ', but we trust the ' + trusted
print('# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
trusted))
u = uimod.ui()
u.setconfig('ui', 'debug', str(bool(debug)))
u.setconfig('ui', 'report_untrusted', str(bool(report)))
u.readconfig('.hg/hgrc')
if silent:
return u
print('trusted')
for name, path in u.configitems('paths'):
print(' ', name, '=', path)
print('untrusted')
for name, path in u.configitems('paths', untrusted=True):
print('.', end=' ')
u.config('paths', name) # warning with debug=True
print('.', end=' ')
u.config('paths', name, untrusted=True) # no warnings
print(name, '=', path)
print()
return u
os.mkdir('repo')
os.chdir('repo')
os.mkdir('.hg')
f = open('.hg/hgrc', 'w')
f.write('[paths]\n')
f.write('local = /another/path\n\n')
f.close()
#print '# Everything is run by user foo, group bar\n'
# same user, same group
testui()
# same user, different group
testui(group='def')
# different user, same group
testui(user='abc')
# ... but we trust the group
testui(user='abc', tgroups=['bar'])
# different user, different group
testui(user='abc', group='def')
# ... but we trust the user
testui(user='abc', group='def', tusers=['abc'])
# ... but we trust the group
testui(user='abc', group='def', tgroups=['def'])
# ... but we trust the user and the group
testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
# ... but we trust all users
print('# we trust all users')
testui(user='abc', group='def', tusers=['*'])
# ... but we trust all groups
print('# we trust all groups')
testui(user='abc', group='def', tgroups=['*'])
# ... but we trust the whole universe
print('# we trust all users and groups')
testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
# ... check that users and groups are in different namespaces
print("# we don't get confused by users and groups with the same name")
testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
# ... lists of user names work
print("# list of user names")
testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
tgroups=['bar', 'baz', 'qux'])
# ... lists of group names work
print("# list of group names")
testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
tgroups=['bar', 'def', 'baz', 'qux'])
print("# Can't figure out the name of the user running this process")
testui(user='abc', group='def', cuser=None)
print("# prints debug warnings")
u = testui(user='abc', group='def', cuser='foo', debug=True)
print("# report_untrusted enabled without debug hides warnings")
u = testui(user='abc', group='def', cuser='foo', report=False)
print("# report_untrusted enabled with debug shows warnings")
u = testui(user='abc', group='def', cuser='foo', debug=True, report=False)
print("# ui.readconfig sections")
filename = 'foobar'
f = open(filename, 'w')
f.write('[foobar]\n')
f.write('baz = quux\n')
f.close()
u.readconfig(filename, sections=['foobar'])
print(u.config('foobar', 'baz'))
print()
print("# read trusted, untrusted, new ui, trusted")
u = uimod.ui()
u.setconfig('ui', 'debug', 'on')
u.readconfig(filename)
u2 = u.copy()
def username(uid=None):
return 'foo'
util.username = username
u2.readconfig('.hg/hgrc')
print('trusted:')
print(u2.config('foobar', 'baz'))
print('untrusted:')
print(u2.config('foobar', 'baz', untrusted=True))
print()
print("# error handling")
def assertraises(f, exc=error.Abort):
try:
f()
except exc as inst:
print('raised', inst.__class__.__name__)
else:
print('no exception?!')
print("# file doesn't exist")
os.unlink('.hg/hgrc')
assert not os.path.exists('.hg/hgrc')
testui(debug=True, silent=True)
testui(user='abc', group='def', debug=True, silent=True)
print()
print("# parse error")
f = open('.hg/hgrc', 'w')
f.write('foo')
f.close()
try:
testui(user='abc', group='def', silent=True)
except error.ParseError as inst:
print(inst)
try:
testui(debug=True, silent=True)
except error.ParseError as inst:
print(inst)