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