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