##// END OF EJS Templates
perf: support multiple compression engines in perfrevlogchunks...
Gregory Szorc -
r30796:168ef0a4 default
parent child Browse files
Show More
@@ -1,1240 +1,1272 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 [('s', 'startrev', 0, 'revision to start at')],
862 [('e', 'engines', '', 'compression engines to use'),
863 ('s', 'startrev', 0, 'revision to start at')],
863 '-c|-m|FILE')
864 '-c|-m|FILE')
864 def perfrevlogchunks(ui, repo, file_=None, startrev=0, **opts):
865 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
865 """Benchmark operations on revlog chunks.
866 """Benchmark operations on revlog chunks.
866
867
867 Logically, each revlog is a collection of fulltext revisions. However,
868 Logically, each revlog is a collection of fulltext revisions. However,
868 stored within each revlog are "chunks" of possibly compressed data. This
869 stored within each revlog are "chunks" of possibly compressed data. This
869 data needs to be read and decompressed or compressed and written.
870 data needs to be read and decompressed or compressed and written.
870
871
871 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
872 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.
873 For measurements of higher-level operations like resolving revisions,
874 For measurements of higher-level operations like resolving revisions,
874 see ``perfrevlog`` and ``perfrevlogrevision``.
875 see ``perfrevlog`` and ``perfrevlogrevision``.
875 """
876 """
876 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
877 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
878
879 # Verify engines argument.
880 if engines:
881 engines = set(e.strip() for e in engines.split(','))
882 for engine in engines:
883 try:
884 util.compressionengines[engine]
885 except KeyError:
886 raise error.Abort('unknown compression engine: %s' % engine)
887 else:
888 engines = []
889 for e in util.compengines:
890 engine = util.compengines[e]
891 try:
892 if engine.available():
893 engine.revlogcompressor().compress('dummy')
894 engines.append(e)
895 except NotImplementedError:
896 pass
897
877 revs = list(rl.revs(startrev, len(rl) - 1))
898 revs = list(rl.revs(startrev, len(rl) - 1))
878
899
879 def rlfh(rl):
900 def rlfh(rl):
880 if rl._inline:
901 if rl._inline:
881 return getsvfs(repo)(rl.indexfile)
902 return getsvfs(repo)(rl.indexfile)
882 else:
903 else:
883 return getsvfs(repo)(rl.datafile)
904 return getsvfs(repo)(rl.datafile)
884
905
885 def doread():
906 def doread():
886 rl.clearcaches()
907 rl.clearcaches()
887 for rev in revs:
908 for rev in revs:
888 rl._chunkraw(rev, rev)
909 rl._chunkraw(rev, rev)
889
910
890 def doreadcachedfh():
911 def doreadcachedfh():
891 rl.clearcaches()
912 rl.clearcaches()
892 fh = rlfh(rl)
913 fh = rlfh(rl)
893 for rev in revs:
914 for rev in revs:
894 rl._chunkraw(rev, rev, df=fh)
915 rl._chunkraw(rev, rev, df=fh)
895
916
896 def doreadbatch():
917 def doreadbatch():
897 rl.clearcaches()
918 rl.clearcaches()
898 rl._chunkraw(revs[0], revs[-1])
919 rl._chunkraw(revs[0], revs[-1])
899
920
900 def doreadbatchcachedfh():
921 def doreadbatchcachedfh():
901 rl.clearcaches()
922 rl.clearcaches()
902 fh = rlfh(rl)
923 fh = rlfh(rl)
903 rl._chunkraw(revs[0], revs[-1], df=fh)
924 rl._chunkraw(revs[0], revs[-1], df=fh)
904
925
905 def dochunk():
926 def dochunk():
906 rl.clearcaches()
927 rl.clearcaches()
907 fh = rlfh(rl)
928 fh = rlfh(rl)
908 for rev in revs:
929 for rev in revs:
909 rl._chunk(rev, df=fh)
930 rl._chunk(rev, df=fh)
910
931
911 chunks = [None]
932 chunks = [None]
912
933
913 def dochunkbatch():
934 def dochunkbatch():
914 rl.clearcaches()
935 rl.clearcaches()
915 fh = rlfh(rl)
936 fh = rlfh(rl)
916 # Save chunks as a side-effect.
937 # Save chunks as a side-effect.
917 chunks[0] = rl._chunks(revs, df=fh)
938 chunks[0] = rl._chunks(revs, df=fh)
918
939
919 def docompress():
940 def docompress(compressor):
920 rl.clearcaches()
941 rl.clearcaches()
942
943 try:
944 # Swap in the requested compression engine.
945 oldcompressor = rl._compressor
946 rl._compressor = compressor
921 for chunk in chunks[0]:
947 for chunk in chunks[0]:
922 rl.compress(chunk)
948 rl.compress(chunk)
949 finally:
950 rl._compressor = oldcompressor
923
951
924 benches = [
952 benches = [
925 (lambda: doread(), 'read'),
953 (lambda: doread(), 'read'),
926 (lambda: doreadcachedfh(), 'read w/ reused fd'),
954 (lambda: doreadcachedfh(), 'read w/ reused fd'),
927 (lambda: doreadbatch(), 'read batch'),
955 (lambda: doreadbatch(), 'read batch'),
928 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
956 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
929 (lambda: dochunk(), 'chunk'),
957 (lambda: dochunk(), 'chunk'),
930 (lambda: dochunkbatch(), 'chunk batch'),
958 (lambda: dochunkbatch(), 'chunk batch'),
931 (lambda: docompress(), 'compress'),
932 ]
959 ]
933
960
961 for engine in sorted(engines):
962 compressor = util.compengines[engine].revlogcompressor()
963 benches.append((functools.partial(docompress, compressor),
964 'compress w/ %s' % engine))
965
934 for fn, title in benches:
966 for fn, title in benches:
935 timer, fm = gettimer(ui, opts)
967 timer, fm = gettimer(ui, opts)
936 timer(fn, title=title)
968 timer(fn, title=title)
937 fm.end()
969 fm.end()
938
970
939 @command('perfrevlogrevision', revlogopts + formatteropts +
971 @command('perfrevlogrevision', revlogopts + formatteropts +
940 [('', 'cache', False, 'use caches instead of clearing')],
972 [('', 'cache', False, 'use caches instead of clearing')],
941 '-c|-m|FILE REV')
973 '-c|-m|FILE REV')
942 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
974 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
943 """Benchmark obtaining a revlog revision.
975 """Benchmark obtaining a revlog revision.
944
976
945 Obtaining a revlog revision consists of roughly the following steps:
977 Obtaining a revlog revision consists of roughly the following steps:
946
978
947 1. Compute the delta chain
979 1. Compute the delta chain
948 2. Obtain the raw chunks for that delta chain
980 2. Obtain the raw chunks for that delta chain
949 3. Decompress each raw chunk
981 3. Decompress each raw chunk
950 4. Apply binary patches to obtain fulltext
982 4. Apply binary patches to obtain fulltext
951 5. Verify hash of fulltext
983 5. Verify hash of fulltext
952
984
953 This command measures the time spent in each of these phases.
985 This command measures the time spent in each of these phases.
954 """
986 """
955 if opts.get('changelog') or opts.get('manifest'):
987 if opts.get('changelog') or opts.get('manifest'):
956 file_, rev = None, file_
988 file_, rev = None, file_
957 elif rev is None:
989 elif rev is None:
958 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
990 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
959
991
960 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
992 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
961 node = r.lookup(rev)
993 node = r.lookup(rev)
962 rev = r.rev(node)
994 rev = r.rev(node)
963
995
964 def dodeltachain(rev):
996 def dodeltachain(rev):
965 if not cache:
997 if not cache:
966 r.clearcaches()
998 r.clearcaches()
967 r._deltachain(rev)
999 r._deltachain(rev)
968
1000
969 def doread(chain):
1001 def doread(chain):
970 if not cache:
1002 if not cache:
971 r.clearcaches()
1003 r.clearcaches()
972 r._chunkraw(chain[0], chain[-1])
1004 r._chunkraw(chain[0], chain[-1])
973
1005
974 def dodecompress(data, chain):
1006 def dodecompress(data, chain):
975 if not cache:
1007 if not cache:
976 r.clearcaches()
1008 r.clearcaches()
977
1009
978 start = r.start
1010 start = r.start
979 length = r.length
1011 length = r.length
980 inline = r._inline
1012 inline = r._inline
981 iosize = r._io.size
1013 iosize = r._io.size
982 buffer = util.buffer
1014 buffer = util.buffer
983 offset = start(chain[0])
1015 offset = start(chain[0])
984
1016
985 for rev in chain:
1017 for rev in chain:
986 chunkstart = start(rev)
1018 chunkstart = start(rev)
987 if inline:
1019 if inline:
988 chunkstart += (rev + 1) * iosize
1020 chunkstart += (rev + 1) * iosize
989 chunklength = length(rev)
1021 chunklength = length(rev)
990 b = buffer(data, chunkstart - offset, chunklength)
1022 b = buffer(data, chunkstart - offset, chunklength)
991 r.decompress(b)
1023 r.decompress(b)
992
1024
993 def dopatch(text, bins):
1025 def dopatch(text, bins):
994 if not cache:
1026 if not cache:
995 r.clearcaches()
1027 r.clearcaches()
996 mdiff.patches(text, bins)
1028 mdiff.patches(text, bins)
997
1029
998 def dohash(text):
1030 def dohash(text):
999 if not cache:
1031 if not cache:
1000 r.clearcaches()
1032 r.clearcaches()
1001 r.checkhash(text, node, rev=rev)
1033 r.checkhash(text, node, rev=rev)
1002
1034
1003 def dorevision():
1035 def dorevision():
1004 if not cache:
1036 if not cache:
1005 r.clearcaches()
1037 r.clearcaches()
1006 r.revision(node)
1038 r.revision(node)
1007
1039
1008 chain = r._deltachain(rev)[0]
1040 chain = r._deltachain(rev)[0]
1009 data = r._chunkraw(chain[0], chain[-1])[1]
1041 data = r._chunkraw(chain[0], chain[-1])[1]
1010 bins = r._chunks(chain)
1042 bins = r._chunks(chain)
1011 text = str(bins[0])
1043 text = str(bins[0])
1012 bins = bins[1:]
1044 bins = bins[1:]
1013 text = mdiff.patches(text, bins)
1045 text = mdiff.patches(text, bins)
1014
1046
1015 benches = [
1047 benches = [
1016 (lambda: dorevision(), 'full'),
1048 (lambda: dorevision(), 'full'),
1017 (lambda: dodeltachain(rev), 'deltachain'),
1049 (lambda: dodeltachain(rev), 'deltachain'),
1018 (lambda: doread(chain), 'read'),
1050 (lambda: doread(chain), 'read'),
1019 (lambda: dodecompress(data, chain), 'decompress'),
1051 (lambda: dodecompress(data, chain), 'decompress'),
1020 (lambda: dopatch(text, bins), 'patch'),
1052 (lambda: dopatch(text, bins), 'patch'),
1021 (lambda: dohash(text), 'hash'),
1053 (lambda: dohash(text), 'hash'),
1022 ]
1054 ]
1023
1055
1024 for fn, title in benches:
1056 for fn, title in benches:
1025 timer, fm = gettimer(ui, opts)
1057 timer, fm = gettimer(ui, opts)
1026 timer(fn, title=title)
1058 timer(fn, title=title)
1027 fm.end()
1059 fm.end()
1028
1060
1029 @command('perfrevset',
1061 @command('perfrevset',
1030 [('C', 'clear', False, 'clear volatile cache between each call.'),
1062 [('C', 'clear', False, 'clear volatile cache between each call.'),
1031 ('', 'contexts', False, 'obtain changectx for each revision')]
1063 ('', 'contexts', False, 'obtain changectx for each revision')]
1032 + formatteropts, "REVSET")
1064 + formatteropts, "REVSET")
1033 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1065 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1034 """benchmark the execution time of a revset
1066 """benchmark the execution time of a revset
1035
1067
1036 Use the --clean option if need to evaluate the impact of build volatile
1068 Use the --clean option if need to evaluate the impact of build volatile
1037 revisions set cache on the revset execution. Volatile cache hold filtered
1069 revisions set cache on the revset execution. Volatile cache hold filtered
1038 and obsolete related cache."""
1070 and obsolete related cache."""
1039 timer, fm = gettimer(ui, opts)
1071 timer, fm = gettimer(ui, opts)
1040 def d():
1072 def d():
1041 if clear:
1073 if clear:
1042 repo.invalidatevolatilesets()
1074 repo.invalidatevolatilesets()
1043 if contexts:
1075 if contexts:
1044 for ctx in repo.set(expr): pass
1076 for ctx in repo.set(expr): pass
1045 else:
1077 else:
1046 for r in repo.revs(expr): pass
1078 for r in repo.revs(expr): pass
1047 timer(d)
1079 timer(d)
1048 fm.end()
1080 fm.end()
1049
1081
1050 @command('perfvolatilesets', formatteropts)
1082 @command('perfvolatilesets', formatteropts)
1051 def perfvolatilesets(ui, repo, *names, **opts):
1083 def perfvolatilesets(ui, repo, *names, **opts):
1052 """benchmark the computation of various volatile set
1084 """benchmark the computation of various volatile set
1053
1085
1054 Volatile set computes element related to filtering and obsolescence."""
1086 Volatile set computes element related to filtering and obsolescence."""
1055 timer, fm = gettimer(ui, opts)
1087 timer, fm = gettimer(ui, opts)
1056 repo = repo.unfiltered()
1088 repo = repo.unfiltered()
1057
1089
1058 def getobs(name):
1090 def getobs(name):
1059 def d():
1091 def d():
1060 repo.invalidatevolatilesets()
1092 repo.invalidatevolatilesets()
1061 obsolete.getrevs(repo, name)
1093 obsolete.getrevs(repo, name)
1062 return d
1094 return d
1063
1095
1064 allobs = sorted(obsolete.cachefuncs)
1096 allobs = sorted(obsolete.cachefuncs)
1065 if names:
1097 if names:
1066 allobs = [n for n in allobs if n in names]
1098 allobs = [n for n in allobs if n in names]
1067
1099
1068 for name in allobs:
1100 for name in allobs:
1069 timer(getobs(name), title=name)
1101 timer(getobs(name), title=name)
1070
1102
1071 def getfiltered(name):
1103 def getfiltered(name):
1072 def d():
1104 def d():
1073 repo.invalidatevolatilesets()
1105 repo.invalidatevolatilesets()
1074 repoview.filterrevs(repo, name)
1106 repoview.filterrevs(repo, name)
1075 return d
1107 return d
1076
1108
1077 allfilter = sorted(repoview.filtertable)
1109 allfilter = sorted(repoview.filtertable)
1078 if names:
1110 if names:
1079 allfilter = [n for n in allfilter if n in names]
1111 allfilter = [n for n in allfilter if n in names]
1080
1112
1081 for name in allfilter:
1113 for name in allfilter:
1082 timer(getfiltered(name), title=name)
1114 timer(getfiltered(name), title=name)
1083 fm.end()
1115 fm.end()
1084
1116
1085 @command('perfbranchmap',
1117 @command('perfbranchmap',
1086 [('f', 'full', False,
1118 [('f', 'full', False,
1087 'Includes build time of subset'),
1119 'Includes build time of subset'),
1088 ] + formatteropts)
1120 ] + formatteropts)
1089 def perfbranchmap(ui, repo, full=False, **opts):
1121 def perfbranchmap(ui, repo, full=False, **opts):
1090 """benchmark the update of a branchmap
1122 """benchmark the update of a branchmap
1091
1123
1092 This benchmarks the full repo.branchmap() call with read and write disabled
1124 This benchmarks the full repo.branchmap() call with read and write disabled
1093 """
1125 """
1094 timer, fm = gettimer(ui, opts)
1126 timer, fm = gettimer(ui, opts)
1095 def getbranchmap(filtername):
1127 def getbranchmap(filtername):
1096 """generate a benchmark function for the filtername"""
1128 """generate a benchmark function for the filtername"""
1097 if filtername is None:
1129 if filtername is None:
1098 view = repo
1130 view = repo
1099 else:
1131 else:
1100 view = repo.filtered(filtername)
1132 view = repo.filtered(filtername)
1101 def d():
1133 def d():
1102 if full:
1134 if full:
1103 view._branchcaches.clear()
1135 view._branchcaches.clear()
1104 else:
1136 else:
1105 view._branchcaches.pop(filtername, None)
1137 view._branchcaches.pop(filtername, None)
1106 view.branchmap()
1138 view.branchmap()
1107 return d
1139 return d
1108 # add filter in smaller subset to bigger subset
1140 # add filter in smaller subset to bigger subset
1109 possiblefilters = set(repoview.filtertable)
1141 possiblefilters = set(repoview.filtertable)
1110 subsettable = getbranchmapsubsettable()
1142 subsettable = getbranchmapsubsettable()
1111 allfilters = []
1143 allfilters = []
1112 while possiblefilters:
1144 while possiblefilters:
1113 for name in possiblefilters:
1145 for name in possiblefilters:
1114 subset = subsettable.get(name)
1146 subset = subsettable.get(name)
1115 if subset not in possiblefilters:
1147 if subset not in possiblefilters:
1116 break
1148 break
1117 else:
1149 else:
1118 assert False, 'subset cycle %s!' % possiblefilters
1150 assert False, 'subset cycle %s!' % possiblefilters
1119 allfilters.append(name)
1151 allfilters.append(name)
1120 possiblefilters.remove(name)
1152 possiblefilters.remove(name)
1121
1153
1122 # warm the cache
1154 # warm the cache
1123 if not full:
1155 if not full:
1124 for name in allfilters:
1156 for name in allfilters:
1125 repo.filtered(name).branchmap()
1157 repo.filtered(name).branchmap()
1126 # add unfiltered
1158 # add unfiltered
1127 allfilters.append(None)
1159 allfilters.append(None)
1128
1160
1129 branchcacheread = safeattrsetter(branchmap, 'read')
1161 branchcacheread = safeattrsetter(branchmap, 'read')
1130 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1162 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1131 branchcacheread.set(lambda repo: None)
1163 branchcacheread.set(lambda repo: None)
1132 branchcachewrite.set(lambda bc, repo: None)
1164 branchcachewrite.set(lambda bc, repo: None)
1133 try:
1165 try:
1134 for name in allfilters:
1166 for name in allfilters:
1135 timer(getbranchmap(name), title=str(name))
1167 timer(getbranchmap(name), title=str(name))
1136 finally:
1168 finally:
1137 branchcacheread.restore()
1169 branchcacheread.restore()
1138 branchcachewrite.restore()
1170 branchcachewrite.restore()
1139 fm.end()
1171 fm.end()
1140
1172
1141 @command('perfloadmarkers')
1173 @command('perfloadmarkers')
1142 def perfloadmarkers(ui, repo):
1174 def perfloadmarkers(ui, repo):
1143 """benchmark the time to parse the on-disk markers for a repo
1175 """benchmark the time to parse the on-disk markers for a repo
1144
1176
1145 Result is the number of markers in the repo."""
1177 Result is the number of markers in the repo."""
1146 timer, fm = gettimer(ui)
1178 timer, fm = gettimer(ui)
1147 svfs = getsvfs(repo)
1179 svfs = getsvfs(repo)
1148 timer(lambda: len(obsolete.obsstore(svfs)))
1180 timer(lambda: len(obsolete.obsstore(svfs)))
1149 fm.end()
1181 fm.end()
1150
1182
1151 @command('perflrucachedict', formatteropts +
1183 @command('perflrucachedict', formatteropts +
1152 [('', 'size', 4, 'size of cache'),
1184 [('', 'size', 4, 'size of cache'),
1153 ('', 'gets', 10000, 'number of key lookups'),
1185 ('', 'gets', 10000, 'number of key lookups'),
1154 ('', 'sets', 10000, 'number of key sets'),
1186 ('', 'sets', 10000, 'number of key sets'),
1155 ('', 'mixed', 10000, 'number of mixed mode operations'),
1187 ('', 'mixed', 10000, 'number of mixed mode operations'),
1156 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1188 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1157 norepo=True)
1189 norepo=True)
1158 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1190 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1159 mixedgetfreq=50, **opts):
1191 mixedgetfreq=50, **opts):
1160 def doinit():
1192 def doinit():
1161 for i in xrange(10000):
1193 for i in xrange(10000):
1162 util.lrucachedict(size)
1194 util.lrucachedict(size)
1163
1195
1164 values = []
1196 values = []
1165 for i in xrange(size):
1197 for i in xrange(size):
1166 values.append(random.randint(0, sys.maxint))
1198 values.append(random.randint(0, sys.maxint))
1167
1199
1168 # Get mode fills the cache and tests raw lookup performance with no
1200 # Get mode fills the cache and tests raw lookup performance with no
1169 # eviction.
1201 # eviction.
1170 getseq = []
1202 getseq = []
1171 for i in xrange(gets):
1203 for i in xrange(gets):
1172 getseq.append(random.choice(values))
1204 getseq.append(random.choice(values))
1173
1205
1174 def dogets():
1206 def dogets():
1175 d = util.lrucachedict(size)
1207 d = util.lrucachedict(size)
1176 for v in values:
1208 for v in values:
1177 d[v] = v
1209 d[v] = v
1178 for key in getseq:
1210 for key in getseq:
1179 value = d[key]
1211 value = d[key]
1180 value # silence pyflakes warning
1212 value # silence pyflakes warning
1181
1213
1182 # Set mode tests insertion speed with cache eviction.
1214 # Set mode tests insertion speed with cache eviction.
1183 setseq = []
1215 setseq = []
1184 for i in xrange(sets):
1216 for i in xrange(sets):
1185 setseq.append(random.randint(0, sys.maxint))
1217 setseq.append(random.randint(0, sys.maxint))
1186
1218
1187 def dosets():
1219 def dosets():
1188 d = util.lrucachedict(size)
1220 d = util.lrucachedict(size)
1189 for v in setseq:
1221 for v in setseq:
1190 d[v] = v
1222 d[v] = v
1191
1223
1192 # Mixed mode randomly performs gets and sets with eviction.
1224 # Mixed mode randomly performs gets and sets with eviction.
1193 mixedops = []
1225 mixedops = []
1194 for i in xrange(mixed):
1226 for i in xrange(mixed):
1195 r = random.randint(0, 100)
1227 r = random.randint(0, 100)
1196 if r < mixedgetfreq:
1228 if r < mixedgetfreq:
1197 op = 0
1229 op = 0
1198 else:
1230 else:
1199 op = 1
1231 op = 1
1200
1232
1201 mixedops.append((op, random.randint(0, size * 2)))
1233 mixedops.append((op, random.randint(0, size * 2)))
1202
1234
1203 def domixed():
1235 def domixed():
1204 d = util.lrucachedict(size)
1236 d = util.lrucachedict(size)
1205
1237
1206 for op, v in mixedops:
1238 for op, v in mixedops:
1207 if op == 0:
1239 if op == 0:
1208 try:
1240 try:
1209 d[v]
1241 d[v]
1210 except KeyError:
1242 except KeyError:
1211 pass
1243 pass
1212 else:
1244 else:
1213 d[v] = v
1245 d[v] = v
1214
1246
1215 benches = [
1247 benches = [
1216 (doinit, 'init'),
1248 (doinit, 'init'),
1217 (dogets, 'gets'),
1249 (dogets, 'gets'),
1218 (dosets, 'sets'),
1250 (dosets, 'sets'),
1219 (domixed, 'mixed')
1251 (domixed, 'mixed')
1220 ]
1252 ]
1221
1253
1222 for fn, title in benches:
1254 for fn, title in benches:
1223 timer, fm = gettimer(ui, opts)
1255 timer, fm = gettimer(ui, opts)
1224 timer(fn, title=title)
1256 timer(fn, title=title)
1225 fm.end()
1257 fm.end()
1226
1258
1227 def uisetup(ui):
1259 def uisetup(ui):
1228 if (util.safehasattr(cmdutil, 'openrevlog') and
1260 if (util.safehasattr(cmdutil, 'openrevlog') and
1229 not util.safehasattr(commands, 'debugrevlogopts')):
1261 not util.safehasattr(commands, 'debugrevlogopts')):
1230 # for "historical portability":
1262 # for "historical portability":
1231 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1263 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1232 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1264 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1233 # openrevlog() should cause failure, because it has been
1265 # openrevlog() should cause failure, because it has been
1234 # available since 3.5 (or 49c583ca48c4).
1266 # available since 3.5 (or 49c583ca48c4).
1235 def openrevlog(orig, repo, cmd, file_, opts):
1267 def openrevlog(orig, repo, cmd, file_, opts):
1236 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1268 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1237 raise error.Abort("This version doesn't support --dir option",
1269 raise error.Abort("This version doesn't support --dir option",
1238 hint="use 3.5 or later")
1270 hint="use 3.5 or later")
1239 return orig(repo, cmd, file_, opts)
1271 return orig(repo, cmd, file_, opts)
1240 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1272 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now