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