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