##// END OF EJS Templates
perf: get subsettable from appropriate module for Mercurial earlier than 2.9...
FUJIWARA Katsunori -
r30144:14031d18 default
parent child Browse files
Show More
@@ -1,978 +1,997 b''
1 # perf.py - performance test routines
1 # perf.py - performance test routines
2 '''helper extension to measure performance'''
2 '''helper extension to measure performance'''
3
3
4 # "historical portability" policy of perf.py:
4 # "historical portability" policy of perf.py:
5 #
5 #
6 # We have to do:
6 # We have to do:
7 # - make perf.py "loadable" with as wide Mercurial version as possible
7 # - make perf.py "loadable" with as wide Mercurial version as possible
8 # This doesn't mean that perf commands work correctly with that Mercurial.
8 # This doesn't mean that perf commands work correctly with that Mercurial.
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
10 # - make historical perf command work correctly with as wide Mercurial
10 # - make historical perf command work correctly with as wide Mercurial
11 # version as possible
11 # version as possible
12 #
12 #
13 # We have to do, if possible with reasonable cost:
13 # We have to do, if possible with reasonable cost:
14 # - make recent perf command for historical feature work correctly
14 # - make recent perf command for historical feature work correctly
15 # with early Mercurial
15 # with early Mercurial
16 #
16 #
17 # We don't have to do:
17 # We don't have to do:
18 # - make perf command for recent feature work correctly with early
18 # - make perf command for recent feature work correctly with early
19 # Mercurial
19 # Mercurial
20
20
21 from __future__ import absolute_import
21 from __future__ import absolute_import
22 import functools
22 import functools
23 import os
23 import os
24 import random
24 import random
25 import sys
25 import sys
26 import time
26 import time
27 from mercurial import (
27 from mercurial import (
28 changegroup,
28 changegroup,
29 cmdutil,
29 cmdutil,
30 commands,
30 commands,
31 copies,
31 copies,
32 error,
32 error,
33 extensions,
33 extensions,
34 mdiff,
34 mdiff,
35 merge,
35 merge,
36 revlog,
36 revlog,
37 util,
37 util,
38 )
38 )
39
39
40 # for "historical portability":
40 # for "historical portability":
41 # try to import modules separately (in dict order), and ignore
41 # try to import modules separately (in dict order), and ignore
42 # failure, because these aren't available with early Mercurial
42 # failure, because these aren't available with early Mercurial
43 try:
43 try:
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
45 except ImportError:
45 except ImportError:
46 pass
46 pass
47 try:
47 try:
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
49 except ImportError:
49 except ImportError:
50 pass
50 pass
51 try:
51 try:
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
53 except ImportError:
53 except ImportError:
54 pass
54 pass
55 try:
55 try:
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
57 except ImportError:
57 except ImportError:
58 pass
58 pass
59
59
60 # for "historical portability":
60 # for "historical portability":
61 # define util.safehasattr forcibly, because util.safehasattr has been
61 # define util.safehasattr forcibly, because util.safehasattr has been
62 # available since 1.9.3 (or 94b200a11cf7)
62 # available since 1.9.3 (or 94b200a11cf7)
63 _undefined = object()
63 _undefined = object()
64 def safehasattr(thing, attr):
64 def safehasattr(thing, attr):
65 return getattr(thing, attr, _undefined) is not _undefined
65 return getattr(thing, attr, _undefined) is not _undefined
66 setattr(util, 'safehasattr', safehasattr)
66 setattr(util, 'safehasattr', safehasattr)
67
67
68 # for "historical portability":
68 # for "historical portability":
69 # use locally defined empty option list, if formatteropts isn't
69 # use locally defined empty option list, if formatteropts isn't
70 # available, because commands.formatteropts has been available since
70 # available, because commands.formatteropts has been available since
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
72 # available since 2.2 (or ae5f92e154d3)
72 # available since 2.2 (or ae5f92e154d3)
73 formatteropts = getattr(commands, "formatteropts", [])
73 formatteropts = getattr(commands, "formatteropts", [])
74
74
75 # for "historical portability":
75 # for "historical portability":
76 # use locally defined option list, if debugrevlogopts isn't available,
76 # use locally defined option list, if debugrevlogopts isn't available,
77 # because commands.debugrevlogopts has been available since 3.7 (or
77 # because commands.debugrevlogopts has been available since 3.7 (or
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
79 # since 1.9 (or a79fea6b3e77).
79 # since 1.9 (or a79fea6b3e77).
80 revlogopts = getattr(commands, "debugrevlogopts", [
80 revlogopts = getattr(commands, "debugrevlogopts", [
81 ('c', 'changelog', False, ('open changelog')),
81 ('c', 'changelog', False, ('open changelog')),
82 ('m', 'manifest', False, ('open manifest')),
82 ('m', 'manifest', False, ('open manifest')),
83 ('', 'dir', False, ('open directory manifest')),
83 ('', 'dir', False, ('open directory manifest')),
84 ])
84 ])
85
85
86 cmdtable = {}
86 cmdtable = {}
87
87
88 # for "historical portability":
88 # for "historical portability":
89 # define parsealiases locally, because cmdutil.parsealiases has been
89 # define parsealiases locally, because cmdutil.parsealiases has been
90 # available since 1.5 (or 6252852b4332)
90 # available since 1.5 (or 6252852b4332)
91 def parsealiases(cmd):
91 def parsealiases(cmd):
92 return cmd.lstrip("^").split("|")
92 return cmd.lstrip("^").split("|")
93
93
94 if safehasattr(cmdutil, 'command'):
94 if safehasattr(cmdutil, 'command'):
95 import inspect
95 import inspect
96 command = cmdutil.command(cmdtable)
96 command = cmdutil.command(cmdtable)
97 if 'norepo' not in inspect.getargspec(command)[0]:
97 if 'norepo' not in inspect.getargspec(command)[0]:
98 # for "historical portability":
98 # for "historical portability":
99 # wrap original cmdutil.command, because "norepo" option has
99 # wrap original cmdutil.command, because "norepo" option has
100 # been available since 3.1 (or 75a96326cecb)
100 # been available since 3.1 (or 75a96326cecb)
101 _command = command
101 _command = command
102 def command(name, options=(), synopsis=None, norepo=False):
102 def command(name, options=(), synopsis=None, norepo=False):
103 if norepo:
103 if norepo:
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
105 return _command(name, list(options), synopsis)
105 return _command(name, list(options), synopsis)
106 else:
106 else:
107 # for "historical portability":
107 # for "historical portability":
108 # define "@command" annotation locally, because cmdutil.command
108 # define "@command" annotation locally, because cmdutil.command
109 # has been available since 1.9 (or 2daa5179e73f)
109 # has been available since 1.9 (or 2daa5179e73f)
110 def command(name, options=(), synopsis=None, norepo=False):
110 def command(name, options=(), synopsis=None, norepo=False):
111 def decorator(func):
111 def decorator(func):
112 if synopsis:
112 if synopsis:
113 cmdtable[name] = func, list(options), synopsis
113 cmdtable[name] = func, list(options), synopsis
114 else:
114 else:
115 cmdtable[name] = func, list(options)
115 cmdtable[name] = func, list(options)
116 if norepo:
116 if norepo:
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
118 return func
118 return func
119 return decorator
119 return decorator
120
120
121 def getlen(ui):
121 def getlen(ui):
122 if ui.configbool("perf", "stub"):
122 if ui.configbool("perf", "stub"):
123 return lambda x: 1
123 return lambda x: 1
124 return len
124 return len
125
125
126 def gettimer(ui, opts=None):
126 def gettimer(ui, opts=None):
127 """return a timer function and formatter: (timer, formatter)
127 """return a timer function and formatter: (timer, formatter)
128
128
129 This function exists to gather the creation of formatter in a single
129 This function exists to gather the creation of formatter in a single
130 place instead of duplicating it in all performance commands."""
130 place instead of duplicating it in all performance commands."""
131
131
132 # enforce an idle period before execution to counteract power management
132 # enforce an idle period before execution to counteract power management
133 # experimental config: perf.presleep
133 # experimental config: perf.presleep
134 time.sleep(ui.configint("perf", "presleep", 1))
134 time.sleep(ui.configint("perf", "presleep", 1))
135
135
136 if opts is None:
136 if opts is None:
137 opts = {}
137 opts = {}
138 # redirect all to stderr
138 # redirect all to stderr
139 ui = ui.copy()
139 ui = ui.copy()
140 ui.fout = ui.ferr
140 ui.fout = ui.ferr
141 # get a formatter
141 # get a formatter
142 fm = ui.formatter('perf', opts)
142 fm = ui.formatter('perf', opts)
143 # stub function, runs code only once instead of in a loop
143 # stub function, runs code only once instead of in a loop
144 # experimental config: perf.stub
144 # experimental config: perf.stub
145 if ui.configbool("perf", "stub"):
145 if ui.configbool("perf", "stub"):
146 return functools.partial(stub_timer, fm), fm
146 return functools.partial(stub_timer, fm), fm
147 return functools.partial(_timer, fm), fm
147 return functools.partial(_timer, fm), fm
148
148
149 def stub_timer(fm, func, title=None):
149 def stub_timer(fm, func, title=None):
150 func()
150 func()
151
151
152 def _timer(fm, func, title=None):
152 def _timer(fm, func, title=None):
153 results = []
153 results = []
154 begin = time.time()
154 begin = time.time()
155 count = 0
155 count = 0
156 while True:
156 while True:
157 ostart = os.times()
157 ostart = os.times()
158 cstart = time.time()
158 cstart = time.time()
159 r = func()
159 r = func()
160 cstop = time.time()
160 cstop = time.time()
161 ostop = os.times()
161 ostop = os.times()
162 count += 1
162 count += 1
163 a, b = ostart, ostop
163 a, b = ostart, ostop
164 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
164 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
165 if cstop - begin > 3 and count >= 100:
165 if cstop - begin > 3 and count >= 100:
166 break
166 break
167 if cstop - begin > 10 and count >= 3:
167 if cstop - begin > 10 and count >= 3:
168 break
168 break
169
169
170 fm.startitem()
170 fm.startitem()
171
171
172 if title:
172 if title:
173 fm.write('title', '! %s\n', title)
173 fm.write('title', '! %s\n', title)
174 if r:
174 if r:
175 fm.write('result', '! result: %s\n', r)
175 fm.write('result', '! result: %s\n', r)
176 m = min(results)
176 m = min(results)
177 fm.plain('!')
177 fm.plain('!')
178 fm.write('wall', ' wall %f', m[0])
178 fm.write('wall', ' wall %f', m[0])
179 fm.write('comb', ' comb %f', m[1] + m[2])
179 fm.write('comb', ' comb %f', m[1] + m[2])
180 fm.write('user', ' user %f', m[1])
180 fm.write('user', ' user %f', m[1])
181 fm.write('sys', ' sys %f', m[2])
181 fm.write('sys', ' sys %f', m[2])
182 fm.write('count', ' (best of %d)', count)
182 fm.write('count', ' (best of %d)', count)
183 fm.plain('\n')
183 fm.plain('\n')
184
184
185 # utilities for historical portability
185 # utilities for historical portability
186
186
187 def safeattrsetter(obj, name, ignoremissing=False):
187 def safeattrsetter(obj, name, ignoremissing=False):
188 """Ensure that 'obj' has 'name' attribute before subsequent setattr
188 """Ensure that 'obj' has 'name' attribute before subsequent setattr
189
189
190 This function is aborted, if 'obj' doesn't have 'name' attribute
190 This function is aborted, if 'obj' doesn't have 'name' attribute
191 at runtime. This avoids overlooking removal of an attribute, which
191 at runtime. This avoids overlooking removal of an attribute, which
192 breaks assumption of performance measurement, in the future.
192 breaks assumption of performance measurement, in the future.
193
193
194 This function returns the object to (1) assign a new value, and
194 This function returns the object to (1) assign a new value, and
195 (2) restore an original value to the attribute.
195 (2) restore an original value to the attribute.
196
196
197 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
197 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
198 abortion, and this function returns None. This is useful to
198 abortion, and this function returns None. This is useful to
199 examine an attribute, which isn't ensured in all Mercurial
199 examine an attribute, which isn't ensured in all Mercurial
200 versions.
200 versions.
201 """
201 """
202 if not util.safehasattr(obj, name):
202 if not util.safehasattr(obj, name):
203 if ignoremissing:
203 if ignoremissing:
204 return None
204 return None
205 raise error.Abort(("missing attribute %s of %s might break assumption"
205 raise error.Abort(("missing attribute %s of %s might break assumption"
206 " of performance measurement") % (name, obj))
206 " of performance measurement") % (name, obj))
207
207
208 origvalue = getattr(obj, name)
208 origvalue = getattr(obj, name)
209 class attrutil(object):
209 class attrutil(object):
210 def set(self, newvalue):
210 def set(self, newvalue):
211 setattr(obj, name, newvalue)
211 setattr(obj, name, newvalue)
212 def restore(self):
212 def restore(self):
213 setattr(obj, name, origvalue)
213 setattr(obj, name, origvalue)
214
214
215 return attrutil()
215 return attrutil()
216
216
217 # utilities to examine each internal API changes
218
219 def getbranchmapsubsettable():
220 # for "historical portability":
221 # subsettable is defined in:
222 # - branchmap since 2.9 (or 175c6fd8cacc)
223 # - repoview since 2.5 (or 59a9f18d4587)
224 for mod in (branchmap, repoview):
225 subsettable = getattr(mod, 'subsettable', None)
226 if subsettable:
227 return subsettable
228
229 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
230 # branchmap and repoview modules exist, but subsettable attribute
231 # doesn't)
232 raise error.Abort(("perfbranchmap not available with this Mercurial"),
233 hint="use 2.5 or later")
234
217 # perf commands
235 # perf commands
218
236
219 @command('perfwalk', formatteropts)
237 @command('perfwalk', formatteropts)
220 def perfwalk(ui, repo, *pats, **opts):
238 def perfwalk(ui, repo, *pats, **opts):
221 timer, fm = gettimer(ui, opts)
239 timer, fm = gettimer(ui, opts)
222 try:
240 try:
223 m = scmutil.match(repo[None], pats, {})
241 m = scmutil.match(repo[None], pats, {})
224 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
242 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
225 except Exception:
243 except Exception:
226 try:
244 try:
227 m = scmutil.match(repo[None], pats, {})
245 m = scmutil.match(repo[None], pats, {})
228 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
246 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
229 except Exception:
247 except Exception:
230 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
248 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
231 fm.end()
249 fm.end()
232
250
233 @command('perfannotate', formatteropts)
251 @command('perfannotate', formatteropts)
234 def perfannotate(ui, repo, f, **opts):
252 def perfannotate(ui, repo, f, **opts):
235 timer, fm = gettimer(ui, opts)
253 timer, fm = gettimer(ui, opts)
236 fc = repo['.'][f]
254 fc = repo['.'][f]
237 timer(lambda: len(fc.annotate(True)))
255 timer(lambda: len(fc.annotate(True)))
238 fm.end()
256 fm.end()
239
257
240 @command('perfstatus',
258 @command('perfstatus',
241 [('u', 'unknown', False,
259 [('u', 'unknown', False,
242 'ask status to look for unknown files')] + formatteropts)
260 'ask status to look for unknown files')] + formatteropts)
243 def perfstatus(ui, repo, **opts):
261 def perfstatus(ui, repo, **opts):
244 #m = match.always(repo.root, repo.getcwd())
262 #m = match.always(repo.root, repo.getcwd())
245 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
263 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
246 # False))))
264 # False))))
247 timer, fm = gettimer(ui, opts)
265 timer, fm = gettimer(ui, opts)
248 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
266 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
249 fm.end()
267 fm.end()
250
268
251 @command('perfaddremove', formatteropts)
269 @command('perfaddremove', formatteropts)
252 def perfaddremove(ui, repo, **opts):
270 def perfaddremove(ui, repo, **opts):
253 timer, fm = gettimer(ui, opts)
271 timer, fm = gettimer(ui, opts)
254 try:
272 try:
255 oldquiet = repo.ui.quiet
273 oldquiet = repo.ui.quiet
256 repo.ui.quiet = True
274 repo.ui.quiet = True
257 matcher = scmutil.match(repo[None])
275 matcher = scmutil.match(repo[None])
258 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
276 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
259 finally:
277 finally:
260 repo.ui.quiet = oldquiet
278 repo.ui.quiet = oldquiet
261 fm.end()
279 fm.end()
262
280
263 def clearcaches(cl):
281 def clearcaches(cl):
264 # behave somewhat consistently across internal API changes
282 # behave somewhat consistently across internal API changes
265 if util.safehasattr(cl, 'clearcaches'):
283 if util.safehasattr(cl, 'clearcaches'):
266 cl.clearcaches()
284 cl.clearcaches()
267 elif util.safehasattr(cl, '_nodecache'):
285 elif util.safehasattr(cl, '_nodecache'):
268 from mercurial.node import nullid, nullrev
286 from mercurial.node import nullid, nullrev
269 cl._nodecache = {nullid: nullrev}
287 cl._nodecache = {nullid: nullrev}
270 cl._nodepos = None
288 cl._nodepos = None
271
289
272 @command('perfheads', formatteropts)
290 @command('perfheads', formatteropts)
273 def perfheads(ui, repo, **opts):
291 def perfheads(ui, repo, **opts):
274 timer, fm = gettimer(ui, opts)
292 timer, fm = gettimer(ui, opts)
275 cl = repo.changelog
293 cl = repo.changelog
276 def d():
294 def d():
277 len(cl.headrevs())
295 len(cl.headrevs())
278 clearcaches(cl)
296 clearcaches(cl)
279 timer(d)
297 timer(d)
280 fm.end()
298 fm.end()
281
299
282 @command('perftags', formatteropts)
300 @command('perftags', formatteropts)
283 def perftags(ui, repo, **opts):
301 def perftags(ui, repo, **opts):
284 import mercurial.changelog
302 import mercurial.changelog
285 import mercurial.manifest
303 import mercurial.manifest
286 timer, fm = gettimer(ui, opts)
304 timer, fm = gettimer(ui, opts)
287 def t():
305 def t():
288 repo.changelog = mercurial.changelog.changelog(repo.svfs)
306 repo.changelog = mercurial.changelog.changelog(repo.svfs)
289 repo.manifest = mercurial.manifest.manifest(repo.svfs)
307 repo.manifest = mercurial.manifest.manifest(repo.svfs)
290 repo._tags = None
308 repo._tags = None
291 return len(repo.tags())
309 return len(repo.tags())
292 timer(t)
310 timer(t)
293 fm.end()
311 fm.end()
294
312
295 @command('perfancestors', formatteropts)
313 @command('perfancestors', formatteropts)
296 def perfancestors(ui, repo, **opts):
314 def perfancestors(ui, repo, **opts):
297 timer, fm = gettimer(ui, opts)
315 timer, fm = gettimer(ui, opts)
298 heads = repo.changelog.headrevs()
316 heads = repo.changelog.headrevs()
299 def d():
317 def d():
300 for a in repo.changelog.ancestors(heads):
318 for a in repo.changelog.ancestors(heads):
301 pass
319 pass
302 timer(d)
320 timer(d)
303 fm.end()
321 fm.end()
304
322
305 @command('perfancestorset', formatteropts)
323 @command('perfancestorset', formatteropts)
306 def perfancestorset(ui, repo, revset, **opts):
324 def perfancestorset(ui, repo, revset, **opts):
307 timer, fm = gettimer(ui, opts)
325 timer, fm = gettimer(ui, opts)
308 revs = repo.revs(revset)
326 revs = repo.revs(revset)
309 heads = repo.changelog.headrevs()
327 heads = repo.changelog.headrevs()
310 def d():
328 def d():
311 s = repo.changelog.ancestors(heads)
329 s = repo.changelog.ancestors(heads)
312 for rev in revs:
330 for rev in revs:
313 rev in s
331 rev in s
314 timer(d)
332 timer(d)
315 fm.end()
333 fm.end()
316
334
317 @command('perfchangegroupchangelog', formatteropts +
335 @command('perfchangegroupchangelog', formatteropts +
318 [('', 'version', '02', 'changegroup version'),
336 [('', 'version', '02', 'changegroup version'),
319 ('r', 'rev', '', 'revisions to add to changegroup')])
337 ('r', 'rev', '', 'revisions to add to changegroup')])
320 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
338 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
321 """Benchmark producing a changelog group for a changegroup.
339 """Benchmark producing a changelog group for a changegroup.
322
340
323 This measures the time spent processing the changelog during a
341 This measures the time spent processing the changelog during a
324 bundle operation. This occurs during `hg bundle` and on a server
342 bundle operation. This occurs during `hg bundle` and on a server
325 processing a `getbundle` wire protocol request (handles clones
343 processing a `getbundle` wire protocol request (handles clones
326 and pull requests).
344 and pull requests).
327
345
328 By default, all revisions are added to the changegroup.
346 By default, all revisions are added to the changegroup.
329 """
347 """
330 cl = repo.changelog
348 cl = repo.changelog
331 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
349 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
332 bundler = changegroup.getbundler(version, repo)
350 bundler = changegroup.getbundler(version, repo)
333
351
334 def lookup(node):
352 def lookup(node):
335 # The real bundler reads the revision in order to access the
353 # The real bundler reads the revision in order to access the
336 # manifest node and files list. Do that here.
354 # manifest node and files list. Do that here.
337 cl.read(node)
355 cl.read(node)
338 return node
356 return node
339
357
340 def d():
358 def d():
341 for chunk in bundler.group(revs, cl, lookup):
359 for chunk in bundler.group(revs, cl, lookup):
342 pass
360 pass
343
361
344 timer, fm = gettimer(ui, opts)
362 timer, fm = gettimer(ui, opts)
345 timer(d)
363 timer(d)
346 fm.end()
364 fm.end()
347
365
348 @command('perfdirs', formatteropts)
366 @command('perfdirs', formatteropts)
349 def perfdirs(ui, repo, **opts):
367 def perfdirs(ui, repo, **opts):
350 timer, fm = gettimer(ui, opts)
368 timer, fm = gettimer(ui, opts)
351 dirstate = repo.dirstate
369 dirstate = repo.dirstate
352 'a' in dirstate
370 'a' in dirstate
353 def d():
371 def d():
354 dirstate.dirs()
372 dirstate.dirs()
355 del dirstate._dirs
373 del dirstate._dirs
356 timer(d)
374 timer(d)
357 fm.end()
375 fm.end()
358
376
359 @command('perfdirstate', formatteropts)
377 @command('perfdirstate', formatteropts)
360 def perfdirstate(ui, repo, **opts):
378 def perfdirstate(ui, repo, **opts):
361 timer, fm = gettimer(ui, opts)
379 timer, fm = gettimer(ui, opts)
362 "a" in repo.dirstate
380 "a" in repo.dirstate
363 def d():
381 def d():
364 repo.dirstate.invalidate()
382 repo.dirstate.invalidate()
365 "a" in repo.dirstate
383 "a" in repo.dirstate
366 timer(d)
384 timer(d)
367 fm.end()
385 fm.end()
368
386
369 @command('perfdirstatedirs', formatteropts)
387 @command('perfdirstatedirs', formatteropts)
370 def perfdirstatedirs(ui, repo, **opts):
388 def perfdirstatedirs(ui, repo, **opts):
371 timer, fm = gettimer(ui, opts)
389 timer, fm = gettimer(ui, opts)
372 "a" in repo.dirstate
390 "a" in repo.dirstate
373 def d():
391 def d():
374 "a" in repo.dirstate._dirs
392 "a" in repo.dirstate._dirs
375 del repo.dirstate._dirs
393 del repo.dirstate._dirs
376 timer(d)
394 timer(d)
377 fm.end()
395 fm.end()
378
396
379 @command('perfdirstatefoldmap', formatteropts)
397 @command('perfdirstatefoldmap', formatteropts)
380 def perfdirstatefoldmap(ui, repo, **opts):
398 def perfdirstatefoldmap(ui, repo, **opts):
381 timer, fm = gettimer(ui, opts)
399 timer, fm = gettimer(ui, opts)
382 dirstate = repo.dirstate
400 dirstate = repo.dirstate
383 'a' in dirstate
401 'a' in dirstate
384 def d():
402 def d():
385 dirstate._filefoldmap.get('a')
403 dirstate._filefoldmap.get('a')
386 del dirstate._filefoldmap
404 del dirstate._filefoldmap
387 timer(d)
405 timer(d)
388 fm.end()
406 fm.end()
389
407
390 @command('perfdirfoldmap', formatteropts)
408 @command('perfdirfoldmap', formatteropts)
391 def perfdirfoldmap(ui, repo, **opts):
409 def perfdirfoldmap(ui, repo, **opts):
392 timer, fm = gettimer(ui, opts)
410 timer, fm = gettimer(ui, opts)
393 dirstate = repo.dirstate
411 dirstate = repo.dirstate
394 'a' in dirstate
412 'a' in dirstate
395 def d():
413 def d():
396 dirstate._dirfoldmap.get('a')
414 dirstate._dirfoldmap.get('a')
397 del dirstate._dirfoldmap
415 del dirstate._dirfoldmap
398 del dirstate._dirs
416 del dirstate._dirs
399 timer(d)
417 timer(d)
400 fm.end()
418 fm.end()
401
419
402 @command('perfdirstatewrite', formatteropts)
420 @command('perfdirstatewrite', formatteropts)
403 def perfdirstatewrite(ui, repo, **opts):
421 def perfdirstatewrite(ui, repo, **opts):
404 timer, fm = gettimer(ui, opts)
422 timer, fm = gettimer(ui, opts)
405 ds = repo.dirstate
423 ds = repo.dirstate
406 "a" in ds
424 "a" in ds
407 def d():
425 def d():
408 ds._dirty = True
426 ds._dirty = True
409 ds.write(repo.currenttransaction())
427 ds.write(repo.currenttransaction())
410 timer(d)
428 timer(d)
411 fm.end()
429 fm.end()
412
430
413 @command('perfmergecalculate',
431 @command('perfmergecalculate',
414 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
432 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
415 def perfmergecalculate(ui, repo, rev, **opts):
433 def perfmergecalculate(ui, repo, rev, **opts):
416 timer, fm = gettimer(ui, opts)
434 timer, fm = gettimer(ui, opts)
417 wctx = repo[None]
435 wctx = repo[None]
418 rctx = scmutil.revsingle(repo, rev, rev)
436 rctx = scmutil.revsingle(repo, rev, rev)
419 ancestor = wctx.ancestor(rctx)
437 ancestor = wctx.ancestor(rctx)
420 # we don't want working dir files to be stat'd in the benchmark, so prime
438 # we don't want working dir files to be stat'd in the benchmark, so prime
421 # that cache
439 # that cache
422 wctx.dirty()
440 wctx.dirty()
423 def d():
441 def d():
424 # acceptremote is True because we don't want prompts in the middle of
442 # acceptremote is True because we don't want prompts in the middle of
425 # our benchmark
443 # our benchmark
426 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
444 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
427 acceptremote=True, followcopies=True)
445 acceptremote=True, followcopies=True)
428 timer(d)
446 timer(d)
429 fm.end()
447 fm.end()
430
448
431 @command('perfpathcopies', [], "REV REV")
449 @command('perfpathcopies', [], "REV REV")
432 def perfpathcopies(ui, repo, rev1, rev2, **opts):
450 def perfpathcopies(ui, repo, rev1, rev2, **opts):
433 timer, fm = gettimer(ui, opts)
451 timer, fm = gettimer(ui, opts)
434 ctx1 = scmutil.revsingle(repo, rev1, rev1)
452 ctx1 = scmutil.revsingle(repo, rev1, rev1)
435 ctx2 = scmutil.revsingle(repo, rev2, rev2)
453 ctx2 = scmutil.revsingle(repo, rev2, rev2)
436 def d():
454 def d():
437 copies.pathcopies(ctx1, ctx2)
455 copies.pathcopies(ctx1, ctx2)
438 timer(d)
456 timer(d)
439 fm.end()
457 fm.end()
440
458
441 @command('perfmanifest', [], 'REV')
459 @command('perfmanifest', [], 'REV')
442 def perfmanifest(ui, repo, rev, **opts):
460 def perfmanifest(ui, repo, rev, **opts):
443 timer, fm = gettimer(ui, opts)
461 timer, fm = gettimer(ui, opts)
444 ctx = scmutil.revsingle(repo, rev, rev)
462 ctx = scmutil.revsingle(repo, rev, rev)
445 t = ctx.manifestnode()
463 t = ctx.manifestnode()
446 def d():
464 def d():
447 repo.manifest.clearcaches()
465 repo.manifest.clearcaches()
448 repo.manifest.read(t)
466 repo.manifest.read(t)
449 timer(d)
467 timer(d)
450 fm.end()
468 fm.end()
451
469
452 @command('perfchangeset', formatteropts)
470 @command('perfchangeset', formatteropts)
453 def perfchangeset(ui, repo, rev, **opts):
471 def perfchangeset(ui, repo, rev, **opts):
454 timer, fm = gettimer(ui, opts)
472 timer, fm = gettimer(ui, opts)
455 n = repo[rev].node()
473 n = repo[rev].node()
456 def d():
474 def d():
457 repo.changelog.read(n)
475 repo.changelog.read(n)
458 #repo.changelog._cache = None
476 #repo.changelog._cache = None
459 timer(d)
477 timer(d)
460 fm.end()
478 fm.end()
461
479
462 @command('perfindex', formatteropts)
480 @command('perfindex', formatteropts)
463 def perfindex(ui, repo, **opts):
481 def perfindex(ui, repo, **opts):
464 import mercurial.revlog
482 import mercurial.revlog
465 timer, fm = gettimer(ui, opts)
483 timer, fm = gettimer(ui, opts)
466 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
484 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
467 n = repo["tip"].node()
485 n = repo["tip"].node()
468 def d():
486 def d():
469 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
487 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
470 cl.rev(n)
488 cl.rev(n)
471 timer(d)
489 timer(d)
472 fm.end()
490 fm.end()
473
491
474 @command('perfstartup', formatteropts)
492 @command('perfstartup', formatteropts)
475 def perfstartup(ui, repo, **opts):
493 def perfstartup(ui, repo, **opts):
476 timer, fm = gettimer(ui, opts)
494 timer, fm = gettimer(ui, opts)
477 cmd = sys.argv[0]
495 cmd = sys.argv[0]
478 def d():
496 def d():
479 if os.name != 'nt':
497 if os.name != 'nt':
480 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
498 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
481 else:
499 else:
482 os.environ['HGRCPATH'] = ''
500 os.environ['HGRCPATH'] = ''
483 os.system("%s version -q > NUL" % cmd)
501 os.system("%s version -q > NUL" % cmd)
484 timer(d)
502 timer(d)
485 fm.end()
503 fm.end()
486
504
487 @command('perfparents', formatteropts)
505 @command('perfparents', formatteropts)
488 def perfparents(ui, repo, **opts):
506 def perfparents(ui, repo, **opts):
489 timer, fm = gettimer(ui, opts)
507 timer, fm = gettimer(ui, opts)
490 # control the number of commits perfparents iterates over
508 # control the number of commits perfparents iterates over
491 # experimental config: perf.parentscount
509 # experimental config: perf.parentscount
492 count = ui.configint("perf", "parentscount", 1000)
510 count = ui.configint("perf", "parentscount", 1000)
493 if len(repo.changelog) < count:
511 if len(repo.changelog) < count:
494 raise error.Abort("repo needs %d commits for this test" % count)
512 raise error.Abort("repo needs %d commits for this test" % count)
495 repo = repo.unfiltered()
513 repo = repo.unfiltered()
496 nl = [repo.changelog.node(i) for i in xrange(count)]
514 nl = [repo.changelog.node(i) for i in xrange(count)]
497 def d():
515 def d():
498 for n in nl:
516 for n in nl:
499 repo.changelog.parents(n)
517 repo.changelog.parents(n)
500 timer(d)
518 timer(d)
501 fm.end()
519 fm.end()
502
520
503 @command('perfctxfiles', formatteropts)
521 @command('perfctxfiles', formatteropts)
504 def perfctxfiles(ui, repo, x, **opts):
522 def perfctxfiles(ui, repo, x, **opts):
505 x = int(x)
523 x = int(x)
506 timer, fm = gettimer(ui, opts)
524 timer, fm = gettimer(ui, opts)
507 def d():
525 def d():
508 len(repo[x].files())
526 len(repo[x].files())
509 timer(d)
527 timer(d)
510 fm.end()
528 fm.end()
511
529
512 @command('perfrawfiles', formatteropts)
530 @command('perfrawfiles', formatteropts)
513 def perfrawfiles(ui, repo, x, **opts):
531 def perfrawfiles(ui, repo, x, **opts):
514 x = int(x)
532 x = int(x)
515 timer, fm = gettimer(ui, opts)
533 timer, fm = gettimer(ui, opts)
516 cl = repo.changelog
534 cl = repo.changelog
517 def d():
535 def d():
518 len(cl.read(x)[3])
536 len(cl.read(x)[3])
519 timer(d)
537 timer(d)
520 fm.end()
538 fm.end()
521
539
522 @command('perflookup', formatteropts)
540 @command('perflookup', formatteropts)
523 def perflookup(ui, repo, rev, **opts):
541 def perflookup(ui, repo, rev, **opts):
524 timer, fm = gettimer(ui, opts)
542 timer, fm = gettimer(ui, opts)
525 timer(lambda: len(repo.lookup(rev)))
543 timer(lambda: len(repo.lookup(rev)))
526 fm.end()
544 fm.end()
527
545
528 @command('perfrevrange', formatteropts)
546 @command('perfrevrange', formatteropts)
529 def perfrevrange(ui, repo, *specs, **opts):
547 def perfrevrange(ui, repo, *specs, **opts):
530 timer, fm = gettimer(ui, opts)
548 timer, fm = gettimer(ui, opts)
531 revrange = scmutil.revrange
549 revrange = scmutil.revrange
532 timer(lambda: len(revrange(repo, specs)))
550 timer(lambda: len(revrange(repo, specs)))
533 fm.end()
551 fm.end()
534
552
535 @command('perfnodelookup', formatteropts)
553 @command('perfnodelookup', formatteropts)
536 def perfnodelookup(ui, repo, rev, **opts):
554 def perfnodelookup(ui, repo, rev, **opts):
537 timer, fm = gettimer(ui, opts)
555 timer, fm = gettimer(ui, opts)
538 import mercurial.revlog
556 import mercurial.revlog
539 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
557 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
540 n = repo[rev].node()
558 n = repo[rev].node()
541 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
559 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
542 def d():
560 def d():
543 cl.rev(n)
561 cl.rev(n)
544 clearcaches(cl)
562 clearcaches(cl)
545 timer(d)
563 timer(d)
546 fm.end()
564 fm.end()
547
565
548 @command('perflog',
566 @command('perflog',
549 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
567 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
550 def perflog(ui, repo, rev=None, **opts):
568 def perflog(ui, repo, rev=None, **opts):
551 if rev is None:
569 if rev is None:
552 rev=[]
570 rev=[]
553 timer, fm = gettimer(ui, opts)
571 timer, fm = gettimer(ui, opts)
554 ui.pushbuffer()
572 ui.pushbuffer()
555 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
573 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
556 copies=opts.get('rename')))
574 copies=opts.get('rename')))
557 ui.popbuffer()
575 ui.popbuffer()
558 fm.end()
576 fm.end()
559
577
560 @command('perfmoonwalk', formatteropts)
578 @command('perfmoonwalk', formatteropts)
561 def perfmoonwalk(ui, repo, **opts):
579 def perfmoonwalk(ui, repo, **opts):
562 """benchmark walking the changelog backwards
580 """benchmark walking the changelog backwards
563
581
564 This also loads the changelog data for each revision in the changelog.
582 This also loads the changelog data for each revision in the changelog.
565 """
583 """
566 timer, fm = gettimer(ui, opts)
584 timer, fm = gettimer(ui, opts)
567 def moonwalk():
585 def moonwalk():
568 for i in xrange(len(repo), -1, -1):
586 for i in xrange(len(repo), -1, -1):
569 ctx = repo[i]
587 ctx = repo[i]
570 ctx.branch() # read changelog data (in addition to the index)
588 ctx.branch() # read changelog data (in addition to the index)
571 timer(moonwalk)
589 timer(moonwalk)
572 fm.end()
590 fm.end()
573
591
574 @command('perftemplating', formatteropts)
592 @command('perftemplating', formatteropts)
575 def perftemplating(ui, repo, rev=None, **opts):
593 def perftemplating(ui, repo, rev=None, **opts):
576 if rev is None:
594 if rev is None:
577 rev=[]
595 rev=[]
578 timer, fm = gettimer(ui, opts)
596 timer, fm = gettimer(ui, opts)
579 ui.pushbuffer()
597 ui.pushbuffer()
580 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
598 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
581 template='{date|shortdate} [{rev}:{node|short}]'
599 template='{date|shortdate} [{rev}:{node|short}]'
582 ' {author|person}: {desc|firstline}\n'))
600 ' {author|person}: {desc|firstline}\n'))
583 ui.popbuffer()
601 ui.popbuffer()
584 fm.end()
602 fm.end()
585
603
586 @command('perfcca', formatteropts)
604 @command('perfcca', formatteropts)
587 def perfcca(ui, repo, **opts):
605 def perfcca(ui, repo, **opts):
588 timer, fm = gettimer(ui, opts)
606 timer, fm = gettimer(ui, opts)
589 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
607 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
590 fm.end()
608 fm.end()
591
609
592 @command('perffncacheload', formatteropts)
610 @command('perffncacheload', formatteropts)
593 def perffncacheload(ui, repo, **opts):
611 def perffncacheload(ui, repo, **opts):
594 timer, fm = gettimer(ui, opts)
612 timer, fm = gettimer(ui, opts)
595 s = repo.store
613 s = repo.store
596 def d():
614 def d():
597 s.fncache._load()
615 s.fncache._load()
598 timer(d)
616 timer(d)
599 fm.end()
617 fm.end()
600
618
601 @command('perffncachewrite', formatteropts)
619 @command('perffncachewrite', formatteropts)
602 def perffncachewrite(ui, repo, **opts):
620 def perffncachewrite(ui, repo, **opts):
603 timer, fm = gettimer(ui, opts)
621 timer, fm = gettimer(ui, opts)
604 s = repo.store
622 s = repo.store
605 s.fncache._load()
623 s.fncache._load()
606 lock = repo.lock()
624 lock = repo.lock()
607 tr = repo.transaction('perffncachewrite')
625 tr = repo.transaction('perffncachewrite')
608 def d():
626 def d():
609 s.fncache._dirty = True
627 s.fncache._dirty = True
610 s.fncache.write(tr)
628 s.fncache.write(tr)
611 timer(d)
629 timer(d)
612 tr.close()
630 tr.close()
613 lock.release()
631 lock.release()
614 fm.end()
632 fm.end()
615
633
616 @command('perffncacheencode', formatteropts)
634 @command('perffncacheencode', formatteropts)
617 def perffncacheencode(ui, repo, **opts):
635 def perffncacheencode(ui, repo, **opts):
618 timer, fm = gettimer(ui, opts)
636 timer, fm = gettimer(ui, opts)
619 s = repo.store
637 s = repo.store
620 s.fncache._load()
638 s.fncache._load()
621 def d():
639 def d():
622 for p in s.fncache.entries:
640 for p in s.fncache.entries:
623 s.encode(p)
641 s.encode(p)
624 timer(d)
642 timer(d)
625 fm.end()
643 fm.end()
626
644
627 @command('perfdiffwd', formatteropts)
645 @command('perfdiffwd', formatteropts)
628 def perfdiffwd(ui, repo, **opts):
646 def perfdiffwd(ui, repo, **opts):
629 """Profile diff of working directory changes"""
647 """Profile diff of working directory changes"""
630 timer, fm = gettimer(ui, opts)
648 timer, fm = gettimer(ui, opts)
631 options = {
649 options = {
632 'w': 'ignore_all_space',
650 'w': 'ignore_all_space',
633 'b': 'ignore_space_change',
651 'b': 'ignore_space_change',
634 'B': 'ignore_blank_lines',
652 'B': 'ignore_blank_lines',
635 }
653 }
636
654
637 for diffopt in ('', 'w', 'b', 'B', 'wB'):
655 for diffopt in ('', 'w', 'b', 'B', 'wB'):
638 opts = dict((options[c], '1') for c in diffopt)
656 opts = dict((options[c], '1') for c in diffopt)
639 def d():
657 def d():
640 ui.pushbuffer()
658 ui.pushbuffer()
641 commands.diff(ui, repo, **opts)
659 commands.diff(ui, repo, **opts)
642 ui.popbuffer()
660 ui.popbuffer()
643 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
661 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
644 timer(d, title)
662 timer(d, title)
645 fm.end()
663 fm.end()
646
664
647 @command('perfrevlog', revlogopts + formatteropts +
665 @command('perfrevlog', revlogopts + formatteropts +
648 [('d', 'dist', 100, 'distance between the revisions'),
666 [('d', 'dist', 100, 'distance between the revisions'),
649 ('s', 'startrev', 0, 'revision to start reading at'),
667 ('s', 'startrev', 0, 'revision to start reading at'),
650 ('', 'reverse', False, 'read in reverse')],
668 ('', 'reverse', False, 'read in reverse')],
651 '-c|-m|FILE')
669 '-c|-m|FILE')
652 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
670 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
653 """Benchmark reading a series of revisions from a revlog.
671 """Benchmark reading a series of revisions from a revlog.
654
672
655 By default, we read every ``-d/--dist`` revision from 0 to tip of
673 By default, we read every ``-d/--dist`` revision from 0 to tip of
656 the specified revlog.
674 the specified revlog.
657
675
658 The start revision can be defined via ``-s/--startrev``.
676 The start revision can be defined via ``-s/--startrev``.
659 """
677 """
660 timer, fm = gettimer(ui, opts)
678 timer, fm = gettimer(ui, opts)
661 _len = getlen(ui)
679 _len = getlen(ui)
662
680
663 def d():
681 def d():
664 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
682 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
665
683
666 startrev = 0
684 startrev = 0
667 endrev = _len(r)
685 endrev = _len(r)
668 dist = opts['dist']
686 dist = opts['dist']
669
687
670 if reverse:
688 if reverse:
671 startrev, endrev = endrev, startrev
689 startrev, endrev = endrev, startrev
672 dist = -1 * dist
690 dist = -1 * dist
673
691
674 for x in xrange(startrev, endrev, dist):
692 for x in xrange(startrev, endrev, dist):
675 r.revision(r.node(x))
693 r.revision(r.node(x))
676
694
677 timer(d)
695 timer(d)
678 fm.end()
696 fm.end()
679
697
680 @command('perfrevlogrevision', revlogopts + formatteropts +
698 @command('perfrevlogrevision', revlogopts + formatteropts +
681 [('', 'cache', False, 'use caches instead of clearing')],
699 [('', 'cache', False, 'use caches instead of clearing')],
682 '-c|-m|FILE REV')
700 '-c|-m|FILE REV')
683 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
701 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
684 """Benchmark obtaining a revlog revision.
702 """Benchmark obtaining a revlog revision.
685
703
686 Obtaining a revlog revision consists of roughly the following steps:
704 Obtaining a revlog revision consists of roughly the following steps:
687
705
688 1. Compute the delta chain
706 1. Compute the delta chain
689 2. Obtain the raw chunks for that delta chain
707 2. Obtain the raw chunks for that delta chain
690 3. Decompress each raw chunk
708 3. Decompress each raw chunk
691 4. Apply binary patches to obtain fulltext
709 4. Apply binary patches to obtain fulltext
692 5. Verify hash of fulltext
710 5. Verify hash of fulltext
693
711
694 This command measures the time spent in each of these phases.
712 This command measures the time spent in each of these phases.
695 """
713 """
696 if opts.get('changelog') or opts.get('manifest'):
714 if opts.get('changelog') or opts.get('manifest'):
697 file_, rev = None, file_
715 file_, rev = None, file_
698 elif rev is None:
716 elif rev is None:
699 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
717 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
700
718
701 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
719 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
702 node = r.lookup(rev)
720 node = r.lookup(rev)
703 rev = r.rev(node)
721 rev = r.rev(node)
704
722
705 def dodeltachain(rev):
723 def dodeltachain(rev):
706 if not cache:
724 if not cache:
707 r.clearcaches()
725 r.clearcaches()
708 r._deltachain(rev)
726 r._deltachain(rev)
709
727
710 def doread(chain):
728 def doread(chain):
711 if not cache:
729 if not cache:
712 r.clearcaches()
730 r.clearcaches()
713 r._chunkraw(chain[0], chain[-1])
731 r._chunkraw(chain[0], chain[-1])
714
732
715 def dodecompress(data, chain):
733 def dodecompress(data, chain):
716 if not cache:
734 if not cache:
717 r.clearcaches()
735 r.clearcaches()
718
736
719 start = r.start
737 start = r.start
720 length = r.length
738 length = r.length
721 inline = r._inline
739 inline = r._inline
722 iosize = r._io.size
740 iosize = r._io.size
723 buffer = util.buffer
741 buffer = util.buffer
724 offset = start(chain[0])
742 offset = start(chain[0])
725
743
726 for rev in chain:
744 for rev in chain:
727 chunkstart = start(rev)
745 chunkstart = start(rev)
728 if inline:
746 if inline:
729 chunkstart += (rev + 1) * iosize
747 chunkstart += (rev + 1) * iosize
730 chunklength = length(rev)
748 chunklength = length(rev)
731 b = buffer(data, chunkstart - offset, chunklength)
749 b = buffer(data, chunkstart - offset, chunklength)
732 revlog.decompress(b)
750 revlog.decompress(b)
733
751
734 def dopatch(text, bins):
752 def dopatch(text, bins):
735 if not cache:
753 if not cache:
736 r.clearcaches()
754 r.clearcaches()
737 mdiff.patches(text, bins)
755 mdiff.patches(text, bins)
738
756
739 def dohash(text):
757 def dohash(text):
740 if not cache:
758 if not cache:
741 r.clearcaches()
759 r.clearcaches()
742 r._checkhash(text, node, rev)
760 r._checkhash(text, node, rev)
743
761
744 def dorevision():
762 def dorevision():
745 if not cache:
763 if not cache:
746 r.clearcaches()
764 r.clearcaches()
747 r.revision(node)
765 r.revision(node)
748
766
749 chain = r._deltachain(rev)[0]
767 chain = r._deltachain(rev)[0]
750 data = r._chunkraw(chain[0], chain[-1])[1]
768 data = r._chunkraw(chain[0], chain[-1])[1]
751 bins = r._chunks(chain)
769 bins = r._chunks(chain)
752 text = str(bins[0])
770 text = str(bins[0])
753 bins = bins[1:]
771 bins = bins[1:]
754 text = mdiff.patches(text, bins)
772 text = mdiff.patches(text, bins)
755
773
756 benches = [
774 benches = [
757 (lambda: dorevision(), 'full'),
775 (lambda: dorevision(), 'full'),
758 (lambda: dodeltachain(rev), 'deltachain'),
776 (lambda: dodeltachain(rev), 'deltachain'),
759 (lambda: doread(chain), 'read'),
777 (lambda: doread(chain), 'read'),
760 (lambda: dodecompress(data, chain), 'decompress'),
778 (lambda: dodecompress(data, chain), 'decompress'),
761 (lambda: dopatch(text, bins), 'patch'),
779 (lambda: dopatch(text, bins), 'patch'),
762 (lambda: dohash(text), 'hash'),
780 (lambda: dohash(text), 'hash'),
763 ]
781 ]
764
782
765 for fn, title in benches:
783 for fn, title in benches:
766 timer, fm = gettimer(ui, opts)
784 timer, fm = gettimer(ui, opts)
767 timer(fn, title=title)
785 timer(fn, title=title)
768 fm.end()
786 fm.end()
769
787
770 @command('perfrevset',
788 @command('perfrevset',
771 [('C', 'clear', False, 'clear volatile cache between each call.'),
789 [('C', 'clear', False, 'clear volatile cache between each call.'),
772 ('', 'contexts', False, 'obtain changectx for each revision')]
790 ('', 'contexts', False, 'obtain changectx for each revision')]
773 + formatteropts, "REVSET")
791 + formatteropts, "REVSET")
774 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
792 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
775 """benchmark the execution time of a revset
793 """benchmark the execution time of a revset
776
794
777 Use the --clean option if need to evaluate the impact of build volatile
795 Use the --clean option if need to evaluate the impact of build volatile
778 revisions set cache on the revset execution. Volatile cache hold filtered
796 revisions set cache on the revset execution. Volatile cache hold filtered
779 and obsolete related cache."""
797 and obsolete related cache."""
780 timer, fm = gettimer(ui, opts)
798 timer, fm = gettimer(ui, opts)
781 def d():
799 def d():
782 if clear:
800 if clear:
783 repo.invalidatevolatilesets()
801 repo.invalidatevolatilesets()
784 if contexts:
802 if contexts:
785 for ctx in repo.set(expr): pass
803 for ctx in repo.set(expr): pass
786 else:
804 else:
787 for r in repo.revs(expr): pass
805 for r in repo.revs(expr): pass
788 timer(d)
806 timer(d)
789 fm.end()
807 fm.end()
790
808
791 @command('perfvolatilesets', formatteropts)
809 @command('perfvolatilesets', formatteropts)
792 def perfvolatilesets(ui, repo, *names, **opts):
810 def perfvolatilesets(ui, repo, *names, **opts):
793 """benchmark the computation of various volatile set
811 """benchmark the computation of various volatile set
794
812
795 Volatile set computes element related to filtering and obsolescence."""
813 Volatile set computes element related to filtering and obsolescence."""
796 timer, fm = gettimer(ui, opts)
814 timer, fm = gettimer(ui, opts)
797 repo = repo.unfiltered()
815 repo = repo.unfiltered()
798
816
799 def getobs(name):
817 def getobs(name):
800 def d():
818 def d():
801 repo.invalidatevolatilesets()
819 repo.invalidatevolatilesets()
802 obsolete.getrevs(repo, name)
820 obsolete.getrevs(repo, name)
803 return d
821 return d
804
822
805 allobs = sorted(obsolete.cachefuncs)
823 allobs = sorted(obsolete.cachefuncs)
806 if names:
824 if names:
807 allobs = [n for n in allobs if n in names]
825 allobs = [n for n in allobs if n in names]
808
826
809 for name in allobs:
827 for name in allobs:
810 timer(getobs(name), title=name)
828 timer(getobs(name), title=name)
811
829
812 def getfiltered(name):
830 def getfiltered(name):
813 def d():
831 def d():
814 repo.invalidatevolatilesets()
832 repo.invalidatevolatilesets()
815 repoview.filterrevs(repo, name)
833 repoview.filterrevs(repo, name)
816 return d
834 return d
817
835
818 allfilter = sorted(repoview.filtertable)
836 allfilter = sorted(repoview.filtertable)
819 if names:
837 if names:
820 allfilter = [n for n in allfilter if n in names]
838 allfilter = [n for n in allfilter if n in names]
821
839
822 for name in allfilter:
840 for name in allfilter:
823 timer(getfiltered(name), title=name)
841 timer(getfiltered(name), title=name)
824 fm.end()
842 fm.end()
825
843
826 @command('perfbranchmap',
844 @command('perfbranchmap',
827 [('f', 'full', False,
845 [('f', 'full', False,
828 'Includes build time of subset'),
846 'Includes build time of subset'),
829 ] + formatteropts)
847 ] + formatteropts)
830 def perfbranchmap(ui, repo, full=False, **opts):
848 def perfbranchmap(ui, repo, full=False, **opts):
831 """benchmark the update of a branchmap
849 """benchmark the update of a branchmap
832
850
833 This benchmarks the full repo.branchmap() call with read and write disabled
851 This benchmarks the full repo.branchmap() call with read and write disabled
834 """
852 """
835 timer, fm = gettimer(ui, opts)
853 timer, fm = gettimer(ui, opts)
836 def getbranchmap(filtername):
854 def getbranchmap(filtername):
837 """generate a benchmark function for the filtername"""
855 """generate a benchmark function for the filtername"""
838 if filtername is None:
856 if filtername is None:
839 view = repo
857 view = repo
840 else:
858 else:
841 view = repo.filtered(filtername)
859 view = repo.filtered(filtername)
842 def d():
860 def d():
843 if full:
861 if full:
844 view._branchcaches.clear()
862 view._branchcaches.clear()
845 else:
863 else:
846 view._branchcaches.pop(filtername, None)
864 view._branchcaches.pop(filtername, None)
847 view.branchmap()
865 view.branchmap()
848 return d
866 return d
849 # add filter in smaller subset to bigger subset
867 # add filter in smaller subset to bigger subset
850 possiblefilters = set(repoview.filtertable)
868 possiblefilters = set(repoview.filtertable)
869 subsettable = getbranchmapsubsettable()
851 allfilters = []
870 allfilters = []
852 while possiblefilters:
871 while possiblefilters:
853 for name in possiblefilters:
872 for name in possiblefilters:
854 subset = branchmap.subsettable.get(name)
873 subset = subsettable.get(name)
855 if subset not in possiblefilters:
874 if subset not in possiblefilters:
856 break
875 break
857 else:
876 else:
858 assert False, 'subset cycle %s!' % possiblefilters
877 assert False, 'subset cycle %s!' % possiblefilters
859 allfilters.append(name)
878 allfilters.append(name)
860 possiblefilters.remove(name)
879 possiblefilters.remove(name)
861
880
862 # warm the cache
881 # warm the cache
863 if not full:
882 if not full:
864 for name in allfilters:
883 for name in allfilters:
865 repo.filtered(name).branchmap()
884 repo.filtered(name).branchmap()
866 # add unfiltered
885 # add unfiltered
867 allfilters.append(None)
886 allfilters.append(None)
868 oldread = branchmap.read
887 oldread = branchmap.read
869 oldwrite = branchmap.branchcache.write
888 oldwrite = branchmap.branchcache.write
870 try:
889 try:
871 branchmap.read = lambda repo: None
890 branchmap.read = lambda repo: None
872 branchmap.write = lambda repo: None
891 branchmap.write = lambda repo: None
873 for name in allfilters:
892 for name in allfilters:
874 timer(getbranchmap(name), title=str(name))
893 timer(getbranchmap(name), title=str(name))
875 finally:
894 finally:
876 branchmap.read = oldread
895 branchmap.read = oldread
877 branchmap.branchcache.write = oldwrite
896 branchmap.branchcache.write = oldwrite
878 fm.end()
897 fm.end()
879
898
880 @command('perfloadmarkers')
899 @command('perfloadmarkers')
881 def perfloadmarkers(ui, repo):
900 def perfloadmarkers(ui, repo):
882 """benchmark the time to parse the on-disk markers for a repo
901 """benchmark the time to parse the on-disk markers for a repo
883
902
884 Result is the number of markers in the repo."""
903 Result is the number of markers in the repo."""
885 timer, fm = gettimer(ui)
904 timer, fm = gettimer(ui)
886 timer(lambda: len(obsolete.obsstore(repo.svfs)))
905 timer(lambda: len(obsolete.obsstore(repo.svfs)))
887 fm.end()
906 fm.end()
888
907
889 @command('perflrucachedict', formatteropts +
908 @command('perflrucachedict', formatteropts +
890 [('', 'size', 4, 'size of cache'),
909 [('', 'size', 4, 'size of cache'),
891 ('', 'gets', 10000, 'number of key lookups'),
910 ('', 'gets', 10000, 'number of key lookups'),
892 ('', 'sets', 10000, 'number of key sets'),
911 ('', 'sets', 10000, 'number of key sets'),
893 ('', 'mixed', 10000, 'number of mixed mode operations'),
912 ('', 'mixed', 10000, 'number of mixed mode operations'),
894 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
913 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
895 norepo=True)
914 norepo=True)
896 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
915 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
897 mixedgetfreq=50, **opts):
916 mixedgetfreq=50, **opts):
898 def doinit():
917 def doinit():
899 for i in xrange(10000):
918 for i in xrange(10000):
900 util.lrucachedict(size)
919 util.lrucachedict(size)
901
920
902 values = []
921 values = []
903 for i in xrange(size):
922 for i in xrange(size):
904 values.append(random.randint(0, sys.maxint))
923 values.append(random.randint(0, sys.maxint))
905
924
906 # Get mode fills the cache and tests raw lookup performance with no
925 # Get mode fills the cache and tests raw lookup performance with no
907 # eviction.
926 # eviction.
908 getseq = []
927 getseq = []
909 for i in xrange(gets):
928 for i in xrange(gets):
910 getseq.append(random.choice(values))
929 getseq.append(random.choice(values))
911
930
912 def dogets():
931 def dogets():
913 d = util.lrucachedict(size)
932 d = util.lrucachedict(size)
914 for v in values:
933 for v in values:
915 d[v] = v
934 d[v] = v
916 for key in getseq:
935 for key in getseq:
917 value = d[key]
936 value = d[key]
918 value # silence pyflakes warning
937 value # silence pyflakes warning
919
938
920 # Set mode tests insertion speed with cache eviction.
939 # Set mode tests insertion speed with cache eviction.
921 setseq = []
940 setseq = []
922 for i in xrange(sets):
941 for i in xrange(sets):
923 setseq.append(random.randint(0, sys.maxint))
942 setseq.append(random.randint(0, sys.maxint))
924
943
925 def dosets():
944 def dosets():
926 d = util.lrucachedict(size)
945 d = util.lrucachedict(size)
927 for v in setseq:
946 for v in setseq:
928 d[v] = v
947 d[v] = v
929
948
930 # Mixed mode randomly performs gets and sets with eviction.
949 # Mixed mode randomly performs gets and sets with eviction.
931 mixedops = []
950 mixedops = []
932 for i in xrange(mixed):
951 for i in xrange(mixed):
933 r = random.randint(0, 100)
952 r = random.randint(0, 100)
934 if r < mixedgetfreq:
953 if r < mixedgetfreq:
935 op = 0
954 op = 0
936 else:
955 else:
937 op = 1
956 op = 1
938
957
939 mixedops.append((op, random.randint(0, size * 2)))
958 mixedops.append((op, random.randint(0, size * 2)))
940
959
941 def domixed():
960 def domixed():
942 d = util.lrucachedict(size)
961 d = util.lrucachedict(size)
943
962
944 for op, v in mixedops:
963 for op, v in mixedops:
945 if op == 0:
964 if op == 0:
946 try:
965 try:
947 d[v]
966 d[v]
948 except KeyError:
967 except KeyError:
949 pass
968 pass
950 else:
969 else:
951 d[v] = v
970 d[v] = v
952
971
953 benches = [
972 benches = [
954 (doinit, 'init'),
973 (doinit, 'init'),
955 (dogets, 'gets'),
974 (dogets, 'gets'),
956 (dosets, 'sets'),
975 (dosets, 'sets'),
957 (domixed, 'mixed')
976 (domixed, 'mixed')
958 ]
977 ]
959
978
960 for fn, title in benches:
979 for fn, title in benches:
961 timer, fm = gettimer(ui, opts)
980 timer, fm = gettimer(ui, opts)
962 timer(fn, title=title)
981 timer(fn, title=title)
963 fm.end()
982 fm.end()
964
983
965 def uisetup(ui):
984 def uisetup(ui):
966 if (util.safehasattr(cmdutil, 'openrevlog') and
985 if (util.safehasattr(cmdutil, 'openrevlog') and
967 not util.safehasattr(commands, 'debugrevlogopts')):
986 not util.safehasattr(commands, 'debugrevlogopts')):
968 # for "historical portability":
987 # for "historical portability":
969 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
988 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
970 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
989 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
971 # openrevlog() should cause failure, because it has been
990 # openrevlog() should cause failure, because it has been
972 # available since 3.5 (or 49c583ca48c4).
991 # available since 3.5 (or 49c583ca48c4).
973 def openrevlog(orig, repo, cmd, file_, opts):
992 def openrevlog(orig, repo, cmd, file_, opts):
974 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
993 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
975 raise error.Abort("This version doesn't support --dir option",
994 raise error.Abort("This version doesn't support --dir option",
976 hint="use 3.5 or later")
995 hint="use 3.5 or later")
977 return orig(repo, cmd, file_, opts)
996 return orig(repo, cmd, file_, opts)
978 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
997 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
@@ -1,73 +1,75 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-perf-code - (historical) portability checker for contrib/perf.py
3 # check-perf-code - (historical) portability checker for contrib/perf.py
4
4
5 from __future__ import absolute_import
5 from __future__ import absolute_import
6
6
7 import os
7 import os
8 import sys
8 import sys
9
9
10 # write static check patterns here
10 # write static check patterns here
11 perfpypats = [
11 perfpypats = [
12 [
12 [
13 (r'(branchmap|repoview)\.subsettable',
14 "use getbranchmapsubsettable() for early Mercurial"),
13 ],
15 ],
14 # warnings
16 # warnings
15 [
17 [
16 ]
18 ]
17 ]
19 ]
18
20
19 def modulewhitelist(names):
21 def modulewhitelist(names):
20 replacement = [('.py', ''), ('.c', ''), # trim suffix
22 replacement = [('.py', ''), ('.c', ''), # trim suffix
21 ('mercurial%s' % (os.sep), ''), # trim "mercurial/" path
23 ('mercurial%s' % (os.sep), ''), # trim "mercurial/" path
22 ]
24 ]
23 ignored = set(['__init__'])
25 ignored = set(['__init__'])
24 modules = {}
26 modules = {}
25
27
26 # convert from file name to module name, and count # of appearances
28 # convert from file name to module name, and count # of appearances
27 for name in names:
29 for name in names:
28 name = name.strip()
30 name = name.strip()
29 for old, new in replacement:
31 for old, new in replacement:
30 name = name.replace(old, new)
32 name = name.replace(old, new)
31 if name not in ignored:
33 if name not in ignored:
32 modules[name] = modules.get(name, 0) + 1
34 modules[name] = modules.get(name, 0) + 1
33
35
34 # list up module names, which appear multiple times
36 # list up module names, which appear multiple times
35 whitelist = []
37 whitelist = []
36 for name, count in modules.items():
38 for name, count in modules.items():
37 if count > 1:
39 if count > 1:
38 whitelist.append(name)
40 whitelist.append(name)
39
41
40 return whitelist
42 return whitelist
41
43
42 if __name__ == "__main__":
44 if __name__ == "__main__":
43 # in this case, it is assumed that result of "hg files" at
45 # in this case, it is assumed that result of "hg files" at
44 # multiple revisions is given via stdin
46 # multiple revisions is given via stdin
45 whitelist = modulewhitelist(sys.stdin)
47 whitelist = modulewhitelist(sys.stdin)
46 assert whitelist, "module whitelist is empty"
48 assert whitelist, "module whitelist is empty"
47
49
48 # build up module whitelist check from file names given at runtime
50 # build up module whitelist check from file names given at runtime
49 perfpypats[0].append(
51 perfpypats[0].append(
50 # this matching pattern assumes importing modules from
52 # this matching pattern assumes importing modules from
51 # "mercurial" package in the current style below, for simplicity
53 # "mercurial" package in the current style below, for simplicity
52 #
54 #
53 # from mercurial import (
55 # from mercurial import (
54 # foo,
56 # foo,
55 # bar,
57 # bar,
56 # baz
58 # baz
57 # )
59 # )
58 ((r'from mercurial import [(][a-z0-9, \n#]*\n(?! *%s,|^[ #]*\n|[)])'
60 ((r'from mercurial import [(][a-z0-9, \n#]*\n(?! *%s,|^[ #]*\n|[)])'
59 % ',| *'.join(whitelist)),
61 % ',| *'.join(whitelist)),
60 "import newer module separately in try clause for early Mercurial"
62 "import newer module separately in try clause for early Mercurial"
61 ))
63 ))
62
64
63 # import contrib/check-code.py as checkcode
65 # import contrib/check-code.py as checkcode
64 assert 'RUNTESTDIR' in os.environ, "use check-perf-code.py in *.t script"
66 assert 'RUNTESTDIR' in os.environ, "use check-perf-code.py in *.t script"
65 contribpath = os.path.join(os.environ['RUNTESTDIR'], '..', 'contrib')
67 contribpath = os.path.join(os.environ['RUNTESTDIR'], '..', 'contrib')
66 sys.path.insert(0, contribpath)
68 sys.path.insert(0, contribpath)
67 checkcode = __import__('check-code')
69 checkcode = __import__('check-code')
68
70
69 # register perf.py specific entry with "checks" in check-code.py
71 # register perf.py specific entry with "checks" in check-code.py
70 checkcode.checks.append(('perf.py', r'contrib/perf.py$', '',
72 checkcode.checks.append(('perf.py', r'contrib/perf.py$', '',
71 checkcode.pyfilters, perfpypats))
73 checkcode.pyfilters, perfpypats))
72
74
73 sys.exit(checkcode.main())
75 sys.exit(checkcode.main())
General Comments 0
You need to be logged in to leave comments. Login now