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