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