##// END OF EJS Templates
perf: perform a garbage collection before each iteration...
Gregory Szorc -
r31397:8f5ed8fa default
parent child Browse files
Show More
@@ -1,1296 +1,1298 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 gc
23 import os
24 import os
24 import random
25 import random
25 import sys
26 import sys
26 import time
27 import time
27 from mercurial import (
28 from mercurial import (
28 bdiff,
29 bdiff,
29 changegroup,
30 changegroup,
30 cmdutil,
31 cmdutil,
31 commands,
32 commands,
32 copies,
33 copies,
33 error,
34 error,
34 extensions,
35 extensions,
35 mdiff,
36 mdiff,
36 merge,
37 merge,
37 util,
38 util,
38 )
39 )
39
40
40 # for "historical portability":
41 # for "historical portability":
41 # try to import modules separately (in dict order), and ignore
42 # try to import modules separately (in dict order), and ignore
42 # failure, because these aren't available with early Mercurial
43 # failure, because these aren't available with early Mercurial
43 try:
44 try:
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
45 from mercurial import branchmap # since 2.5 (or bcee63733aad)
45 except ImportError:
46 except ImportError:
46 pass
47 pass
47 try:
48 try:
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
49 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
49 except ImportError:
50 except ImportError:
50 pass
51 pass
51 try:
52 try:
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
53 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
53 except ImportError:
54 except ImportError:
54 pass
55 pass
55 try:
56 try:
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
57 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
57 except ImportError:
58 except ImportError:
58 pass
59 pass
59
60
60 # for "historical portability":
61 # for "historical portability":
61 # define util.safehasattr forcibly, because util.safehasattr has been
62 # define util.safehasattr forcibly, because util.safehasattr has been
62 # available since 1.9.3 (or 94b200a11cf7)
63 # available since 1.9.3 (or 94b200a11cf7)
63 _undefined = object()
64 _undefined = object()
64 def safehasattr(thing, attr):
65 def safehasattr(thing, attr):
65 return getattr(thing, attr, _undefined) is not _undefined
66 return getattr(thing, attr, _undefined) is not _undefined
66 setattr(util, 'safehasattr', safehasattr)
67 setattr(util, 'safehasattr', safehasattr)
67
68
68 # for "historical portability":
69 # for "historical portability":
69 # use locally defined empty option list, if formatteropts isn't
70 # use locally defined empty option list, if formatteropts isn't
70 # available, because commands.formatteropts has been available since
71 # available, because commands.formatteropts has been available since
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
72 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
72 # available since 2.2 (or ae5f92e154d3)
73 # available since 2.2 (or ae5f92e154d3)
73 formatteropts = getattr(commands, "formatteropts", [])
74 formatteropts = getattr(commands, "formatteropts", [])
74
75
75 # for "historical portability":
76 # for "historical portability":
76 # use locally defined option list, if debugrevlogopts isn't available,
77 # use locally defined option list, if debugrevlogopts isn't available,
77 # because commands.debugrevlogopts has been available since 3.7 (or
78 # because commands.debugrevlogopts has been available since 3.7 (or
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
79 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
79 # since 1.9 (or a79fea6b3e77).
80 # since 1.9 (or a79fea6b3e77).
80 revlogopts = getattr(commands, "debugrevlogopts", [
81 revlogopts = getattr(commands, "debugrevlogopts", [
81 ('c', 'changelog', False, ('open changelog')),
82 ('c', 'changelog', False, ('open changelog')),
82 ('m', 'manifest', False, ('open manifest')),
83 ('m', 'manifest', False, ('open manifest')),
83 ('', 'dir', False, ('open directory manifest')),
84 ('', 'dir', False, ('open directory manifest')),
84 ])
85 ])
85
86
86 cmdtable = {}
87 cmdtable = {}
87
88
88 # for "historical portability":
89 # for "historical portability":
89 # define parsealiases locally, because cmdutil.parsealiases has been
90 # define parsealiases locally, because cmdutil.parsealiases has been
90 # available since 1.5 (or 6252852b4332)
91 # available since 1.5 (or 6252852b4332)
91 def parsealiases(cmd):
92 def parsealiases(cmd):
92 return cmd.lstrip("^").split("|")
93 return cmd.lstrip("^").split("|")
93
94
94 if safehasattr(cmdutil, 'command'):
95 if safehasattr(cmdutil, 'command'):
95 import inspect
96 import inspect
96 command = cmdutil.command(cmdtable)
97 command = cmdutil.command(cmdtable)
97 if 'norepo' not in inspect.getargspec(command)[0]:
98 if 'norepo' not in inspect.getargspec(command)[0]:
98 # for "historical portability":
99 # for "historical portability":
99 # wrap original cmdutil.command, because "norepo" option has
100 # wrap original cmdutil.command, because "norepo" option has
100 # been available since 3.1 (or 75a96326cecb)
101 # been available since 3.1 (or 75a96326cecb)
101 _command = command
102 _command = command
102 def command(name, options=(), synopsis=None, norepo=False):
103 def command(name, options=(), synopsis=None, norepo=False):
103 if norepo:
104 if norepo:
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
105 commands.norepo += ' %s' % ' '.join(parsealiases(name))
105 return _command(name, list(options), synopsis)
106 return _command(name, list(options), synopsis)
106 else:
107 else:
107 # for "historical portability":
108 # for "historical portability":
108 # define "@command" annotation locally, because cmdutil.command
109 # define "@command" annotation locally, because cmdutil.command
109 # has been available since 1.9 (or 2daa5179e73f)
110 # has been available since 1.9 (or 2daa5179e73f)
110 def command(name, options=(), synopsis=None, norepo=False):
111 def command(name, options=(), synopsis=None, norepo=False):
111 def decorator(func):
112 def decorator(func):
112 if synopsis:
113 if synopsis:
113 cmdtable[name] = func, list(options), synopsis
114 cmdtable[name] = func, list(options), synopsis
114 else:
115 else:
115 cmdtable[name] = func, list(options)
116 cmdtable[name] = func, list(options)
116 if norepo:
117 if norepo:
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
118 commands.norepo += ' %s' % ' '.join(parsealiases(name))
118 return func
119 return func
119 return decorator
120 return decorator
120
121
121 def getlen(ui):
122 def getlen(ui):
122 if ui.configbool("perf", "stub"):
123 if ui.configbool("perf", "stub"):
123 return lambda x: 1
124 return lambda x: 1
124 return len
125 return len
125
126
126 def gettimer(ui, opts=None):
127 def gettimer(ui, opts=None):
127 """return a timer function and formatter: (timer, formatter)
128 """return a timer function and formatter: (timer, formatter)
128
129
129 This function exists to gather the creation of formatter in a single
130 This function exists to gather the creation of formatter in a single
130 place instead of duplicating it in all performance commands."""
131 place instead of duplicating it in all performance commands."""
131
132
132 # enforce an idle period before execution to counteract power management
133 # enforce an idle period before execution to counteract power management
133 # experimental config: perf.presleep
134 # experimental config: perf.presleep
134 time.sleep(getint(ui, "perf", "presleep", 1))
135 time.sleep(getint(ui, "perf", "presleep", 1))
135
136
136 if opts is None:
137 if opts is None:
137 opts = {}
138 opts = {}
138 # redirect all to stderr unless buffer api is in use
139 # redirect all to stderr unless buffer api is in use
139 if not ui._buffers:
140 if not ui._buffers:
140 ui = ui.copy()
141 ui = ui.copy()
141 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
142 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
142 if uifout:
143 if uifout:
143 # for "historical portability":
144 # for "historical portability":
144 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
145 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
145 uifout.set(ui.ferr)
146 uifout.set(ui.ferr)
146
147
147 # get a formatter
148 # get a formatter
148 uiformatter = getattr(ui, 'formatter', None)
149 uiformatter = getattr(ui, 'formatter', None)
149 if uiformatter:
150 if uiformatter:
150 fm = uiformatter('perf', opts)
151 fm = uiformatter('perf', opts)
151 else:
152 else:
152 # for "historical portability":
153 # for "historical portability":
153 # define formatter locally, because ui.formatter has been
154 # define formatter locally, because ui.formatter has been
154 # available since 2.2 (or ae5f92e154d3)
155 # available since 2.2 (or ae5f92e154d3)
155 from mercurial import node
156 from mercurial import node
156 class defaultformatter(object):
157 class defaultformatter(object):
157 """Minimized composition of baseformatter and plainformatter
158 """Minimized composition of baseformatter and plainformatter
158 """
159 """
159 def __init__(self, ui, topic, opts):
160 def __init__(self, ui, topic, opts):
160 self._ui = ui
161 self._ui = ui
161 if ui.debugflag:
162 if ui.debugflag:
162 self.hexfunc = node.hex
163 self.hexfunc = node.hex
163 else:
164 else:
164 self.hexfunc = node.short
165 self.hexfunc = node.short
165 def __nonzero__(self):
166 def __nonzero__(self):
166 return False
167 return False
167 def startitem(self):
168 def startitem(self):
168 pass
169 pass
169 def data(self, **data):
170 def data(self, **data):
170 pass
171 pass
171 def write(self, fields, deftext, *fielddata, **opts):
172 def write(self, fields, deftext, *fielddata, **opts):
172 self._ui.write(deftext % fielddata, **opts)
173 self._ui.write(deftext % fielddata, **opts)
173 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
174 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
174 if cond:
175 if cond:
175 self._ui.write(deftext % fielddata, **opts)
176 self._ui.write(deftext % fielddata, **opts)
176 def plain(self, text, **opts):
177 def plain(self, text, **opts):
177 self._ui.write(text, **opts)
178 self._ui.write(text, **opts)
178 def end(self):
179 def end(self):
179 pass
180 pass
180 fm = defaultformatter(ui, 'perf', opts)
181 fm = defaultformatter(ui, 'perf', opts)
181
182
182 # stub function, runs code only once instead of in a loop
183 # stub function, runs code only once instead of in a loop
183 # experimental config: perf.stub
184 # experimental config: perf.stub
184 if ui.configbool("perf", "stub"):
185 if ui.configbool("perf", "stub"):
185 return functools.partial(stub_timer, fm), fm
186 return functools.partial(stub_timer, fm), fm
186 return functools.partial(_timer, fm), fm
187 return functools.partial(_timer, fm), fm
187
188
188 def stub_timer(fm, func, title=None):
189 def stub_timer(fm, func, title=None):
189 func()
190 func()
190
191
191 def _timer(fm, func, title=None):
192 def _timer(fm, func, title=None):
193 gc.collect()
192 results = []
194 results = []
193 begin = util.timer()
195 begin = util.timer()
194 count = 0
196 count = 0
195 while True:
197 while True:
196 ostart = os.times()
198 ostart = os.times()
197 cstart = util.timer()
199 cstart = util.timer()
198 r = func()
200 r = func()
199 cstop = util.timer()
201 cstop = util.timer()
200 ostop = os.times()
202 ostop = os.times()
201 count += 1
203 count += 1
202 a, b = ostart, ostop
204 a, b = ostart, ostop
203 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
205 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
204 if cstop - begin > 3 and count >= 100:
206 if cstop - begin > 3 and count >= 100:
205 break
207 break
206 if cstop - begin > 10 and count >= 3:
208 if cstop - begin > 10 and count >= 3:
207 break
209 break
208
210
209 fm.startitem()
211 fm.startitem()
210
212
211 if title:
213 if title:
212 fm.write('title', '! %s\n', title)
214 fm.write('title', '! %s\n', title)
213 if r:
215 if r:
214 fm.write('result', '! result: %s\n', r)
216 fm.write('result', '! result: %s\n', r)
215 m = min(results)
217 m = min(results)
216 fm.plain('!')
218 fm.plain('!')
217 fm.write('wall', ' wall %f', m[0])
219 fm.write('wall', ' wall %f', m[0])
218 fm.write('comb', ' comb %f', m[1] + m[2])
220 fm.write('comb', ' comb %f', m[1] + m[2])
219 fm.write('user', ' user %f', m[1])
221 fm.write('user', ' user %f', m[1])
220 fm.write('sys', ' sys %f', m[2])
222 fm.write('sys', ' sys %f', m[2])
221 fm.write('count', ' (best of %d)', count)
223 fm.write('count', ' (best of %d)', count)
222 fm.plain('\n')
224 fm.plain('\n')
223
225
224 # utilities for historical portability
226 # utilities for historical portability
225
227
226 def getint(ui, section, name, default):
228 def getint(ui, section, name, default):
227 # for "historical portability":
229 # for "historical portability":
228 # ui.configint has been available since 1.9 (or fa2b596db182)
230 # ui.configint has been available since 1.9 (or fa2b596db182)
229 v = ui.config(section, name, None)
231 v = ui.config(section, name, None)
230 if v is None:
232 if v is None:
231 return default
233 return default
232 try:
234 try:
233 return int(v)
235 return int(v)
234 except ValueError:
236 except ValueError:
235 raise error.ConfigError(("%s.%s is not an integer ('%s')")
237 raise error.ConfigError(("%s.%s is not an integer ('%s')")
236 % (section, name, v))
238 % (section, name, v))
237
239
238 def safeattrsetter(obj, name, ignoremissing=False):
240 def safeattrsetter(obj, name, ignoremissing=False):
239 """Ensure that 'obj' has 'name' attribute before subsequent setattr
241 """Ensure that 'obj' has 'name' attribute before subsequent setattr
240
242
241 This function is aborted, if 'obj' doesn't have 'name' attribute
243 This function is aborted, if 'obj' doesn't have 'name' attribute
242 at runtime. This avoids overlooking removal of an attribute, which
244 at runtime. This avoids overlooking removal of an attribute, which
243 breaks assumption of performance measurement, in the future.
245 breaks assumption of performance measurement, in the future.
244
246
245 This function returns the object to (1) assign a new value, and
247 This function returns the object to (1) assign a new value, and
246 (2) restore an original value to the attribute.
248 (2) restore an original value to the attribute.
247
249
248 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
250 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
249 abortion, and this function returns None. This is useful to
251 abortion, and this function returns None. This is useful to
250 examine an attribute, which isn't ensured in all Mercurial
252 examine an attribute, which isn't ensured in all Mercurial
251 versions.
253 versions.
252 """
254 """
253 if not util.safehasattr(obj, name):
255 if not util.safehasattr(obj, name):
254 if ignoremissing:
256 if ignoremissing:
255 return None
257 return None
256 raise error.Abort(("missing attribute %s of %s might break assumption"
258 raise error.Abort(("missing attribute %s of %s might break assumption"
257 " of performance measurement") % (name, obj))
259 " of performance measurement") % (name, obj))
258
260
259 origvalue = getattr(obj, name)
261 origvalue = getattr(obj, name)
260 class attrutil(object):
262 class attrutil(object):
261 def set(self, newvalue):
263 def set(self, newvalue):
262 setattr(obj, name, newvalue)
264 setattr(obj, name, newvalue)
263 def restore(self):
265 def restore(self):
264 setattr(obj, name, origvalue)
266 setattr(obj, name, origvalue)
265
267
266 return attrutil()
268 return attrutil()
267
269
268 # utilities to examine each internal API changes
270 # utilities to examine each internal API changes
269
271
270 def getbranchmapsubsettable():
272 def getbranchmapsubsettable():
271 # for "historical portability":
273 # for "historical portability":
272 # subsettable is defined in:
274 # subsettable is defined in:
273 # - branchmap since 2.9 (or 175c6fd8cacc)
275 # - branchmap since 2.9 (or 175c6fd8cacc)
274 # - repoview since 2.5 (or 59a9f18d4587)
276 # - repoview since 2.5 (or 59a9f18d4587)
275 for mod in (branchmap, repoview):
277 for mod in (branchmap, repoview):
276 subsettable = getattr(mod, 'subsettable', None)
278 subsettable = getattr(mod, 'subsettable', None)
277 if subsettable:
279 if subsettable:
278 return subsettable
280 return subsettable
279
281
280 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
282 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
281 # branchmap and repoview modules exist, but subsettable attribute
283 # branchmap and repoview modules exist, but subsettable attribute
282 # doesn't)
284 # doesn't)
283 raise error.Abort(("perfbranchmap not available with this Mercurial"),
285 raise error.Abort(("perfbranchmap not available with this Mercurial"),
284 hint="use 2.5 or later")
286 hint="use 2.5 or later")
285
287
286 def getsvfs(repo):
288 def getsvfs(repo):
287 """Return appropriate object to access files under .hg/store
289 """Return appropriate object to access files under .hg/store
288 """
290 """
289 # for "historical portability":
291 # for "historical portability":
290 # repo.svfs has been available since 2.3 (or 7034365089bf)
292 # repo.svfs has been available since 2.3 (or 7034365089bf)
291 svfs = getattr(repo, 'svfs', None)
293 svfs = getattr(repo, 'svfs', None)
292 if svfs:
294 if svfs:
293 return svfs
295 return svfs
294 else:
296 else:
295 return getattr(repo, 'sopener')
297 return getattr(repo, 'sopener')
296
298
297 def getvfs(repo):
299 def getvfs(repo):
298 """Return appropriate object to access files under .hg
300 """Return appropriate object to access files under .hg
299 """
301 """
300 # for "historical portability":
302 # for "historical portability":
301 # repo.vfs has been available since 2.3 (or 7034365089bf)
303 # repo.vfs has been available since 2.3 (or 7034365089bf)
302 vfs = getattr(repo, 'vfs', None)
304 vfs = getattr(repo, 'vfs', None)
303 if vfs:
305 if vfs:
304 return vfs
306 return vfs
305 else:
307 else:
306 return getattr(repo, 'opener')
308 return getattr(repo, 'opener')
307
309
308 def repocleartagscachefunc(repo):
310 def repocleartagscachefunc(repo):
309 """Return the function to clear tags cache according to repo internal API
311 """Return the function to clear tags cache according to repo internal API
310 """
312 """
311 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
313 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
312 # in this case, setattr(repo, '_tagscache', None) or so isn't
314 # in this case, setattr(repo, '_tagscache', None) or so isn't
313 # correct way to clear tags cache, because existing code paths
315 # correct way to clear tags cache, because existing code paths
314 # expect _tagscache to be a structured object.
316 # expect _tagscache to be a structured object.
315 def clearcache():
317 def clearcache():
316 # _tagscache has been filteredpropertycache since 2.5 (or
318 # _tagscache has been filteredpropertycache since 2.5 (or
317 # 98c867ac1330), and delattr() can't work in such case
319 # 98c867ac1330), and delattr() can't work in such case
318 if '_tagscache' in vars(repo):
320 if '_tagscache' in vars(repo):
319 del repo.__dict__['_tagscache']
321 del repo.__dict__['_tagscache']
320 return clearcache
322 return clearcache
321
323
322 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
324 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
323 if repotags: # since 1.4 (or 5614a628d173)
325 if repotags: # since 1.4 (or 5614a628d173)
324 return lambda : repotags.set(None)
326 return lambda : repotags.set(None)
325
327
326 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
328 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
327 if repotagscache: # since 0.6 (or d7df759d0e97)
329 if repotagscache: # since 0.6 (or d7df759d0e97)
328 return lambda : repotagscache.set(None)
330 return lambda : repotagscache.set(None)
329
331
330 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
332 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
331 # this point, but it isn't so problematic, because:
333 # this point, but it isn't so problematic, because:
332 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
334 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
333 # in perftags() causes failure soon
335 # in perftags() causes failure soon
334 # - perf.py itself has been available since 1.1 (or eb240755386d)
336 # - perf.py itself has been available since 1.1 (or eb240755386d)
335 raise error.Abort(("tags API of this hg command is unknown"))
337 raise error.Abort(("tags API of this hg command is unknown"))
336
338
337 # perf commands
339 # perf commands
338
340
339 @command('perfwalk', formatteropts)
341 @command('perfwalk', formatteropts)
340 def perfwalk(ui, repo, *pats, **opts):
342 def perfwalk(ui, repo, *pats, **opts):
341 timer, fm = gettimer(ui, opts)
343 timer, fm = gettimer(ui, opts)
342 try:
344 try:
343 m = scmutil.match(repo[None], pats, {})
345 m = scmutil.match(repo[None], pats, {})
344 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
346 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
345 except Exception:
347 except Exception:
346 try:
348 try:
347 m = scmutil.match(repo[None], pats, {})
349 m = scmutil.match(repo[None], pats, {})
348 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
350 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
349 except Exception:
351 except Exception:
350 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
352 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
351 fm.end()
353 fm.end()
352
354
353 @command('perfannotate', formatteropts)
355 @command('perfannotate', formatteropts)
354 def perfannotate(ui, repo, f, **opts):
356 def perfannotate(ui, repo, f, **opts):
355 timer, fm = gettimer(ui, opts)
357 timer, fm = gettimer(ui, opts)
356 fc = repo['.'][f]
358 fc = repo['.'][f]
357 timer(lambda: len(fc.annotate(True)))
359 timer(lambda: len(fc.annotate(True)))
358 fm.end()
360 fm.end()
359
361
360 @command('perfstatus',
362 @command('perfstatus',
361 [('u', 'unknown', False,
363 [('u', 'unknown', False,
362 'ask status to look for unknown files')] + formatteropts)
364 'ask status to look for unknown files')] + formatteropts)
363 def perfstatus(ui, repo, **opts):
365 def perfstatus(ui, repo, **opts):
364 #m = match.always(repo.root, repo.getcwd())
366 #m = match.always(repo.root, repo.getcwd())
365 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
367 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
366 # False))))
368 # False))))
367 timer, fm = gettimer(ui, opts)
369 timer, fm = gettimer(ui, opts)
368 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
370 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
369 fm.end()
371 fm.end()
370
372
371 @command('perfaddremove', formatteropts)
373 @command('perfaddremove', formatteropts)
372 def perfaddremove(ui, repo, **opts):
374 def perfaddremove(ui, repo, **opts):
373 timer, fm = gettimer(ui, opts)
375 timer, fm = gettimer(ui, opts)
374 try:
376 try:
375 oldquiet = repo.ui.quiet
377 oldquiet = repo.ui.quiet
376 repo.ui.quiet = True
378 repo.ui.quiet = True
377 matcher = scmutil.match(repo[None])
379 matcher = scmutil.match(repo[None])
378 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
380 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
379 finally:
381 finally:
380 repo.ui.quiet = oldquiet
382 repo.ui.quiet = oldquiet
381 fm.end()
383 fm.end()
382
384
383 def clearcaches(cl):
385 def clearcaches(cl):
384 # behave somewhat consistently across internal API changes
386 # behave somewhat consistently across internal API changes
385 if util.safehasattr(cl, 'clearcaches'):
387 if util.safehasattr(cl, 'clearcaches'):
386 cl.clearcaches()
388 cl.clearcaches()
387 elif util.safehasattr(cl, '_nodecache'):
389 elif util.safehasattr(cl, '_nodecache'):
388 from mercurial.node import nullid, nullrev
390 from mercurial.node import nullid, nullrev
389 cl._nodecache = {nullid: nullrev}
391 cl._nodecache = {nullid: nullrev}
390 cl._nodepos = None
392 cl._nodepos = None
391
393
392 @command('perfheads', formatteropts)
394 @command('perfheads', formatteropts)
393 def perfheads(ui, repo, **opts):
395 def perfheads(ui, repo, **opts):
394 timer, fm = gettimer(ui, opts)
396 timer, fm = gettimer(ui, opts)
395 cl = repo.changelog
397 cl = repo.changelog
396 def d():
398 def d():
397 len(cl.headrevs())
399 len(cl.headrevs())
398 clearcaches(cl)
400 clearcaches(cl)
399 timer(d)
401 timer(d)
400 fm.end()
402 fm.end()
401
403
402 @command('perftags', formatteropts)
404 @command('perftags', formatteropts)
403 def perftags(ui, repo, **opts):
405 def perftags(ui, repo, **opts):
404 import mercurial.changelog
406 import mercurial.changelog
405 import mercurial.manifest
407 import mercurial.manifest
406 timer, fm = gettimer(ui, opts)
408 timer, fm = gettimer(ui, opts)
407 svfs = getsvfs(repo)
409 svfs = getsvfs(repo)
408 repocleartagscache = repocleartagscachefunc(repo)
410 repocleartagscache = repocleartagscachefunc(repo)
409 def t():
411 def t():
410 repo.changelog = mercurial.changelog.changelog(svfs)
412 repo.changelog = mercurial.changelog.changelog(svfs)
411 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
413 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
412 repocleartagscache()
414 repocleartagscache()
413 return len(repo.tags())
415 return len(repo.tags())
414 timer(t)
416 timer(t)
415 fm.end()
417 fm.end()
416
418
417 @command('perfancestors', formatteropts)
419 @command('perfancestors', formatteropts)
418 def perfancestors(ui, repo, **opts):
420 def perfancestors(ui, repo, **opts):
419 timer, fm = gettimer(ui, opts)
421 timer, fm = gettimer(ui, opts)
420 heads = repo.changelog.headrevs()
422 heads = repo.changelog.headrevs()
421 def d():
423 def d():
422 for a in repo.changelog.ancestors(heads):
424 for a in repo.changelog.ancestors(heads):
423 pass
425 pass
424 timer(d)
426 timer(d)
425 fm.end()
427 fm.end()
426
428
427 @command('perfancestorset', formatteropts)
429 @command('perfancestorset', formatteropts)
428 def perfancestorset(ui, repo, revset, **opts):
430 def perfancestorset(ui, repo, revset, **opts):
429 timer, fm = gettimer(ui, opts)
431 timer, fm = gettimer(ui, opts)
430 revs = repo.revs(revset)
432 revs = repo.revs(revset)
431 heads = repo.changelog.headrevs()
433 heads = repo.changelog.headrevs()
432 def d():
434 def d():
433 s = repo.changelog.ancestors(heads)
435 s = repo.changelog.ancestors(heads)
434 for rev in revs:
436 for rev in revs:
435 rev in s
437 rev in s
436 timer(d)
438 timer(d)
437 fm.end()
439 fm.end()
438
440
439 @command('perfchangegroupchangelog', formatteropts +
441 @command('perfchangegroupchangelog', formatteropts +
440 [('', 'version', '02', 'changegroup version'),
442 [('', 'version', '02', 'changegroup version'),
441 ('r', 'rev', '', 'revisions to add to changegroup')])
443 ('r', 'rev', '', 'revisions to add to changegroup')])
442 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
444 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
443 """Benchmark producing a changelog group for a changegroup.
445 """Benchmark producing a changelog group for a changegroup.
444
446
445 This measures the time spent processing the changelog during a
447 This measures the time spent processing the changelog during a
446 bundle operation. This occurs during `hg bundle` and on a server
448 bundle operation. This occurs during `hg bundle` and on a server
447 processing a `getbundle` wire protocol request (handles clones
449 processing a `getbundle` wire protocol request (handles clones
448 and pull requests).
450 and pull requests).
449
451
450 By default, all revisions are added to the changegroup.
452 By default, all revisions are added to the changegroup.
451 """
453 """
452 cl = repo.changelog
454 cl = repo.changelog
453 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
455 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
454 bundler = changegroup.getbundler(version, repo)
456 bundler = changegroup.getbundler(version, repo)
455
457
456 def lookup(node):
458 def lookup(node):
457 # The real bundler reads the revision in order to access the
459 # The real bundler reads the revision in order to access the
458 # manifest node and files list. Do that here.
460 # manifest node and files list. Do that here.
459 cl.read(node)
461 cl.read(node)
460 return node
462 return node
461
463
462 def d():
464 def d():
463 for chunk in bundler.group(revs, cl, lookup):
465 for chunk in bundler.group(revs, cl, lookup):
464 pass
466 pass
465
467
466 timer, fm = gettimer(ui, opts)
468 timer, fm = gettimer(ui, opts)
467 timer(d)
469 timer(d)
468 fm.end()
470 fm.end()
469
471
470 @command('perfdirs', formatteropts)
472 @command('perfdirs', formatteropts)
471 def perfdirs(ui, repo, **opts):
473 def perfdirs(ui, repo, **opts):
472 timer, fm = gettimer(ui, opts)
474 timer, fm = gettimer(ui, opts)
473 dirstate = repo.dirstate
475 dirstate = repo.dirstate
474 'a' in dirstate
476 'a' in dirstate
475 def d():
477 def d():
476 dirstate.dirs()
478 dirstate.dirs()
477 del dirstate._dirs
479 del dirstate._dirs
478 timer(d)
480 timer(d)
479 fm.end()
481 fm.end()
480
482
481 @command('perfdirstate', formatteropts)
483 @command('perfdirstate', formatteropts)
482 def perfdirstate(ui, repo, **opts):
484 def perfdirstate(ui, repo, **opts):
483 timer, fm = gettimer(ui, opts)
485 timer, fm = gettimer(ui, opts)
484 "a" in repo.dirstate
486 "a" in repo.dirstate
485 def d():
487 def d():
486 repo.dirstate.invalidate()
488 repo.dirstate.invalidate()
487 "a" in repo.dirstate
489 "a" in repo.dirstate
488 timer(d)
490 timer(d)
489 fm.end()
491 fm.end()
490
492
491 @command('perfdirstatedirs', formatteropts)
493 @command('perfdirstatedirs', formatteropts)
492 def perfdirstatedirs(ui, repo, **opts):
494 def perfdirstatedirs(ui, repo, **opts):
493 timer, fm = gettimer(ui, opts)
495 timer, fm = gettimer(ui, opts)
494 "a" in repo.dirstate
496 "a" in repo.dirstate
495 def d():
497 def d():
496 "a" in repo.dirstate._dirs
498 "a" in repo.dirstate._dirs
497 del repo.dirstate._dirs
499 del repo.dirstate._dirs
498 timer(d)
500 timer(d)
499 fm.end()
501 fm.end()
500
502
501 @command('perfdirstatefoldmap', formatteropts)
503 @command('perfdirstatefoldmap', formatteropts)
502 def perfdirstatefoldmap(ui, repo, **opts):
504 def perfdirstatefoldmap(ui, repo, **opts):
503 timer, fm = gettimer(ui, opts)
505 timer, fm = gettimer(ui, opts)
504 dirstate = repo.dirstate
506 dirstate = repo.dirstate
505 'a' in dirstate
507 'a' in dirstate
506 def d():
508 def d():
507 dirstate._filefoldmap.get('a')
509 dirstate._filefoldmap.get('a')
508 del dirstate._filefoldmap
510 del dirstate._filefoldmap
509 timer(d)
511 timer(d)
510 fm.end()
512 fm.end()
511
513
512 @command('perfdirfoldmap', formatteropts)
514 @command('perfdirfoldmap', formatteropts)
513 def perfdirfoldmap(ui, repo, **opts):
515 def perfdirfoldmap(ui, repo, **opts):
514 timer, fm = gettimer(ui, opts)
516 timer, fm = gettimer(ui, opts)
515 dirstate = repo.dirstate
517 dirstate = repo.dirstate
516 'a' in dirstate
518 'a' in dirstate
517 def d():
519 def d():
518 dirstate._dirfoldmap.get('a')
520 dirstate._dirfoldmap.get('a')
519 del dirstate._dirfoldmap
521 del dirstate._dirfoldmap
520 del dirstate._dirs
522 del dirstate._dirs
521 timer(d)
523 timer(d)
522 fm.end()
524 fm.end()
523
525
524 @command('perfdirstatewrite', formatteropts)
526 @command('perfdirstatewrite', formatteropts)
525 def perfdirstatewrite(ui, repo, **opts):
527 def perfdirstatewrite(ui, repo, **opts):
526 timer, fm = gettimer(ui, opts)
528 timer, fm = gettimer(ui, opts)
527 ds = repo.dirstate
529 ds = repo.dirstate
528 "a" in ds
530 "a" in ds
529 def d():
531 def d():
530 ds._dirty = True
532 ds._dirty = True
531 ds.write(repo.currenttransaction())
533 ds.write(repo.currenttransaction())
532 timer(d)
534 timer(d)
533 fm.end()
535 fm.end()
534
536
535 @command('perfmergecalculate',
537 @command('perfmergecalculate',
536 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
538 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
537 def perfmergecalculate(ui, repo, rev, **opts):
539 def perfmergecalculate(ui, repo, rev, **opts):
538 timer, fm = gettimer(ui, opts)
540 timer, fm = gettimer(ui, opts)
539 wctx = repo[None]
541 wctx = repo[None]
540 rctx = scmutil.revsingle(repo, rev, rev)
542 rctx = scmutil.revsingle(repo, rev, rev)
541 ancestor = wctx.ancestor(rctx)
543 ancestor = wctx.ancestor(rctx)
542 # we don't want working dir files to be stat'd in the benchmark, so prime
544 # we don't want working dir files to be stat'd in the benchmark, so prime
543 # that cache
545 # that cache
544 wctx.dirty()
546 wctx.dirty()
545 def d():
547 def d():
546 # acceptremote is True because we don't want prompts in the middle of
548 # acceptremote is True because we don't want prompts in the middle of
547 # our benchmark
549 # our benchmark
548 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
550 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
549 acceptremote=True, followcopies=True)
551 acceptremote=True, followcopies=True)
550 timer(d)
552 timer(d)
551 fm.end()
553 fm.end()
552
554
553 @command('perfpathcopies', [], "REV REV")
555 @command('perfpathcopies', [], "REV REV")
554 def perfpathcopies(ui, repo, rev1, rev2, **opts):
556 def perfpathcopies(ui, repo, rev1, rev2, **opts):
555 timer, fm = gettimer(ui, opts)
557 timer, fm = gettimer(ui, opts)
556 ctx1 = scmutil.revsingle(repo, rev1, rev1)
558 ctx1 = scmutil.revsingle(repo, rev1, rev1)
557 ctx2 = scmutil.revsingle(repo, rev2, rev2)
559 ctx2 = scmutil.revsingle(repo, rev2, rev2)
558 def d():
560 def d():
559 copies.pathcopies(ctx1, ctx2)
561 copies.pathcopies(ctx1, ctx2)
560 timer(d)
562 timer(d)
561 fm.end()
563 fm.end()
562
564
563 @command('perfmanifest', [], 'REV')
565 @command('perfmanifest', [], 'REV')
564 def perfmanifest(ui, repo, rev, **opts):
566 def perfmanifest(ui, repo, rev, **opts):
565 timer, fm = gettimer(ui, opts)
567 timer, fm = gettimer(ui, opts)
566 ctx = scmutil.revsingle(repo, rev, rev)
568 ctx = scmutil.revsingle(repo, rev, rev)
567 t = ctx.manifestnode()
569 t = ctx.manifestnode()
568 def d():
570 def d():
569 repo.manifestlog.clearcaches()
571 repo.manifestlog.clearcaches()
570 repo.manifestlog[t].read()
572 repo.manifestlog[t].read()
571 timer(d)
573 timer(d)
572 fm.end()
574 fm.end()
573
575
574 @command('perfchangeset', formatteropts)
576 @command('perfchangeset', formatteropts)
575 def perfchangeset(ui, repo, rev, **opts):
577 def perfchangeset(ui, repo, rev, **opts):
576 timer, fm = gettimer(ui, opts)
578 timer, fm = gettimer(ui, opts)
577 n = repo[rev].node()
579 n = repo[rev].node()
578 def d():
580 def d():
579 repo.changelog.read(n)
581 repo.changelog.read(n)
580 #repo.changelog._cache = None
582 #repo.changelog._cache = None
581 timer(d)
583 timer(d)
582 fm.end()
584 fm.end()
583
585
584 @command('perfindex', formatteropts)
586 @command('perfindex', formatteropts)
585 def perfindex(ui, repo, **opts):
587 def perfindex(ui, repo, **opts):
586 import mercurial.revlog
588 import mercurial.revlog
587 timer, fm = gettimer(ui, opts)
589 timer, fm = gettimer(ui, opts)
588 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
590 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
589 n = repo["tip"].node()
591 n = repo["tip"].node()
590 svfs = getsvfs(repo)
592 svfs = getsvfs(repo)
591 def d():
593 def d():
592 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
594 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
593 cl.rev(n)
595 cl.rev(n)
594 timer(d)
596 timer(d)
595 fm.end()
597 fm.end()
596
598
597 @command('perfstartup', formatteropts)
599 @command('perfstartup', formatteropts)
598 def perfstartup(ui, repo, **opts):
600 def perfstartup(ui, repo, **opts):
599 timer, fm = gettimer(ui, opts)
601 timer, fm = gettimer(ui, opts)
600 cmd = sys.argv[0]
602 cmd = sys.argv[0]
601 def d():
603 def d():
602 if os.name != 'nt':
604 if os.name != 'nt':
603 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
605 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
604 else:
606 else:
605 os.environ['HGRCPATH'] = ''
607 os.environ['HGRCPATH'] = ''
606 os.system("%s version -q > NUL" % cmd)
608 os.system("%s version -q > NUL" % cmd)
607 timer(d)
609 timer(d)
608 fm.end()
610 fm.end()
609
611
610 @command('perfparents', formatteropts)
612 @command('perfparents', formatteropts)
611 def perfparents(ui, repo, **opts):
613 def perfparents(ui, repo, **opts):
612 timer, fm = gettimer(ui, opts)
614 timer, fm = gettimer(ui, opts)
613 # control the number of commits perfparents iterates over
615 # control the number of commits perfparents iterates over
614 # experimental config: perf.parentscount
616 # experimental config: perf.parentscount
615 count = getint(ui, "perf", "parentscount", 1000)
617 count = getint(ui, "perf", "parentscount", 1000)
616 if len(repo.changelog) < count:
618 if len(repo.changelog) < count:
617 raise error.Abort("repo needs %d commits for this test" % count)
619 raise error.Abort("repo needs %d commits for this test" % count)
618 repo = repo.unfiltered()
620 repo = repo.unfiltered()
619 nl = [repo.changelog.node(i) for i in xrange(count)]
621 nl = [repo.changelog.node(i) for i in xrange(count)]
620 def d():
622 def d():
621 for n in nl:
623 for n in nl:
622 repo.changelog.parents(n)
624 repo.changelog.parents(n)
623 timer(d)
625 timer(d)
624 fm.end()
626 fm.end()
625
627
626 @command('perfctxfiles', formatteropts)
628 @command('perfctxfiles', formatteropts)
627 def perfctxfiles(ui, repo, x, **opts):
629 def perfctxfiles(ui, repo, x, **opts):
628 x = int(x)
630 x = int(x)
629 timer, fm = gettimer(ui, opts)
631 timer, fm = gettimer(ui, opts)
630 def d():
632 def d():
631 len(repo[x].files())
633 len(repo[x].files())
632 timer(d)
634 timer(d)
633 fm.end()
635 fm.end()
634
636
635 @command('perfrawfiles', formatteropts)
637 @command('perfrawfiles', formatteropts)
636 def perfrawfiles(ui, repo, x, **opts):
638 def perfrawfiles(ui, repo, x, **opts):
637 x = int(x)
639 x = int(x)
638 timer, fm = gettimer(ui, opts)
640 timer, fm = gettimer(ui, opts)
639 cl = repo.changelog
641 cl = repo.changelog
640 def d():
642 def d():
641 len(cl.read(x)[3])
643 len(cl.read(x)[3])
642 timer(d)
644 timer(d)
643 fm.end()
645 fm.end()
644
646
645 @command('perflookup', formatteropts)
647 @command('perflookup', formatteropts)
646 def perflookup(ui, repo, rev, **opts):
648 def perflookup(ui, repo, rev, **opts):
647 timer, fm = gettimer(ui, opts)
649 timer, fm = gettimer(ui, opts)
648 timer(lambda: len(repo.lookup(rev)))
650 timer(lambda: len(repo.lookup(rev)))
649 fm.end()
651 fm.end()
650
652
651 @command('perfrevrange', formatteropts)
653 @command('perfrevrange', formatteropts)
652 def perfrevrange(ui, repo, *specs, **opts):
654 def perfrevrange(ui, repo, *specs, **opts):
653 timer, fm = gettimer(ui, opts)
655 timer, fm = gettimer(ui, opts)
654 revrange = scmutil.revrange
656 revrange = scmutil.revrange
655 timer(lambda: len(revrange(repo, specs)))
657 timer(lambda: len(revrange(repo, specs)))
656 fm.end()
658 fm.end()
657
659
658 @command('perfnodelookup', formatteropts)
660 @command('perfnodelookup', formatteropts)
659 def perfnodelookup(ui, repo, rev, **opts):
661 def perfnodelookup(ui, repo, rev, **opts):
660 timer, fm = gettimer(ui, opts)
662 timer, fm = gettimer(ui, opts)
661 import mercurial.revlog
663 import mercurial.revlog
662 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
664 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
663 n = repo[rev].node()
665 n = repo[rev].node()
664 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
666 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
665 def d():
667 def d():
666 cl.rev(n)
668 cl.rev(n)
667 clearcaches(cl)
669 clearcaches(cl)
668 timer(d)
670 timer(d)
669 fm.end()
671 fm.end()
670
672
671 @command('perflog',
673 @command('perflog',
672 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
674 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
673 def perflog(ui, repo, rev=None, **opts):
675 def perflog(ui, repo, rev=None, **opts):
674 if rev is None:
676 if rev is None:
675 rev=[]
677 rev=[]
676 timer, fm = gettimer(ui, opts)
678 timer, fm = gettimer(ui, opts)
677 ui.pushbuffer()
679 ui.pushbuffer()
678 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
680 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
679 copies=opts.get('rename')))
681 copies=opts.get('rename')))
680 ui.popbuffer()
682 ui.popbuffer()
681 fm.end()
683 fm.end()
682
684
683 @command('perfmoonwalk', formatteropts)
685 @command('perfmoonwalk', formatteropts)
684 def perfmoonwalk(ui, repo, **opts):
686 def perfmoonwalk(ui, repo, **opts):
685 """benchmark walking the changelog backwards
687 """benchmark walking the changelog backwards
686
688
687 This also loads the changelog data for each revision in the changelog.
689 This also loads the changelog data for each revision in the changelog.
688 """
690 """
689 timer, fm = gettimer(ui, opts)
691 timer, fm = gettimer(ui, opts)
690 def moonwalk():
692 def moonwalk():
691 for i in xrange(len(repo), -1, -1):
693 for i in xrange(len(repo), -1, -1):
692 ctx = repo[i]
694 ctx = repo[i]
693 ctx.branch() # read changelog data (in addition to the index)
695 ctx.branch() # read changelog data (in addition to the index)
694 timer(moonwalk)
696 timer(moonwalk)
695 fm.end()
697 fm.end()
696
698
697 @command('perftemplating', formatteropts)
699 @command('perftemplating', formatteropts)
698 def perftemplating(ui, repo, rev=None, **opts):
700 def perftemplating(ui, repo, rev=None, **opts):
699 if rev is None:
701 if rev is None:
700 rev=[]
702 rev=[]
701 timer, fm = gettimer(ui, opts)
703 timer, fm = gettimer(ui, opts)
702 ui.pushbuffer()
704 ui.pushbuffer()
703 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
705 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
704 template='{date|shortdate} [{rev}:{node|short}]'
706 template='{date|shortdate} [{rev}:{node|short}]'
705 ' {author|person}: {desc|firstline}\n'))
707 ' {author|person}: {desc|firstline}\n'))
706 ui.popbuffer()
708 ui.popbuffer()
707 fm.end()
709 fm.end()
708
710
709 @command('perfcca', formatteropts)
711 @command('perfcca', formatteropts)
710 def perfcca(ui, repo, **opts):
712 def perfcca(ui, repo, **opts):
711 timer, fm = gettimer(ui, opts)
713 timer, fm = gettimer(ui, opts)
712 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
714 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
713 fm.end()
715 fm.end()
714
716
715 @command('perffncacheload', formatteropts)
717 @command('perffncacheload', formatteropts)
716 def perffncacheload(ui, repo, **opts):
718 def perffncacheload(ui, repo, **opts):
717 timer, fm = gettimer(ui, opts)
719 timer, fm = gettimer(ui, opts)
718 s = repo.store
720 s = repo.store
719 def d():
721 def d():
720 s.fncache._load()
722 s.fncache._load()
721 timer(d)
723 timer(d)
722 fm.end()
724 fm.end()
723
725
724 @command('perffncachewrite', formatteropts)
726 @command('perffncachewrite', formatteropts)
725 def perffncachewrite(ui, repo, **opts):
727 def perffncachewrite(ui, repo, **opts):
726 timer, fm = gettimer(ui, opts)
728 timer, fm = gettimer(ui, opts)
727 s = repo.store
729 s = repo.store
728 s.fncache._load()
730 s.fncache._load()
729 lock = repo.lock()
731 lock = repo.lock()
730 tr = repo.transaction('perffncachewrite')
732 tr = repo.transaction('perffncachewrite')
731 def d():
733 def d():
732 s.fncache._dirty = True
734 s.fncache._dirty = True
733 s.fncache.write(tr)
735 s.fncache.write(tr)
734 timer(d)
736 timer(d)
735 tr.close()
737 tr.close()
736 lock.release()
738 lock.release()
737 fm.end()
739 fm.end()
738
740
739 @command('perffncacheencode', formatteropts)
741 @command('perffncacheencode', formatteropts)
740 def perffncacheencode(ui, repo, **opts):
742 def perffncacheencode(ui, repo, **opts):
741 timer, fm = gettimer(ui, opts)
743 timer, fm = gettimer(ui, opts)
742 s = repo.store
744 s = repo.store
743 s.fncache._load()
745 s.fncache._load()
744 def d():
746 def d():
745 for p in s.fncache.entries:
747 for p in s.fncache.entries:
746 s.encode(p)
748 s.encode(p)
747 timer(d)
749 timer(d)
748 fm.end()
750 fm.end()
749
751
750 @command('perfbdiff', revlogopts + formatteropts + [
752 @command('perfbdiff', revlogopts + formatteropts + [
751 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
753 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
752 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
754 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
753 '-c|-m|FILE REV')
755 '-c|-m|FILE REV')
754 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
756 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
755 """benchmark a bdiff between revisions
757 """benchmark a bdiff between revisions
756
758
757 By default, benchmark a bdiff between its delta parent and itself.
759 By default, benchmark a bdiff between its delta parent and itself.
758
760
759 With ``--count``, benchmark bdiffs between delta parents and self for N
761 With ``--count``, benchmark bdiffs between delta parents and self for N
760 revisions starting at the specified revision.
762 revisions starting at the specified revision.
761
763
762 With ``--alldata``, assume the requested revision is a changeset and
764 With ``--alldata``, assume the requested revision is a changeset and
763 measure bdiffs for all changes related to that changeset (manifest
765 measure bdiffs for all changes related to that changeset (manifest
764 and filelogs).
766 and filelogs).
765 """
767 """
766 if opts['alldata']:
768 if opts['alldata']:
767 opts['changelog'] = True
769 opts['changelog'] = True
768
770
769 if opts.get('changelog') or opts.get('manifest'):
771 if opts.get('changelog') or opts.get('manifest'):
770 file_, rev = None, file_
772 file_, rev = None, file_
771 elif rev is None:
773 elif rev is None:
772 raise error.CommandError('perfbdiff', 'invalid arguments')
774 raise error.CommandError('perfbdiff', 'invalid arguments')
773
775
774 textpairs = []
776 textpairs = []
775
777
776 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
778 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
777
779
778 startrev = r.rev(r.lookup(rev))
780 startrev = r.rev(r.lookup(rev))
779 for rev in range(startrev, min(startrev + count, len(r) - 1)):
781 for rev in range(startrev, min(startrev + count, len(r) - 1)):
780 if opts['alldata']:
782 if opts['alldata']:
781 # Load revisions associated with changeset.
783 # Load revisions associated with changeset.
782 ctx = repo[rev]
784 ctx = repo[rev]
783 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
785 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
784 for pctx in ctx.parents():
786 for pctx in ctx.parents():
785 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
787 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
786 textpairs.append((pman, mtext))
788 textpairs.append((pman, mtext))
787
789
788 # Load filelog revisions by iterating manifest delta.
790 # Load filelog revisions by iterating manifest delta.
789 man = ctx.manifest()
791 man = ctx.manifest()
790 pman = ctx.p1().manifest()
792 pman = ctx.p1().manifest()
791 for filename, change in pman.diff(man).items():
793 for filename, change in pman.diff(man).items():
792 fctx = repo.file(filename)
794 fctx = repo.file(filename)
793 f1 = fctx.revision(change[0][0] or -1)
795 f1 = fctx.revision(change[0][0] or -1)
794 f2 = fctx.revision(change[1][0] or -1)
796 f2 = fctx.revision(change[1][0] or -1)
795 textpairs.append((f1, f2))
797 textpairs.append((f1, f2))
796 else:
798 else:
797 dp = r.deltaparent(rev)
799 dp = r.deltaparent(rev)
798 textpairs.append((r.revision(dp), r.revision(rev)))
800 textpairs.append((r.revision(dp), r.revision(rev)))
799
801
800 def d():
802 def d():
801 for pair in textpairs:
803 for pair in textpairs:
802 bdiff.bdiff(*pair)
804 bdiff.bdiff(*pair)
803
805
804 timer, fm = gettimer(ui, opts)
806 timer, fm = gettimer(ui, opts)
805 timer(d)
807 timer(d)
806 fm.end()
808 fm.end()
807
809
808 @command('perfdiffwd', formatteropts)
810 @command('perfdiffwd', formatteropts)
809 def perfdiffwd(ui, repo, **opts):
811 def perfdiffwd(ui, repo, **opts):
810 """Profile diff of working directory changes"""
812 """Profile diff of working directory changes"""
811 timer, fm = gettimer(ui, opts)
813 timer, fm = gettimer(ui, opts)
812 options = {
814 options = {
813 'w': 'ignore_all_space',
815 'w': 'ignore_all_space',
814 'b': 'ignore_space_change',
816 'b': 'ignore_space_change',
815 'B': 'ignore_blank_lines',
817 'B': 'ignore_blank_lines',
816 }
818 }
817
819
818 for diffopt in ('', 'w', 'b', 'B', 'wB'):
820 for diffopt in ('', 'w', 'b', 'B', 'wB'):
819 opts = dict((options[c], '1') for c in diffopt)
821 opts = dict((options[c], '1') for c in diffopt)
820 def d():
822 def d():
821 ui.pushbuffer()
823 ui.pushbuffer()
822 commands.diff(ui, repo, **opts)
824 commands.diff(ui, repo, **opts)
823 ui.popbuffer()
825 ui.popbuffer()
824 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
826 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
825 timer(d, title)
827 timer(d, title)
826 fm.end()
828 fm.end()
827
829
828 @command('perfrevlog', revlogopts + formatteropts +
830 @command('perfrevlog', revlogopts + formatteropts +
829 [('d', 'dist', 100, 'distance between the revisions'),
831 [('d', 'dist', 100, 'distance between the revisions'),
830 ('s', 'startrev', 0, 'revision to start reading at'),
832 ('s', 'startrev', 0, 'revision to start reading at'),
831 ('', 'reverse', False, 'read in reverse')],
833 ('', 'reverse', False, 'read in reverse')],
832 '-c|-m|FILE')
834 '-c|-m|FILE')
833 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
835 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
834 """Benchmark reading a series of revisions from a revlog.
836 """Benchmark reading a series of revisions from a revlog.
835
837
836 By default, we read every ``-d/--dist`` revision from 0 to tip of
838 By default, we read every ``-d/--dist`` revision from 0 to tip of
837 the specified revlog.
839 the specified revlog.
838
840
839 The start revision can be defined via ``-s/--startrev``.
841 The start revision can be defined via ``-s/--startrev``.
840 """
842 """
841 timer, fm = gettimer(ui, opts)
843 timer, fm = gettimer(ui, opts)
842 _len = getlen(ui)
844 _len = getlen(ui)
843
845
844 def d():
846 def d():
845 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
847 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
846
848
847 startrev = 0
849 startrev = 0
848 endrev = _len(r)
850 endrev = _len(r)
849 dist = opts['dist']
851 dist = opts['dist']
850
852
851 if reverse:
853 if reverse:
852 startrev, endrev = endrev, startrev
854 startrev, endrev = endrev, startrev
853 dist = -1 * dist
855 dist = -1 * dist
854
856
855 for x in xrange(startrev, endrev, dist):
857 for x in xrange(startrev, endrev, dist):
856 r.revision(r.node(x))
858 r.revision(r.node(x))
857
859
858 timer(d)
860 timer(d)
859 fm.end()
861 fm.end()
860
862
861 @command('perfrevlogchunks', revlogopts + formatteropts +
863 @command('perfrevlogchunks', revlogopts + formatteropts +
862 [('e', 'engines', '', 'compression engines to use'),
864 [('e', 'engines', '', 'compression engines to use'),
863 ('s', 'startrev', 0, 'revision to start at')],
865 ('s', 'startrev', 0, 'revision to start at')],
864 '-c|-m|FILE')
866 '-c|-m|FILE')
865 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
867 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
866 """Benchmark operations on revlog chunks.
868 """Benchmark operations on revlog chunks.
867
869
868 Logically, each revlog is a collection of fulltext revisions. However,
870 Logically, each revlog is a collection of fulltext revisions. However,
869 stored within each revlog are "chunks" of possibly compressed data. This
871 stored within each revlog are "chunks" of possibly compressed data. This
870 data needs to be read and decompressed or compressed and written.
872 data needs to be read and decompressed or compressed and written.
871
873
872 This command measures the time it takes to read+decompress and recompress
874 This command measures the time it takes to read+decompress and recompress
873 chunks in a revlog. It effectively isolates I/O and compression performance.
875 chunks in a revlog. It effectively isolates I/O and compression performance.
874 For measurements of higher-level operations like resolving revisions,
876 For measurements of higher-level operations like resolving revisions,
875 see ``perfrevlog`` and ``perfrevlogrevision``.
877 see ``perfrevlog`` and ``perfrevlogrevision``.
876 """
878 """
877 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
879 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
878
880
879 # Verify engines argument.
881 # Verify engines argument.
880 if engines:
882 if engines:
881 engines = set(e.strip() for e in engines.split(','))
883 engines = set(e.strip() for e in engines.split(','))
882 for engine in engines:
884 for engine in engines:
883 try:
885 try:
884 util.compressionengines[engine]
886 util.compressionengines[engine]
885 except KeyError:
887 except KeyError:
886 raise error.Abort('unknown compression engine: %s' % engine)
888 raise error.Abort('unknown compression engine: %s' % engine)
887 else:
889 else:
888 engines = []
890 engines = []
889 for e in util.compengines:
891 for e in util.compengines:
890 engine = util.compengines[e]
892 engine = util.compengines[e]
891 try:
893 try:
892 if engine.available():
894 if engine.available():
893 engine.revlogcompressor().compress('dummy')
895 engine.revlogcompressor().compress('dummy')
894 engines.append(e)
896 engines.append(e)
895 except NotImplementedError:
897 except NotImplementedError:
896 pass
898 pass
897
899
898 revs = list(rl.revs(startrev, len(rl) - 1))
900 revs = list(rl.revs(startrev, len(rl) - 1))
899
901
900 def rlfh(rl):
902 def rlfh(rl):
901 if rl._inline:
903 if rl._inline:
902 return getsvfs(repo)(rl.indexfile)
904 return getsvfs(repo)(rl.indexfile)
903 else:
905 else:
904 return getsvfs(repo)(rl.datafile)
906 return getsvfs(repo)(rl.datafile)
905
907
906 def doread():
908 def doread():
907 rl.clearcaches()
909 rl.clearcaches()
908 for rev in revs:
910 for rev in revs:
909 rl._chunkraw(rev, rev)
911 rl._chunkraw(rev, rev)
910
912
911 def doreadcachedfh():
913 def doreadcachedfh():
912 rl.clearcaches()
914 rl.clearcaches()
913 fh = rlfh(rl)
915 fh = rlfh(rl)
914 for rev in revs:
916 for rev in revs:
915 rl._chunkraw(rev, rev, df=fh)
917 rl._chunkraw(rev, rev, df=fh)
916
918
917 def doreadbatch():
919 def doreadbatch():
918 rl.clearcaches()
920 rl.clearcaches()
919 rl._chunkraw(revs[0], revs[-1])
921 rl._chunkraw(revs[0], revs[-1])
920
922
921 def doreadbatchcachedfh():
923 def doreadbatchcachedfh():
922 rl.clearcaches()
924 rl.clearcaches()
923 fh = rlfh(rl)
925 fh = rlfh(rl)
924 rl._chunkraw(revs[0], revs[-1], df=fh)
926 rl._chunkraw(revs[0], revs[-1], df=fh)
925
927
926 def dochunk():
928 def dochunk():
927 rl.clearcaches()
929 rl.clearcaches()
928 fh = rlfh(rl)
930 fh = rlfh(rl)
929 for rev in revs:
931 for rev in revs:
930 rl._chunk(rev, df=fh)
932 rl._chunk(rev, df=fh)
931
933
932 chunks = [None]
934 chunks = [None]
933
935
934 def dochunkbatch():
936 def dochunkbatch():
935 rl.clearcaches()
937 rl.clearcaches()
936 fh = rlfh(rl)
938 fh = rlfh(rl)
937 # Save chunks as a side-effect.
939 # Save chunks as a side-effect.
938 chunks[0] = rl._chunks(revs, df=fh)
940 chunks[0] = rl._chunks(revs, df=fh)
939
941
940 def docompress(compressor):
942 def docompress(compressor):
941 rl.clearcaches()
943 rl.clearcaches()
942
944
943 try:
945 try:
944 # Swap in the requested compression engine.
946 # Swap in the requested compression engine.
945 oldcompressor = rl._compressor
947 oldcompressor = rl._compressor
946 rl._compressor = compressor
948 rl._compressor = compressor
947 for chunk in chunks[0]:
949 for chunk in chunks[0]:
948 rl.compress(chunk)
950 rl.compress(chunk)
949 finally:
951 finally:
950 rl._compressor = oldcompressor
952 rl._compressor = oldcompressor
951
953
952 benches = [
954 benches = [
953 (lambda: doread(), 'read'),
955 (lambda: doread(), 'read'),
954 (lambda: doreadcachedfh(), 'read w/ reused fd'),
956 (lambda: doreadcachedfh(), 'read w/ reused fd'),
955 (lambda: doreadbatch(), 'read batch'),
957 (lambda: doreadbatch(), 'read batch'),
956 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
958 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
957 (lambda: dochunk(), 'chunk'),
959 (lambda: dochunk(), 'chunk'),
958 (lambda: dochunkbatch(), 'chunk batch'),
960 (lambda: dochunkbatch(), 'chunk batch'),
959 ]
961 ]
960
962
961 for engine in sorted(engines):
963 for engine in sorted(engines):
962 compressor = util.compengines[engine].revlogcompressor()
964 compressor = util.compengines[engine].revlogcompressor()
963 benches.append((functools.partial(docompress, compressor),
965 benches.append((functools.partial(docompress, compressor),
964 'compress w/ %s' % engine))
966 'compress w/ %s' % engine))
965
967
966 for fn, title in benches:
968 for fn, title in benches:
967 timer, fm = gettimer(ui, opts)
969 timer, fm = gettimer(ui, opts)
968 timer(fn, title=title)
970 timer(fn, title=title)
969 fm.end()
971 fm.end()
970
972
971 @command('perfrevlogrevision', revlogopts + formatteropts +
973 @command('perfrevlogrevision', revlogopts + formatteropts +
972 [('', 'cache', False, 'use caches instead of clearing')],
974 [('', 'cache', False, 'use caches instead of clearing')],
973 '-c|-m|FILE REV')
975 '-c|-m|FILE REV')
974 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
976 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
975 """Benchmark obtaining a revlog revision.
977 """Benchmark obtaining a revlog revision.
976
978
977 Obtaining a revlog revision consists of roughly the following steps:
979 Obtaining a revlog revision consists of roughly the following steps:
978
980
979 1. Compute the delta chain
981 1. Compute the delta chain
980 2. Obtain the raw chunks for that delta chain
982 2. Obtain the raw chunks for that delta chain
981 3. Decompress each raw chunk
983 3. Decompress each raw chunk
982 4. Apply binary patches to obtain fulltext
984 4. Apply binary patches to obtain fulltext
983 5. Verify hash of fulltext
985 5. Verify hash of fulltext
984
986
985 This command measures the time spent in each of these phases.
987 This command measures the time spent in each of these phases.
986 """
988 """
987 if opts.get('changelog') or opts.get('manifest'):
989 if opts.get('changelog') or opts.get('manifest'):
988 file_, rev = None, file_
990 file_, rev = None, file_
989 elif rev is None:
991 elif rev is None:
990 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
992 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
991
993
992 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
994 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
993 node = r.lookup(rev)
995 node = r.lookup(rev)
994 rev = r.rev(node)
996 rev = r.rev(node)
995
997
996 def getrawchunks(data, chain):
998 def getrawchunks(data, chain):
997 start = r.start
999 start = r.start
998 length = r.length
1000 length = r.length
999 inline = r._inline
1001 inline = r._inline
1000 iosize = r._io.size
1002 iosize = r._io.size
1001 buffer = util.buffer
1003 buffer = util.buffer
1002 offset = start(chain[0])
1004 offset = start(chain[0])
1003
1005
1004 chunks = []
1006 chunks = []
1005 ladd = chunks.append
1007 ladd = chunks.append
1006
1008
1007 for rev in chain:
1009 for rev in chain:
1008 chunkstart = start(rev)
1010 chunkstart = start(rev)
1009 if inline:
1011 if inline:
1010 chunkstart += (rev + 1) * iosize
1012 chunkstart += (rev + 1) * iosize
1011 chunklength = length(rev)
1013 chunklength = length(rev)
1012 ladd(buffer(data, chunkstart - offset, chunklength))
1014 ladd(buffer(data, chunkstart - offset, chunklength))
1013
1015
1014 return chunks
1016 return chunks
1015
1017
1016 def dodeltachain(rev):
1018 def dodeltachain(rev):
1017 if not cache:
1019 if not cache:
1018 r.clearcaches()
1020 r.clearcaches()
1019 r._deltachain(rev)
1021 r._deltachain(rev)
1020
1022
1021 def doread(chain):
1023 def doread(chain):
1022 if not cache:
1024 if not cache:
1023 r.clearcaches()
1025 r.clearcaches()
1024 r._chunkraw(chain[0], chain[-1])
1026 r._chunkraw(chain[0], chain[-1])
1025
1027
1026 def dorawchunks(data, chain):
1028 def dorawchunks(data, chain):
1027 if not cache:
1029 if not cache:
1028 r.clearcaches()
1030 r.clearcaches()
1029 getrawchunks(data, chain)
1031 getrawchunks(data, chain)
1030
1032
1031 def dodecompress(chunks):
1033 def dodecompress(chunks):
1032 decomp = r.decompress
1034 decomp = r.decompress
1033 for chunk in chunks:
1035 for chunk in chunks:
1034 decomp(chunk)
1036 decomp(chunk)
1035
1037
1036 def dopatch(text, bins):
1038 def dopatch(text, bins):
1037 if not cache:
1039 if not cache:
1038 r.clearcaches()
1040 r.clearcaches()
1039 mdiff.patches(text, bins)
1041 mdiff.patches(text, bins)
1040
1042
1041 def dohash(text):
1043 def dohash(text):
1042 if not cache:
1044 if not cache:
1043 r.clearcaches()
1045 r.clearcaches()
1044 r.checkhash(text, node, rev=rev)
1046 r.checkhash(text, node, rev=rev)
1045
1047
1046 def dorevision():
1048 def dorevision():
1047 if not cache:
1049 if not cache:
1048 r.clearcaches()
1050 r.clearcaches()
1049 r.revision(node)
1051 r.revision(node)
1050
1052
1051 chain = r._deltachain(rev)[0]
1053 chain = r._deltachain(rev)[0]
1052 data = r._chunkraw(chain[0], chain[-1])[1]
1054 data = r._chunkraw(chain[0], chain[-1])[1]
1053 rawchunks = getrawchunks(data, chain)
1055 rawchunks = getrawchunks(data, chain)
1054 bins = r._chunks(chain)
1056 bins = r._chunks(chain)
1055 text = str(bins[0])
1057 text = str(bins[0])
1056 bins = bins[1:]
1058 bins = bins[1:]
1057 text = mdiff.patches(text, bins)
1059 text = mdiff.patches(text, bins)
1058
1060
1059 benches = [
1061 benches = [
1060 (lambda: dorevision(), 'full'),
1062 (lambda: dorevision(), 'full'),
1061 (lambda: dodeltachain(rev), 'deltachain'),
1063 (lambda: dodeltachain(rev), 'deltachain'),
1062 (lambda: doread(chain), 'read'),
1064 (lambda: doread(chain), 'read'),
1063 (lambda: dorawchunks(data, chain), 'rawchunks'),
1065 (lambda: dorawchunks(data, chain), 'rawchunks'),
1064 (lambda: dodecompress(rawchunks), 'decompress'),
1066 (lambda: dodecompress(rawchunks), 'decompress'),
1065 (lambda: dopatch(text, bins), 'patch'),
1067 (lambda: dopatch(text, bins), 'patch'),
1066 (lambda: dohash(text), 'hash'),
1068 (lambda: dohash(text), 'hash'),
1067 ]
1069 ]
1068
1070
1069 for fn, title in benches:
1071 for fn, title in benches:
1070 timer, fm = gettimer(ui, opts)
1072 timer, fm = gettimer(ui, opts)
1071 timer(fn, title=title)
1073 timer(fn, title=title)
1072 fm.end()
1074 fm.end()
1073
1075
1074 @command('perfrevset',
1076 @command('perfrevset',
1075 [('C', 'clear', False, 'clear volatile cache between each call.'),
1077 [('C', 'clear', False, 'clear volatile cache between each call.'),
1076 ('', 'contexts', False, 'obtain changectx for each revision')]
1078 ('', 'contexts', False, 'obtain changectx for each revision')]
1077 + formatteropts, "REVSET")
1079 + formatteropts, "REVSET")
1078 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1080 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1079 """benchmark the execution time of a revset
1081 """benchmark the execution time of a revset
1080
1082
1081 Use the --clean option if need to evaluate the impact of build volatile
1083 Use the --clean option if need to evaluate the impact of build volatile
1082 revisions set cache on the revset execution. Volatile cache hold filtered
1084 revisions set cache on the revset execution. Volatile cache hold filtered
1083 and obsolete related cache."""
1085 and obsolete related cache."""
1084 timer, fm = gettimer(ui, opts)
1086 timer, fm = gettimer(ui, opts)
1085 def d():
1087 def d():
1086 if clear:
1088 if clear:
1087 repo.invalidatevolatilesets()
1089 repo.invalidatevolatilesets()
1088 if contexts:
1090 if contexts:
1089 for ctx in repo.set(expr): pass
1091 for ctx in repo.set(expr): pass
1090 else:
1092 else:
1091 for r in repo.revs(expr): pass
1093 for r in repo.revs(expr): pass
1092 timer(d)
1094 timer(d)
1093 fm.end()
1095 fm.end()
1094
1096
1095 @command('perfvolatilesets', formatteropts)
1097 @command('perfvolatilesets', formatteropts)
1096 def perfvolatilesets(ui, repo, *names, **opts):
1098 def perfvolatilesets(ui, repo, *names, **opts):
1097 """benchmark the computation of various volatile set
1099 """benchmark the computation of various volatile set
1098
1100
1099 Volatile set computes element related to filtering and obsolescence."""
1101 Volatile set computes element related to filtering and obsolescence."""
1100 timer, fm = gettimer(ui, opts)
1102 timer, fm = gettimer(ui, opts)
1101 repo = repo.unfiltered()
1103 repo = repo.unfiltered()
1102
1104
1103 def getobs(name):
1105 def getobs(name):
1104 def d():
1106 def d():
1105 repo.invalidatevolatilesets()
1107 repo.invalidatevolatilesets()
1106 obsolete.getrevs(repo, name)
1108 obsolete.getrevs(repo, name)
1107 return d
1109 return d
1108
1110
1109 allobs = sorted(obsolete.cachefuncs)
1111 allobs = sorted(obsolete.cachefuncs)
1110 if names:
1112 if names:
1111 allobs = [n for n in allobs if n in names]
1113 allobs = [n for n in allobs if n in names]
1112
1114
1113 for name in allobs:
1115 for name in allobs:
1114 timer(getobs(name), title=name)
1116 timer(getobs(name), title=name)
1115
1117
1116 def getfiltered(name):
1118 def getfiltered(name):
1117 def d():
1119 def d():
1118 repo.invalidatevolatilesets()
1120 repo.invalidatevolatilesets()
1119 repoview.filterrevs(repo, name)
1121 repoview.filterrevs(repo, name)
1120 return d
1122 return d
1121
1123
1122 allfilter = sorted(repoview.filtertable)
1124 allfilter = sorted(repoview.filtertable)
1123 if names:
1125 if names:
1124 allfilter = [n for n in allfilter if n in names]
1126 allfilter = [n for n in allfilter if n in names]
1125
1127
1126 for name in allfilter:
1128 for name in allfilter:
1127 timer(getfiltered(name), title=name)
1129 timer(getfiltered(name), title=name)
1128 fm.end()
1130 fm.end()
1129
1131
1130 @command('perfbranchmap',
1132 @command('perfbranchmap',
1131 [('f', 'full', False,
1133 [('f', 'full', False,
1132 'Includes build time of subset'),
1134 'Includes build time of subset'),
1133 ] + formatteropts)
1135 ] + formatteropts)
1134 def perfbranchmap(ui, repo, full=False, **opts):
1136 def perfbranchmap(ui, repo, full=False, **opts):
1135 """benchmark the update of a branchmap
1137 """benchmark the update of a branchmap
1136
1138
1137 This benchmarks the full repo.branchmap() call with read and write disabled
1139 This benchmarks the full repo.branchmap() call with read and write disabled
1138 """
1140 """
1139 timer, fm = gettimer(ui, opts)
1141 timer, fm = gettimer(ui, opts)
1140 def getbranchmap(filtername):
1142 def getbranchmap(filtername):
1141 """generate a benchmark function for the filtername"""
1143 """generate a benchmark function for the filtername"""
1142 if filtername is None:
1144 if filtername is None:
1143 view = repo
1145 view = repo
1144 else:
1146 else:
1145 view = repo.filtered(filtername)
1147 view = repo.filtered(filtername)
1146 def d():
1148 def d():
1147 if full:
1149 if full:
1148 view._branchcaches.clear()
1150 view._branchcaches.clear()
1149 else:
1151 else:
1150 view._branchcaches.pop(filtername, None)
1152 view._branchcaches.pop(filtername, None)
1151 view.branchmap()
1153 view.branchmap()
1152 return d
1154 return d
1153 # add filter in smaller subset to bigger subset
1155 # add filter in smaller subset to bigger subset
1154 possiblefilters = set(repoview.filtertable)
1156 possiblefilters = set(repoview.filtertable)
1155 subsettable = getbranchmapsubsettable()
1157 subsettable = getbranchmapsubsettable()
1156 allfilters = []
1158 allfilters = []
1157 while possiblefilters:
1159 while possiblefilters:
1158 for name in possiblefilters:
1160 for name in possiblefilters:
1159 subset = subsettable.get(name)
1161 subset = subsettable.get(name)
1160 if subset not in possiblefilters:
1162 if subset not in possiblefilters:
1161 break
1163 break
1162 else:
1164 else:
1163 assert False, 'subset cycle %s!' % possiblefilters
1165 assert False, 'subset cycle %s!' % possiblefilters
1164 allfilters.append(name)
1166 allfilters.append(name)
1165 possiblefilters.remove(name)
1167 possiblefilters.remove(name)
1166
1168
1167 # warm the cache
1169 # warm the cache
1168 if not full:
1170 if not full:
1169 for name in allfilters:
1171 for name in allfilters:
1170 repo.filtered(name).branchmap()
1172 repo.filtered(name).branchmap()
1171 # add unfiltered
1173 # add unfiltered
1172 allfilters.append(None)
1174 allfilters.append(None)
1173
1175
1174 branchcacheread = safeattrsetter(branchmap, 'read')
1176 branchcacheread = safeattrsetter(branchmap, 'read')
1175 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1177 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1176 branchcacheread.set(lambda repo: None)
1178 branchcacheread.set(lambda repo: None)
1177 branchcachewrite.set(lambda bc, repo: None)
1179 branchcachewrite.set(lambda bc, repo: None)
1178 try:
1180 try:
1179 for name in allfilters:
1181 for name in allfilters:
1180 timer(getbranchmap(name), title=str(name))
1182 timer(getbranchmap(name), title=str(name))
1181 finally:
1183 finally:
1182 branchcacheread.restore()
1184 branchcacheread.restore()
1183 branchcachewrite.restore()
1185 branchcachewrite.restore()
1184 fm.end()
1186 fm.end()
1185
1187
1186 @command('perfloadmarkers')
1188 @command('perfloadmarkers')
1187 def perfloadmarkers(ui, repo):
1189 def perfloadmarkers(ui, repo):
1188 """benchmark the time to parse the on-disk markers for a repo
1190 """benchmark the time to parse the on-disk markers for a repo
1189
1191
1190 Result is the number of markers in the repo."""
1192 Result is the number of markers in the repo."""
1191 timer, fm = gettimer(ui)
1193 timer, fm = gettimer(ui)
1192 svfs = getsvfs(repo)
1194 svfs = getsvfs(repo)
1193 timer(lambda: len(obsolete.obsstore(svfs)))
1195 timer(lambda: len(obsolete.obsstore(svfs)))
1194 fm.end()
1196 fm.end()
1195
1197
1196 @command('perflrucachedict', formatteropts +
1198 @command('perflrucachedict', formatteropts +
1197 [('', 'size', 4, 'size of cache'),
1199 [('', 'size', 4, 'size of cache'),
1198 ('', 'gets', 10000, 'number of key lookups'),
1200 ('', 'gets', 10000, 'number of key lookups'),
1199 ('', 'sets', 10000, 'number of key sets'),
1201 ('', 'sets', 10000, 'number of key sets'),
1200 ('', 'mixed', 10000, 'number of mixed mode operations'),
1202 ('', 'mixed', 10000, 'number of mixed mode operations'),
1201 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1203 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1202 norepo=True)
1204 norepo=True)
1203 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1205 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1204 mixedgetfreq=50, **opts):
1206 mixedgetfreq=50, **opts):
1205 def doinit():
1207 def doinit():
1206 for i in xrange(10000):
1208 for i in xrange(10000):
1207 util.lrucachedict(size)
1209 util.lrucachedict(size)
1208
1210
1209 values = []
1211 values = []
1210 for i in xrange(size):
1212 for i in xrange(size):
1211 values.append(random.randint(0, sys.maxint))
1213 values.append(random.randint(0, sys.maxint))
1212
1214
1213 # Get mode fills the cache and tests raw lookup performance with no
1215 # Get mode fills the cache and tests raw lookup performance with no
1214 # eviction.
1216 # eviction.
1215 getseq = []
1217 getseq = []
1216 for i in xrange(gets):
1218 for i in xrange(gets):
1217 getseq.append(random.choice(values))
1219 getseq.append(random.choice(values))
1218
1220
1219 def dogets():
1221 def dogets():
1220 d = util.lrucachedict(size)
1222 d = util.lrucachedict(size)
1221 for v in values:
1223 for v in values:
1222 d[v] = v
1224 d[v] = v
1223 for key in getseq:
1225 for key in getseq:
1224 value = d[key]
1226 value = d[key]
1225 value # silence pyflakes warning
1227 value # silence pyflakes warning
1226
1228
1227 # Set mode tests insertion speed with cache eviction.
1229 # Set mode tests insertion speed with cache eviction.
1228 setseq = []
1230 setseq = []
1229 for i in xrange(sets):
1231 for i in xrange(sets):
1230 setseq.append(random.randint(0, sys.maxint))
1232 setseq.append(random.randint(0, sys.maxint))
1231
1233
1232 def dosets():
1234 def dosets():
1233 d = util.lrucachedict(size)
1235 d = util.lrucachedict(size)
1234 for v in setseq:
1236 for v in setseq:
1235 d[v] = v
1237 d[v] = v
1236
1238
1237 # Mixed mode randomly performs gets and sets with eviction.
1239 # Mixed mode randomly performs gets and sets with eviction.
1238 mixedops = []
1240 mixedops = []
1239 for i in xrange(mixed):
1241 for i in xrange(mixed):
1240 r = random.randint(0, 100)
1242 r = random.randint(0, 100)
1241 if r < mixedgetfreq:
1243 if r < mixedgetfreq:
1242 op = 0
1244 op = 0
1243 else:
1245 else:
1244 op = 1
1246 op = 1
1245
1247
1246 mixedops.append((op, random.randint(0, size * 2)))
1248 mixedops.append((op, random.randint(0, size * 2)))
1247
1249
1248 def domixed():
1250 def domixed():
1249 d = util.lrucachedict(size)
1251 d = util.lrucachedict(size)
1250
1252
1251 for op, v in mixedops:
1253 for op, v in mixedops:
1252 if op == 0:
1254 if op == 0:
1253 try:
1255 try:
1254 d[v]
1256 d[v]
1255 except KeyError:
1257 except KeyError:
1256 pass
1258 pass
1257 else:
1259 else:
1258 d[v] = v
1260 d[v] = v
1259
1261
1260 benches = [
1262 benches = [
1261 (doinit, 'init'),
1263 (doinit, 'init'),
1262 (dogets, 'gets'),
1264 (dogets, 'gets'),
1263 (dosets, 'sets'),
1265 (dosets, 'sets'),
1264 (domixed, 'mixed')
1266 (domixed, 'mixed')
1265 ]
1267 ]
1266
1268
1267 for fn, title in benches:
1269 for fn, title in benches:
1268 timer, fm = gettimer(ui, opts)
1270 timer, fm = gettimer(ui, opts)
1269 timer(fn, title=title)
1271 timer(fn, title=title)
1270 fm.end()
1272 fm.end()
1271
1273
1272 @command('perfwrite', formatteropts)
1274 @command('perfwrite', formatteropts)
1273 def perfwrite(ui, repo, **opts):
1275 def perfwrite(ui, repo, **opts):
1274 """microbenchmark ui.write
1276 """microbenchmark ui.write
1275 """
1277 """
1276 timer, fm = gettimer(ui, opts)
1278 timer, fm = gettimer(ui, opts)
1277 def write():
1279 def write():
1278 for i in range(100000):
1280 for i in range(100000):
1279 ui.write(('Testing write performance\n'))
1281 ui.write(('Testing write performance\n'))
1280 timer(write)
1282 timer(write)
1281 fm.end()
1283 fm.end()
1282
1284
1283 def uisetup(ui):
1285 def uisetup(ui):
1284 if (util.safehasattr(cmdutil, 'openrevlog') and
1286 if (util.safehasattr(cmdutil, 'openrevlog') and
1285 not util.safehasattr(commands, 'debugrevlogopts')):
1287 not util.safehasattr(commands, 'debugrevlogopts')):
1286 # for "historical portability":
1288 # for "historical portability":
1287 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1289 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1288 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1290 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1289 # openrevlog() should cause failure, because it has been
1291 # openrevlog() should cause failure, because it has been
1290 # available since 3.5 (or 49c583ca48c4).
1292 # available since 3.5 (or 49c583ca48c4).
1291 def openrevlog(orig, repo, cmd, file_, opts):
1293 def openrevlog(orig, repo, cmd, file_, opts):
1292 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1294 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1293 raise error.Abort("This version doesn't support --dir option",
1295 raise error.Abort("This version doesn't support --dir option",
1294 hint="use 3.5 or later")
1296 hint="use 3.5 or later")
1295 return orig(repo, cmd, file_, opts)
1297 return orig(repo, cmd, file_, opts)
1296 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1298 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now