##// END OF EJS Templates
perf: introduce safeattrsetter to replace direct attribute assignment...
FUJIWARA Katsunori -
r30143:2d858c77 default
parent child Browse files
Show More
@@ -1,944 +1,978 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
186
187 def safeattrsetter(obj, name, ignoremissing=False):
188 """Ensure that 'obj' has 'name' attribute before subsequent setattr
189
190 This function is aborted, if 'obj' doesn't have 'name' attribute
191 at runtime. This avoids overlooking removal of an attribute, which
192 breaks assumption of performance measurement, in the future.
193
194 This function returns the object to (1) assign a new value, and
195 (2) restore an original value to the attribute.
196
197 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
198 abortion, and this function returns None. This is useful to
199 examine an attribute, which isn't ensured in all Mercurial
200 versions.
201 """
202 if not util.safehasattr(obj, name):
203 if ignoremissing:
204 return None
205 raise error.Abort(("missing attribute %s of %s might break assumption"
206 " of performance measurement") % (name, obj))
207
208 origvalue = getattr(obj, name)
209 class attrutil(object):
210 def set(self, newvalue):
211 setattr(obj, name, newvalue)
212 def restore(self):
213 setattr(obj, name, origvalue)
214
215 return attrutil()
216
217 # perf commands
218
185 @command('perfwalk', formatteropts)
219 @command('perfwalk', formatteropts)
186 def perfwalk(ui, repo, *pats, **opts):
220 def perfwalk(ui, repo, *pats, **opts):
187 timer, fm = gettimer(ui, opts)
221 timer, fm = gettimer(ui, opts)
188 try:
222 try:
189 m = scmutil.match(repo[None], pats, {})
223 m = scmutil.match(repo[None], pats, {})
190 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
224 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
191 except Exception:
225 except Exception:
192 try:
226 try:
193 m = scmutil.match(repo[None], pats, {})
227 m = scmutil.match(repo[None], pats, {})
194 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
228 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
195 except Exception:
229 except Exception:
196 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
230 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
197 fm.end()
231 fm.end()
198
232
199 @command('perfannotate', formatteropts)
233 @command('perfannotate', formatteropts)
200 def perfannotate(ui, repo, f, **opts):
234 def perfannotate(ui, repo, f, **opts):
201 timer, fm = gettimer(ui, opts)
235 timer, fm = gettimer(ui, opts)
202 fc = repo['.'][f]
236 fc = repo['.'][f]
203 timer(lambda: len(fc.annotate(True)))
237 timer(lambda: len(fc.annotate(True)))
204 fm.end()
238 fm.end()
205
239
206 @command('perfstatus',
240 @command('perfstatus',
207 [('u', 'unknown', False,
241 [('u', 'unknown', False,
208 'ask status to look for unknown files')] + formatteropts)
242 'ask status to look for unknown files')] + formatteropts)
209 def perfstatus(ui, repo, **opts):
243 def perfstatus(ui, repo, **opts):
210 #m = match.always(repo.root, repo.getcwd())
244 #m = match.always(repo.root, repo.getcwd())
211 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
245 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
212 # False))))
246 # False))))
213 timer, fm = gettimer(ui, opts)
247 timer, fm = gettimer(ui, opts)
214 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
248 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
215 fm.end()
249 fm.end()
216
250
217 @command('perfaddremove', formatteropts)
251 @command('perfaddremove', formatteropts)
218 def perfaddremove(ui, repo, **opts):
252 def perfaddremove(ui, repo, **opts):
219 timer, fm = gettimer(ui, opts)
253 timer, fm = gettimer(ui, opts)
220 try:
254 try:
221 oldquiet = repo.ui.quiet
255 oldquiet = repo.ui.quiet
222 repo.ui.quiet = True
256 repo.ui.quiet = True
223 matcher = scmutil.match(repo[None])
257 matcher = scmutil.match(repo[None])
224 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
258 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
225 finally:
259 finally:
226 repo.ui.quiet = oldquiet
260 repo.ui.quiet = oldquiet
227 fm.end()
261 fm.end()
228
262
229 def clearcaches(cl):
263 def clearcaches(cl):
230 # behave somewhat consistently across internal API changes
264 # behave somewhat consistently across internal API changes
231 if util.safehasattr(cl, 'clearcaches'):
265 if util.safehasattr(cl, 'clearcaches'):
232 cl.clearcaches()
266 cl.clearcaches()
233 elif util.safehasattr(cl, '_nodecache'):
267 elif util.safehasattr(cl, '_nodecache'):
234 from mercurial.node import nullid, nullrev
268 from mercurial.node import nullid, nullrev
235 cl._nodecache = {nullid: nullrev}
269 cl._nodecache = {nullid: nullrev}
236 cl._nodepos = None
270 cl._nodepos = None
237
271
238 @command('perfheads', formatteropts)
272 @command('perfheads', formatteropts)
239 def perfheads(ui, repo, **opts):
273 def perfheads(ui, repo, **opts):
240 timer, fm = gettimer(ui, opts)
274 timer, fm = gettimer(ui, opts)
241 cl = repo.changelog
275 cl = repo.changelog
242 def d():
276 def d():
243 len(cl.headrevs())
277 len(cl.headrevs())
244 clearcaches(cl)
278 clearcaches(cl)
245 timer(d)
279 timer(d)
246 fm.end()
280 fm.end()
247
281
248 @command('perftags', formatteropts)
282 @command('perftags', formatteropts)
249 def perftags(ui, repo, **opts):
283 def perftags(ui, repo, **opts):
250 import mercurial.changelog
284 import mercurial.changelog
251 import mercurial.manifest
285 import mercurial.manifest
252 timer, fm = gettimer(ui, opts)
286 timer, fm = gettimer(ui, opts)
253 def t():
287 def t():
254 repo.changelog = mercurial.changelog.changelog(repo.svfs)
288 repo.changelog = mercurial.changelog.changelog(repo.svfs)
255 repo.manifest = mercurial.manifest.manifest(repo.svfs)
289 repo.manifest = mercurial.manifest.manifest(repo.svfs)
256 repo._tags = None
290 repo._tags = None
257 return len(repo.tags())
291 return len(repo.tags())
258 timer(t)
292 timer(t)
259 fm.end()
293 fm.end()
260
294
261 @command('perfancestors', formatteropts)
295 @command('perfancestors', formatteropts)
262 def perfancestors(ui, repo, **opts):
296 def perfancestors(ui, repo, **opts):
263 timer, fm = gettimer(ui, opts)
297 timer, fm = gettimer(ui, opts)
264 heads = repo.changelog.headrevs()
298 heads = repo.changelog.headrevs()
265 def d():
299 def d():
266 for a in repo.changelog.ancestors(heads):
300 for a in repo.changelog.ancestors(heads):
267 pass
301 pass
268 timer(d)
302 timer(d)
269 fm.end()
303 fm.end()
270
304
271 @command('perfancestorset', formatteropts)
305 @command('perfancestorset', formatteropts)
272 def perfancestorset(ui, repo, revset, **opts):
306 def perfancestorset(ui, repo, revset, **opts):
273 timer, fm = gettimer(ui, opts)
307 timer, fm = gettimer(ui, opts)
274 revs = repo.revs(revset)
308 revs = repo.revs(revset)
275 heads = repo.changelog.headrevs()
309 heads = repo.changelog.headrevs()
276 def d():
310 def d():
277 s = repo.changelog.ancestors(heads)
311 s = repo.changelog.ancestors(heads)
278 for rev in revs:
312 for rev in revs:
279 rev in s
313 rev in s
280 timer(d)
314 timer(d)
281 fm.end()
315 fm.end()
282
316
283 @command('perfchangegroupchangelog', formatteropts +
317 @command('perfchangegroupchangelog', formatteropts +
284 [('', 'version', '02', 'changegroup version'),
318 [('', 'version', '02', 'changegroup version'),
285 ('r', 'rev', '', 'revisions to add to changegroup')])
319 ('r', 'rev', '', 'revisions to add to changegroup')])
286 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
320 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
287 """Benchmark producing a changelog group for a changegroup.
321 """Benchmark producing a changelog group for a changegroup.
288
322
289 This measures the time spent processing the changelog during a
323 This measures the time spent processing the changelog during a
290 bundle operation. This occurs during `hg bundle` and on a server
324 bundle operation. This occurs during `hg bundle` and on a server
291 processing a `getbundle` wire protocol request (handles clones
325 processing a `getbundle` wire protocol request (handles clones
292 and pull requests).
326 and pull requests).
293
327
294 By default, all revisions are added to the changegroup.
328 By default, all revisions are added to the changegroup.
295 """
329 """
296 cl = repo.changelog
330 cl = repo.changelog
297 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
331 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
298 bundler = changegroup.getbundler(version, repo)
332 bundler = changegroup.getbundler(version, repo)
299
333
300 def lookup(node):
334 def lookup(node):
301 # The real bundler reads the revision in order to access the
335 # The real bundler reads the revision in order to access the
302 # manifest node and files list. Do that here.
336 # manifest node and files list. Do that here.
303 cl.read(node)
337 cl.read(node)
304 return node
338 return node
305
339
306 def d():
340 def d():
307 for chunk in bundler.group(revs, cl, lookup):
341 for chunk in bundler.group(revs, cl, lookup):
308 pass
342 pass
309
343
310 timer, fm = gettimer(ui, opts)
344 timer, fm = gettimer(ui, opts)
311 timer(d)
345 timer(d)
312 fm.end()
346 fm.end()
313
347
314 @command('perfdirs', formatteropts)
348 @command('perfdirs', formatteropts)
315 def perfdirs(ui, repo, **opts):
349 def perfdirs(ui, repo, **opts):
316 timer, fm = gettimer(ui, opts)
350 timer, fm = gettimer(ui, opts)
317 dirstate = repo.dirstate
351 dirstate = repo.dirstate
318 'a' in dirstate
352 'a' in dirstate
319 def d():
353 def d():
320 dirstate.dirs()
354 dirstate.dirs()
321 del dirstate._dirs
355 del dirstate._dirs
322 timer(d)
356 timer(d)
323 fm.end()
357 fm.end()
324
358
325 @command('perfdirstate', formatteropts)
359 @command('perfdirstate', formatteropts)
326 def perfdirstate(ui, repo, **opts):
360 def perfdirstate(ui, repo, **opts):
327 timer, fm = gettimer(ui, opts)
361 timer, fm = gettimer(ui, opts)
328 "a" in repo.dirstate
362 "a" in repo.dirstate
329 def d():
363 def d():
330 repo.dirstate.invalidate()
364 repo.dirstate.invalidate()
331 "a" in repo.dirstate
365 "a" in repo.dirstate
332 timer(d)
366 timer(d)
333 fm.end()
367 fm.end()
334
368
335 @command('perfdirstatedirs', formatteropts)
369 @command('perfdirstatedirs', formatteropts)
336 def perfdirstatedirs(ui, repo, **opts):
370 def perfdirstatedirs(ui, repo, **opts):
337 timer, fm = gettimer(ui, opts)
371 timer, fm = gettimer(ui, opts)
338 "a" in repo.dirstate
372 "a" in repo.dirstate
339 def d():
373 def d():
340 "a" in repo.dirstate._dirs
374 "a" in repo.dirstate._dirs
341 del repo.dirstate._dirs
375 del repo.dirstate._dirs
342 timer(d)
376 timer(d)
343 fm.end()
377 fm.end()
344
378
345 @command('perfdirstatefoldmap', formatteropts)
379 @command('perfdirstatefoldmap', formatteropts)
346 def perfdirstatefoldmap(ui, repo, **opts):
380 def perfdirstatefoldmap(ui, repo, **opts):
347 timer, fm = gettimer(ui, opts)
381 timer, fm = gettimer(ui, opts)
348 dirstate = repo.dirstate
382 dirstate = repo.dirstate
349 'a' in dirstate
383 'a' in dirstate
350 def d():
384 def d():
351 dirstate._filefoldmap.get('a')
385 dirstate._filefoldmap.get('a')
352 del dirstate._filefoldmap
386 del dirstate._filefoldmap
353 timer(d)
387 timer(d)
354 fm.end()
388 fm.end()
355
389
356 @command('perfdirfoldmap', formatteropts)
390 @command('perfdirfoldmap', formatteropts)
357 def perfdirfoldmap(ui, repo, **opts):
391 def perfdirfoldmap(ui, repo, **opts):
358 timer, fm = gettimer(ui, opts)
392 timer, fm = gettimer(ui, opts)
359 dirstate = repo.dirstate
393 dirstate = repo.dirstate
360 'a' in dirstate
394 'a' in dirstate
361 def d():
395 def d():
362 dirstate._dirfoldmap.get('a')
396 dirstate._dirfoldmap.get('a')
363 del dirstate._dirfoldmap
397 del dirstate._dirfoldmap
364 del dirstate._dirs
398 del dirstate._dirs
365 timer(d)
399 timer(d)
366 fm.end()
400 fm.end()
367
401
368 @command('perfdirstatewrite', formatteropts)
402 @command('perfdirstatewrite', formatteropts)
369 def perfdirstatewrite(ui, repo, **opts):
403 def perfdirstatewrite(ui, repo, **opts):
370 timer, fm = gettimer(ui, opts)
404 timer, fm = gettimer(ui, opts)
371 ds = repo.dirstate
405 ds = repo.dirstate
372 "a" in ds
406 "a" in ds
373 def d():
407 def d():
374 ds._dirty = True
408 ds._dirty = True
375 ds.write(repo.currenttransaction())
409 ds.write(repo.currenttransaction())
376 timer(d)
410 timer(d)
377 fm.end()
411 fm.end()
378
412
379 @command('perfmergecalculate',
413 @command('perfmergecalculate',
380 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
414 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
381 def perfmergecalculate(ui, repo, rev, **opts):
415 def perfmergecalculate(ui, repo, rev, **opts):
382 timer, fm = gettimer(ui, opts)
416 timer, fm = gettimer(ui, opts)
383 wctx = repo[None]
417 wctx = repo[None]
384 rctx = scmutil.revsingle(repo, rev, rev)
418 rctx = scmutil.revsingle(repo, rev, rev)
385 ancestor = wctx.ancestor(rctx)
419 ancestor = wctx.ancestor(rctx)
386 # we don't want working dir files to be stat'd in the benchmark, so prime
420 # we don't want working dir files to be stat'd in the benchmark, so prime
387 # that cache
421 # that cache
388 wctx.dirty()
422 wctx.dirty()
389 def d():
423 def d():
390 # acceptremote is True because we don't want prompts in the middle of
424 # acceptremote is True because we don't want prompts in the middle of
391 # our benchmark
425 # our benchmark
392 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
426 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
393 acceptremote=True, followcopies=True)
427 acceptremote=True, followcopies=True)
394 timer(d)
428 timer(d)
395 fm.end()
429 fm.end()
396
430
397 @command('perfpathcopies', [], "REV REV")
431 @command('perfpathcopies', [], "REV REV")
398 def perfpathcopies(ui, repo, rev1, rev2, **opts):
432 def perfpathcopies(ui, repo, rev1, rev2, **opts):
399 timer, fm = gettimer(ui, opts)
433 timer, fm = gettimer(ui, opts)
400 ctx1 = scmutil.revsingle(repo, rev1, rev1)
434 ctx1 = scmutil.revsingle(repo, rev1, rev1)
401 ctx2 = scmutil.revsingle(repo, rev2, rev2)
435 ctx2 = scmutil.revsingle(repo, rev2, rev2)
402 def d():
436 def d():
403 copies.pathcopies(ctx1, ctx2)
437 copies.pathcopies(ctx1, ctx2)
404 timer(d)
438 timer(d)
405 fm.end()
439 fm.end()
406
440
407 @command('perfmanifest', [], 'REV')
441 @command('perfmanifest', [], 'REV')
408 def perfmanifest(ui, repo, rev, **opts):
442 def perfmanifest(ui, repo, rev, **opts):
409 timer, fm = gettimer(ui, opts)
443 timer, fm = gettimer(ui, opts)
410 ctx = scmutil.revsingle(repo, rev, rev)
444 ctx = scmutil.revsingle(repo, rev, rev)
411 t = ctx.manifestnode()
445 t = ctx.manifestnode()
412 def d():
446 def d():
413 repo.manifest.clearcaches()
447 repo.manifest.clearcaches()
414 repo.manifest.read(t)
448 repo.manifest.read(t)
415 timer(d)
449 timer(d)
416 fm.end()
450 fm.end()
417
451
418 @command('perfchangeset', formatteropts)
452 @command('perfchangeset', formatteropts)
419 def perfchangeset(ui, repo, rev, **opts):
453 def perfchangeset(ui, repo, rev, **opts):
420 timer, fm = gettimer(ui, opts)
454 timer, fm = gettimer(ui, opts)
421 n = repo[rev].node()
455 n = repo[rev].node()
422 def d():
456 def d():
423 repo.changelog.read(n)
457 repo.changelog.read(n)
424 #repo.changelog._cache = None
458 #repo.changelog._cache = None
425 timer(d)
459 timer(d)
426 fm.end()
460 fm.end()
427
461
428 @command('perfindex', formatteropts)
462 @command('perfindex', formatteropts)
429 def perfindex(ui, repo, **opts):
463 def perfindex(ui, repo, **opts):
430 import mercurial.revlog
464 import mercurial.revlog
431 timer, fm = gettimer(ui, opts)
465 timer, fm = gettimer(ui, opts)
432 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
466 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
433 n = repo["tip"].node()
467 n = repo["tip"].node()
434 def d():
468 def d():
435 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
469 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
436 cl.rev(n)
470 cl.rev(n)
437 timer(d)
471 timer(d)
438 fm.end()
472 fm.end()
439
473
440 @command('perfstartup', formatteropts)
474 @command('perfstartup', formatteropts)
441 def perfstartup(ui, repo, **opts):
475 def perfstartup(ui, repo, **opts):
442 timer, fm = gettimer(ui, opts)
476 timer, fm = gettimer(ui, opts)
443 cmd = sys.argv[0]
477 cmd = sys.argv[0]
444 def d():
478 def d():
445 if os.name != 'nt':
479 if os.name != 'nt':
446 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
480 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
447 else:
481 else:
448 os.environ['HGRCPATH'] = ''
482 os.environ['HGRCPATH'] = ''
449 os.system("%s version -q > NUL" % cmd)
483 os.system("%s version -q > NUL" % cmd)
450 timer(d)
484 timer(d)
451 fm.end()
485 fm.end()
452
486
453 @command('perfparents', formatteropts)
487 @command('perfparents', formatteropts)
454 def perfparents(ui, repo, **opts):
488 def perfparents(ui, repo, **opts):
455 timer, fm = gettimer(ui, opts)
489 timer, fm = gettimer(ui, opts)
456 # control the number of commits perfparents iterates over
490 # control the number of commits perfparents iterates over
457 # experimental config: perf.parentscount
491 # experimental config: perf.parentscount
458 count = ui.configint("perf", "parentscount", 1000)
492 count = ui.configint("perf", "parentscount", 1000)
459 if len(repo.changelog) < count:
493 if len(repo.changelog) < count:
460 raise error.Abort("repo needs %d commits for this test" % count)
494 raise error.Abort("repo needs %d commits for this test" % count)
461 repo = repo.unfiltered()
495 repo = repo.unfiltered()
462 nl = [repo.changelog.node(i) for i in xrange(count)]
496 nl = [repo.changelog.node(i) for i in xrange(count)]
463 def d():
497 def d():
464 for n in nl:
498 for n in nl:
465 repo.changelog.parents(n)
499 repo.changelog.parents(n)
466 timer(d)
500 timer(d)
467 fm.end()
501 fm.end()
468
502
469 @command('perfctxfiles', formatteropts)
503 @command('perfctxfiles', formatteropts)
470 def perfctxfiles(ui, repo, x, **opts):
504 def perfctxfiles(ui, repo, x, **opts):
471 x = int(x)
505 x = int(x)
472 timer, fm = gettimer(ui, opts)
506 timer, fm = gettimer(ui, opts)
473 def d():
507 def d():
474 len(repo[x].files())
508 len(repo[x].files())
475 timer(d)
509 timer(d)
476 fm.end()
510 fm.end()
477
511
478 @command('perfrawfiles', formatteropts)
512 @command('perfrawfiles', formatteropts)
479 def perfrawfiles(ui, repo, x, **opts):
513 def perfrawfiles(ui, repo, x, **opts):
480 x = int(x)
514 x = int(x)
481 timer, fm = gettimer(ui, opts)
515 timer, fm = gettimer(ui, opts)
482 cl = repo.changelog
516 cl = repo.changelog
483 def d():
517 def d():
484 len(cl.read(x)[3])
518 len(cl.read(x)[3])
485 timer(d)
519 timer(d)
486 fm.end()
520 fm.end()
487
521
488 @command('perflookup', formatteropts)
522 @command('perflookup', formatteropts)
489 def perflookup(ui, repo, rev, **opts):
523 def perflookup(ui, repo, rev, **opts):
490 timer, fm = gettimer(ui, opts)
524 timer, fm = gettimer(ui, opts)
491 timer(lambda: len(repo.lookup(rev)))
525 timer(lambda: len(repo.lookup(rev)))
492 fm.end()
526 fm.end()
493
527
494 @command('perfrevrange', formatteropts)
528 @command('perfrevrange', formatteropts)
495 def perfrevrange(ui, repo, *specs, **opts):
529 def perfrevrange(ui, repo, *specs, **opts):
496 timer, fm = gettimer(ui, opts)
530 timer, fm = gettimer(ui, opts)
497 revrange = scmutil.revrange
531 revrange = scmutil.revrange
498 timer(lambda: len(revrange(repo, specs)))
532 timer(lambda: len(revrange(repo, specs)))
499 fm.end()
533 fm.end()
500
534
501 @command('perfnodelookup', formatteropts)
535 @command('perfnodelookup', formatteropts)
502 def perfnodelookup(ui, repo, rev, **opts):
536 def perfnodelookup(ui, repo, rev, **opts):
503 timer, fm = gettimer(ui, opts)
537 timer, fm = gettimer(ui, opts)
504 import mercurial.revlog
538 import mercurial.revlog
505 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
539 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
506 n = repo[rev].node()
540 n = repo[rev].node()
507 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
541 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
508 def d():
542 def d():
509 cl.rev(n)
543 cl.rev(n)
510 clearcaches(cl)
544 clearcaches(cl)
511 timer(d)
545 timer(d)
512 fm.end()
546 fm.end()
513
547
514 @command('perflog',
548 @command('perflog',
515 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
549 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
516 def perflog(ui, repo, rev=None, **opts):
550 def perflog(ui, repo, rev=None, **opts):
517 if rev is None:
551 if rev is None:
518 rev=[]
552 rev=[]
519 timer, fm = gettimer(ui, opts)
553 timer, fm = gettimer(ui, opts)
520 ui.pushbuffer()
554 ui.pushbuffer()
521 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
555 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
522 copies=opts.get('rename')))
556 copies=opts.get('rename')))
523 ui.popbuffer()
557 ui.popbuffer()
524 fm.end()
558 fm.end()
525
559
526 @command('perfmoonwalk', formatteropts)
560 @command('perfmoonwalk', formatteropts)
527 def perfmoonwalk(ui, repo, **opts):
561 def perfmoonwalk(ui, repo, **opts):
528 """benchmark walking the changelog backwards
562 """benchmark walking the changelog backwards
529
563
530 This also loads the changelog data for each revision in the changelog.
564 This also loads the changelog data for each revision in the changelog.
531 """
565 """
532 timer, fm = gettimer(ui, opts)
566 timer, fm = gettimer(ui, opts)
533 def moonwalk():
567 def moonwalk():
534 for i in xrange(len(repo), -1, -1):
568 for i in xrange(len(repo), -1, -1):
535 ctx = repo[i]
569 ctx = repo[i]
536 ctx.branch() # read changelog data (in addition to the index)
570 ctx.branch() # read changelog data (in addition to the index)
537 timer(moonwalk)
571 timer(moonwalk)
538 fm.end()
572 fm.end()
539
573
540 @command('perftemplating', formatteropts)
574 @command('perftemplating', formatteropts)
541 def perftemplating(ui, repo, rev=None, **opts):
575 def perftemplating(ui, repo, rev=None, **opts):
542 if rev is None:
576 if rev is None:
543 rev=[]
577 rev=[]
544 timer, fm = gettimer(ui, opts)
578 timer, fm = gettimer(ui, opts)
545 ui.pushbuffer()
579 ui.pushbuffer()
546 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
580 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
547 template='{date|shortdate} [{rev}:{node|short}]'
581 template='{date|shortdate} [{rev}:{node|short}]'
548 ' {author|person}: {desc|firstline}\n'))
582 ' {author|person}: {desc|firstline}\n'))
549 ui.popbuffer()
583 ui.popbuffer()
550 fm.end()
584 fm.end()
551
585
552 @command('perfcca', formatteropts)
586 @command('perfcca', formatteropts)
553 def perfcca(ui, repo, **opts):
587 def perfcca(ui, repo, **opts):
554 timer, fm = gettimer(ui, opts)
588 timer, fm = gettimer(ui, opts)
555 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
589 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
556 fm.end()
590 fm.end()
557
591
558 @command('perffncacheload', formatteropts)
592 @command('perffncacheload', formatteropts)
559 def perffncacheload(ui, repo, **opts):
593 def perffncacheload(ui, repo, **opts):
560 timer, fm = gettimer(ui, opts)
594 timer, fm = gettimer(ui, opts)
561 s = repo.store
595 s = repo.store
562 def d():
596 def d():
563 s.fncache._load()
597 s.fncache._load()
564 timer(d)
598 timer(d)
565 fm.end()
599 fm.end()
566
600
567 @command('perffncachewrite', formatteropts)
601 @command('perffncachewrite', formatteropts)
568 def perffncachewrite(ui, repo, **opts):
602 def perffncachewrite(ui, repo, **opts):
569 timer, fm = gettimer(ui, opts)
603 timer, fm = gettimer(ui, opts)
570 s = repo.store
604 s = repo.store
571 s.fncache._load()
605 s.fncache._load()
572 lock = repo.lock()
606 lock = repo.lock()
573 tr = repo.transaction('perffncachewrite')
607 tr = repo.transaction('perffncachewrite')
574 def d():
608 def d():
575 s.fncache._dirty = True
609 s.fncache._dirty = True
576 s.fncache.write(tr)
610 s.fncache.write(tr)
577 timer(d)
611 timer(d)
578 tr.close()
612 tr.close()
579 lock.release()
613 lock.release()
580 fm.end()
614 fm.end()
581
615
582 @command('perffncacheencode', formatteropts)
616 @command('perffncacheencode', formatteropts)
583 def perffncacheencode(ui, repo, **opts):
617 def perffncacheencode(ui, repo, **opts):
584 timer, fm = gettimer(ui, opts)
618 timer, fm = gettimer(ui, opts)
585 s = repo.store
619 s = repo.store
586 s.fncache._load()
620 s.fncache._load()
587 def d():
621 def d():
588 for p in s.fncache.entries:
622 for p in s.fncache.entries:
589 s.encode(p)
623 s.encode(p)
590 timer(d)
624 timer(d)
591 fm.end()
625 fm.end()
592
626
593 @command('perfdiffwd', formatteropts)
627 @command('perfdiffwd', formatteropts)
594 def perfdiffwd(ui, repo, **opts):
628 def perfdiffwd(ui, repo, **opts):
595 """Profile diff of working directory changes"""
629 """Profile diff of working directory changes"""
596 timer, fm = gettimer(ui, opts)
630 timer, fm = gettimer(ui, opts)
597 options = {
631 options = {
598 'w': 'ignore_all_space',
632 'w': 'ignore_all_space',
599 'b': 'ignore_space_change',
633 'b': 'ignore_space_change',
600 'B': 'ignore_blank_lines',
634 'B': 'ignore_blank_lines',
601 }
635 }
602
636
603 for diffopt in ('', 'w', 'b', 'B', 'wB'):
637 for diffopt in ('', 'w', 'b', 'B', 'wB'):
604 opts = dict((options[c], '1') for c in diffopt)
638 opts = dict((options[c], '1') for c in diffopt)
605 def d():
639 def d():
606 ui.pushbuffer()
640 ui.pushbuffer()
607 commands.diff(ui, repo, **opts)
641 commands.diff(ui, repo, **opts)
608 ui.popbuffer()
642 ui.popbuffer()
609 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
643 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
610 timer(d, title)
644 timer(d, title)
611 fm.end()
645 fm.end()
612
646
613 @command('perfrevlog', revlogopts + formatteropts +
647 @command('perfrevlog', revlogopts + formatteropts +
614 [('d', 'dist', 100, 'distance between the revisions'),
648 [('d', 'dist', 100, 'distance between the revisions'),
615 ('s', 'startrev', 0, 'revision to start reading at'),
649 ('s', 'startrev', 0, 'revision to start reading at'),
616 ('', 'reverse', False, 'read in reverse')],
650 ('', 'reverse', False, 'read in reverse')],
617 '-c|-m|FILE')
651 '-c|-m|FILE')
618 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
652 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
619 """Benchmark reading a series of revisions from a revlog.
653 """Benchmark reading a series of revisions from a revlog.
620
654
621 By default, we read every ``-d/--dist`` revision from 0 to tip of
655 By default, we read every ``-d/--dist`` revision from 0 to tip of
622 the specified revlog.
656 the specified revlog.
623
657
624 The start revision can be defined via ``-s/--startrev``.
658 The start revision can be defined via ``-s/--startrev``.
625 """
659 """
626 timer, fm = gettimer(ui, opts)
660 timer, fm = gettimer(ui, opts)
627 _len = getlen(ui)
661 _len = getlen(ui)
628
662
629 def d():
663 def d():
630 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
664 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
631
665
632 startrev = 0
666 startrev = 0
633 endrev = _len(r)
667 endrev = _len(r)
634 dist = opts['dist']
668 dist = opts['dist']
635
669
636 if reverse:
670 if reverse:
637 startrev, endrev = endrev, startrev
671 startrev, endrev = endrev, startrev
638 dist = -1 * dist
672 dist = -1 * dist
639
673
640 for x in xrange(startrev, endrev, dist):
674 for x in xrange(startrev, endrev, dist):
641 r.revision(r.node(x))
675 r.revision(r.node(x))
642
676
643 timer(d)
677 timer(d)
644 fm.end()
678 fm.end()
645
679
646 @command('perfrevlogrevision', revlogopts + formatteropts +
680 @command('perfrevlogrevision', revlogopts + formatteropts +
647 [('', 'cache', False, 'use caches instead of clearing')],
681 [('', 'cache', False, 'use caches instead of clearing')],
648 '-c|-m|FILE REV')
682 '-c|-m|FILE REV')
649 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
683 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
650 """Benchmark obtaining a revlog revision.
684 """Benchmark obtaining a revlog revision.
651
685
652 Obtaining a revlog revision consists of roughly the following steps:
686 Obtaining a revlog revision consists of roughly the following steps:
653
687
654 1. Compute the delta chain
688 1. Compute the delta chain
655 2. Obtain the raw chunks for that delta chain
689 2. Obtain the raw chunks for that delta chain
656 3. Decompress each raw chunk
690 3. Decompress each raw chunk
657 4. Apply binary patches to obtain fulltext
691 4. Apply binary patches to obtain fulltext
658 5. Verify hash of fulltext
692 5. Verify hash of fulltext
659
693
660 This command measures the time spent in each of these phases.
694 This command measures the time spent in each of these phases.
661 """
695 """
662 if opts.get('changelog') or opts.get('manifest'):
696 if opts.get('changelog') or opts.get('manifest'):
663 file_, rev = None, file_
697 file_, rev = None, file_
664 elif rev is None:
698 elif rev is None:
665 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
699 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
666
700
667 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
701 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
668 node = r.lookup(rev)
702 node = r.lookup(rev)
669 rev = r.rev(node)
703 rev = r.rev(node)
670
704
671 def dodeltachain(rev):
705 def dodeltachain(rev):
672 if not cache:
706 if not cache:
673 r.clearcaches()
707 r.clearcaches()
674 r._deltachain(rev)
708 r._deltachain(rev)
675
709
676 def doread(chain):
710 def doread(chain):
677 if not cache:
711 if not cache:
678 r.clearcaches()
712 r.clearcaches()
679 r._chunkraw(chain[0], chain[-1])
713 r._chunkraw(chain[0], chain[-1])
680
714
681 def dodecompress(data, chain):
715 def dodecompress(data, chain):
682 if not cache:
716 if not cache:
683 r.clearcaches()
717 r.clearcaches()
684
718
685 start = r.start
719 start = r.start
686 length = r.length
720 length = r.length
687 inline = r._inline
721 inline = r._inline
688 iosize = r._io.size
722 iosize = r._io.size
689 buffer = util.buffer
723 buffer = util.buffer
690 offset = start(chain[0])
724 offset = start(chain[0])
691
725
692 for rev in chain:
726 for rev in chain:
693 chunkstart = start(rev)
727 chunkstart = start(rev)
694 if inline:
728 if inline:
695 chunkstart += (rev + 1) * iosize
729 chunkstart += (rev + 1) * iosize
696 chunklength = length(rev)
730 chunklength = length(rev)
697 b = buffer(data, chunkstart - offset, chunklength)
731 b = buffer(data, chunkstart - offset, chunklength)
698 revlog.decompress(b)
732 revlog.decompress(b)
699
733
700 def dopatch(text, bins):
734 def dopatch(text, bins):
701 if not cache:
735 if not cache:
702 r.clearcaches()
736 r.clearcaches()
703 mdiff.patches(text, bins)
737 mdiff.patches(text, bins)
704
738
705 def dohash(text):
739 def dohash(text):
706 if not cache:
740 if not cache:
707 r.clearcaches()
741 r.clearcaches()
708 r._checkhash(text, node, rev)
742 r._checkhash(text, node, rev)
709
743
710 def dorevision():
744 def dorevision():
711 if not cache:
745 if not cache:
712 r.clearcaches()
746 r.clearcaches()
713 r.revision(node)
747 r.revision(node)
714
748
715 chain = r._deltachain(rev)[0]
749 chain = r._deltachain(rev)[0]
716 data = r._chunkraw(chain[0], chain[-1])[1]
750 data = r._chunkraw(chain[0], chain[-1])[1]
717 bins = r._chunks(chain)
751 bins = r._chunks(chain)
718 text = str(bins[0])
752 text = str(bins[0])
719 bins = bins[1:]
753 bins = bins[1:]
720 text = mdiff.patches(text, bins)
754 text = mdiff.patches(text, bins)
721
755
722 benches = [
756 benches = [
723 (lambda: dorevision(), 'full'),
757 (lambda: dorevision(), 'full'),
724 (lambda: dodeltachain(rev), 'deltachain'),
758 (lambda: dodeltachain(rev), 'deltachain'),
725 (lambda: doread(chain), 'read'),
759 (lambda: doread(chain), 'read'),
726 (lambda: dodecompress(data, chain), 'decompress'),
760 (lambda: dodecompress(data, chain), 'decompress'),
727 (lambda: dopatch(text, bins), 'patch'),
761 (lambda: dopatch(text, bins), 'patch'),
728 (lambda: dohash(text), 'hash'),
762 (lambda: dohash(text), 'hash'),
729 ]
763 ]
730
764
731 for fn, title in benches:
765 for fn, title in benches:
732 timer, fm = gettimer(ui, opts)
766 timer, fm = gettimer(ui, opts)
733 timer(fn, title=title)
767 timer(fn, title=title)
734 fm.end()
768 fm.end()
735
769
736 @command('perfrevset',
770 @command('perfrevset',
737 [('C', 'clear', False, 'clear volatile cache between each call.'),
771 [('C', 'clear', False, 'clear volatile cache between each call.'),
738 ('', 'contexts', False, 'obtain changectx for each revision')]
772 ('', 'contexts', False, 'obtain changectx for each revision')]
739 + formatteropts, "REVSET")
773 + formatteropts, "REVSET")
740 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
774 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
741 """benchmark the execution time of a revset
775 """benchmark the execution time of a revset
742
776
743 Use the --clean option if need to evaluate the impact of build volatile
777 Use the --clean option if need to evaluate the impact of build volatile
744 revisions set cache on the revset execution. Volatile cache hold filtered
778 revisions set cache on the revset execution. Volatile cache hold filtered
745 and obsolete related cache."""
779 and obsolete related cache."""
746 timer, fm = gettimer(ui, opts)
780 timer, fm = gettimer(ui, opts)
747 def d():
781 def d():
748 if clear:
782 if clear:
749 repo.invalidatevolatilesets()
783 repo.invalidatevolatilesets()
750 if contexts:
784 if contexts:
751 for ctx in repo.set(expr): pass
785 for ctx in repo.set(expr): pass
752 else:
786 else:
753 for r in repo.revs(expr): pass
787 for r in repo.revs(expr): pass
754 timer(d)
788 timer(d)
755 fm.end()
789 fm.end()
756
790
757 @command('perfvolatilesets', formatteropts)
791 @command('perfvolatilesets', formatteropts)
758 def perfvolatilesets(ui, repo, *names, **opts):
792 def perfvolatilesets(ui, repo, *names, **opts):
759 """benchmark the computation of various volatile set
793 """benchmark the computation of various volatile set
760
794
761 Volatile set computes element related to filtering and obsolescence."""
795 Volatile set computes element related to filtering and obsolescence."""
762 timer, fm = gettimer(ui, opts)
796 timer, fm = gettimer(ui, opts)
763 repo = repo.unfiltered()
797 repo = repo.unfiltered()
764
798
765 def getobs(name):
799 def getobs(name):
766 def d():
800 def d():
767 repo.invalidatevolatilesets()
801 repo.invalidatevolatilesets()
768 obsolete.getrevs(repo, name)
802 obsolete.getrevs(repo, name)
769 return d
803 return d
770
804
771 allobs = sorted(obsolete.cachefuncs)
805 allobs = sorted(obsolete.cachefuncs)
772 if names:
806 if names:
773 allobs = [n for n in allobs if n in names]
807 allobs = [n for n in allobs if n in names]
774
808
775 for name in allobs:
809 for name in allobs:
776 timer(getobs(name), title=name)
810 timer(getobs(name), title=name)
777
811
778 def getfiltered(name):
812 def getfiltered(name):
779 def d():
813 def d():
780 repo.invalidatevolatilesets()
814 repo.invalidatevolatilesets()
781 repoview.filterrevs(repo, name)
815 repoview.filterrevs(repo, name)
782 return d
816 return d
783
817
784 allfilter = sorted(repoview.filtertable)
818 allfilter = sorted(repoview.filtertable)
785 if names:
819 if names:
786 allfilter = [n for n in allfilter if n in names]
820 allfilter = [n for n in allfilter if n in names]
787
821
788 for name in allfilter:
822 for name in allfilter:
789 timer(getfiltered(name), title=name)
823 timer(getfiltered(name), title=name)
790 fm.end()
824 fm.end()
791
825
792 @command('perfbranchmap',
826 @command('perfbranchmap',
793 [('f', 'full', False,
827 [('f', 'full', False,
794 'Includes build time of subset'),
828 'Includes build time of subset'),
795 ] + formatteropts)
829 ] + formatteropts)
796 def perfbranchmap(ui, repo, full=False, **opts):
830 def perfbranchmap(ui, repo, full=False, **opts):
797 """benchmark the update of a branchmap
831 """benchmark the update of a branchmap
798
832
799 This benchmarks the full repo.branchmap() call with read and write disabled
833 This benchmarks the full repo.branchmap() call with read and write disabled
800 """
834 """
801 timer, fm = gettimer(ui, opts)
835 timer, fm = gettimer(ui, opts)
802 def getbranchmap(filtername):
836 def getbranchmap(filtername):
803 """generate a benchmark function for the filtername"""
837 """generate a benchmark function for the filtername"""
804 if filtername is None:
838 if filtername is None:
805 view = repo
839 view = repo
806 else:
840 else:
807 view = repo.filtered(filtername)
841 view = repo.filtered(filtername)
808 def d():
842 def d():
809 if full:
843 if full:
810 view._branchcaches.clear()
844 view._branchcaches.clear()
811 else:
845 else:
812 view._branchcaches.pop(filtername, None)
846 view._branchcaches.pop(filtername, None)
813 view.branchmap()
847 view.branchmap()
814 return d
848 return d
815 # add filter in smaller subset to bigger subset
849 # add filter in smaller subset to bigger subset
816 possiblefilters = set(repoview.filtertable)
850 possiblefilters = set(repoview.filtertable)
817 allfilters = []
851 allfilters = []
818 while possiblefilters:
852 while possiblefilters:
819 for name in possiblefilters:
853 for name in possiblefilters:
820 subset = branchmap.subsettable.get(name)
854 subset = branchmap.subsettable.get(name)
821 if subset not in possiblefilters:
855 if subset not in possiblefilters:
822 break
856 break
823 else:
857 else:
824 assert False, 'subset cycle %s!' % possiblefilters
858 assert False, 'subset cycle %s!' % possiblefilters
825 allfilters.append(name)
859 allfilters.append(name)
826 possiblefilters.remove(name)
860 possiblefilters.remove(name)
827
861
828 # warm the cache
862 # warm the cache
829 if not full:
863 if not full:
830 for name in allfilters:
864 for name in allfilters:
831 repo.filtered(name).branchmap()
865 repo.filtered(name).branchmap()
832 # add unfiltered
866 # add unfiltered
833 allfilters.append(None)
867 allfilters.append(None)
834 oldread = branchmap.read
868 oldread = branchmap.read
835 oldwrite = branchmap.branchcache.write
869 oldwrite = branchmap.branchcache.write
836 try:
870 try:
837 branchmap.read = lambda repo: None
871 branchmap.read = lambda repo: None
838 branchmap.write = lambda repo: None
872 branchmap.write = lambda repo: None
839 for name in allfilters:
873 for name in allfilters:
840 timer(getbranchmap(name), title=str(name))
874 timer(getbranchmap(name), title=str(name))
841 finally:
875 finally:
842 branchmap.read = oldread
876 branchmap.read = oldread
843 branchmap.branchcache.write = oldwrite
877 branchmap.branchcache.write = oldwrite
844 fm.end()
878 fm.end()
845
879
846 @command('perfloadmarkers')
880 @command('perfloadmarkers')
847 def perfloadmarkers(ui, repo):
881 def perfloadmarkers(ui, repo):
848 """benchmark the time to parse the on-disk markers for a repo
882 """benchmark the time to parse the on-disk markers for a repo
849
883
850 Result is the number of markers in the repo."""
884 Result is the number of markers in the repo."""
851 timer, fm = gettimer(ui)
885 timer, fm = gettimer(ui)
852 timer(lambda: len(obsolete.obsstore(repo.svfs)))
886 timer(lambda: len(obsolete.obsstore(repo.svfs)))
853 fm.end()
887 fm.end()
854
888
855 @command('perflrucachedict', formatteropts +
889 @command('perflrucachedict', formatteropts +
856 [('', 'size', 4, 'size of cache'),
890 [('', 'size', 4, 'size of cache'),
857 ('', 'gets', 10000, 'number of key lookups'),
891 ('', 'gets', 10000, 'number of key lookups'),
858 ('', 'sets', 10000, 'number of key sets'),
892 ('', 'sets', 10000, 'number of key sets'),
859 ('', 'mixed', 10000, 'number of mixed mode operations'),
893 ('', 'mixed', 10000, 'number of mixed mode operations'),
860 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
894 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
861 norepo=True)
895 norepo=True)
862 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
896 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
863 mixedgetfreq=50, **opts):
897 mixedgetfreq=50, **opts):
864 def doinit():
898 def doinit():
865 for i in xrange(10000):
899 for i in xrange(10000):
866 util.lrucachedict(size)
900 util.lrucachedict(size)
867
901
868 values = []
902 values = []
869 for i in xrange(size):
903 for i in xrange(size):
870 values.append(random.randint(0, sys.maxint))
904 values.append(random.randint(0, sys.maxint))
871
905
872 # Get mode fills the cache and tests raw lookup performance with no
906 # Get mode fills the cache and tests raw lookup performance with no
873 # eviction.
907 # eviction.
874 getseq = []
908 getseq = []
875 for i in xrange(gets):
909 for i in xrange(gets):
876 getseq.append(random.choice(values))
910 getseq.append(random.choice(values))
877
911
878 def dogets():
912 def dogets():
879 d = util.lrucachedict(size)
913 d = util.lrucachedict(size)
880 for v in values:
914 for v in values:
881 d[v] = v
915 d[v] = v
882 for key in getseq:
916 for key in getseq:
883 value = d[key]
917 value = d[key]
884 value # silence pyflakes warning
918 value # silence pyflakes warning
885
919
886 # Set mode tests insertion speed with cache eviction.
920 # Set mode tests insertion speed with cache eviction.
887 setseq = []
921 setseq = []
888 for i in xrange(sets):
922 for i in xrange(sets):
889 setseq.append(random.randint(0, sys.maxint))
923 setseq.append(random.randint(0, sys.maxint))
890
924
891 def dosets():
925 def dosets():
892 d = util.lrucachedict(size)
926 d = util.lrucachedict(size)
893 for v in setseq:
927 for v in setseq:
894 d[v] = v
928 d[v] = v
895
929
896 # Mixed mode randomly performs gets and sets with eviction.
930 # Mixed mode randomly performs gets and sets with eviction.
897 mixedops = []
931 mixedops = []
898 for i in xrange(mixed):
932 for i in xrange(mixed):
899 r = random.randint(0, 100)
933 r = random.randint(0, 100)
900 if r < mixedgetfreq:
934 if r < mixedgetfreq:
901 op = 0
935 op = 0
902 else:
936 else:
903 op = 1
937 op = 1
904
938
905 mixedops.append((op, random.randint(0, size * 2)))
939 mixedops.append((op, random.randint(0, size * 2)))
906
940
907 def domixed():
941 def domixed():
908 d = util.lrucachedict(size)
942 d = util.lrucachedict(size)
909
943
910 for op, v in mixedops:
944 for op, v in mixedops:
911 if op == 0:
945 if op == 0:
912 try:
946 try:
913 d[v]
947 d[v]
914 except KeyError:
948 except KeyError:
915 pass
949 pass
916 else:
950 else:
917 d[v] = v
951 d[v] = v
918
952
919 benches = [
953 benches = [
920 (doinit, 'init'),
954 (doinit, 'init'),
921 (dogets, 'gets'),
955 (dogets, 'gets'),
922 (dosets, 'sets'),
956 (dosets, 'sets'),
923 (domixed, 'mixed')
957 (domixed, 'mixed')
924 ]
958 ]
925
959
926 for fn, title in benches:
960 for fn, title in benches:
927 timer, fm = gettimer(ui, opts)
961 timer, fm = gettimer(ui, opts)
928 timer(fn, title=title)
962 timer(fn, title=title)
929 fm.end()
963 fm.end()
930
964
931 def uisetup(ui):
965 def uisetup(ui):
932 if (util.safehasattr(cmdutil, 'openrevlog') and
966 if (util.safehasattr(cmdutil, 'openrevlog') and
933 not util.safehasattr(commands, 'debugrevlogopts')):
967 not util.safehasattr(commands, 'debugrevlogopts')):
934 # for "historical portability":
968 # for "historical portability":
935 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
969 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
936 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
970 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
937 # openrevlog() should cause failure, because it has been
971 # openrevlog() should cause failure, because it has been
938 # available since 3.5 (or 49c583ca48c4).
972 # available since 3.5 (or 49c583ca48c4).
939 def openrevlog(orig, repo, cmd, file_, opts):
973 def openrevlog(orig, repo, cmd, file_, opts):
940 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
974 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
941 raise error.Abort("This version doesn't support --dir option",
975 raise error.Abort("This version doesn't support --dir option",
942 hint="use 3.5 or later")
976 hint="use 3.5 or later")
943 return orig(repo, cmd, file_, opts)
977 return orig(repo, cmd, file_, opts)
944 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
978 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now