##// END OF EJS Templates
perf: add a perfnodemap command...
Boris Feld -
r41610:c9ff9388 default
parent child Browse files
Show More
@@ -1,2701 +1,2754 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 contextlib
22 import contextlib
23 import functools
23 import functools
24 import gc
24 import gc
25 import os
25 import os
26 import random
26 import random
27 import shutil
27 import shutil
28 import struct
28 import struct
29 import sys
29 import sys
30 import tempfile
30 import tempfile
31 import threading
31 import threading
32 import time
32 import time
33 from mercurial import (
33 from mercurial import (
34 changegroup,
34 changegroup,
35 cmdutil,
35 cmdutil,
36 commands,
36 commands,
37 copies,
37 copies,
38 error,
38 error,
39 extensions,
39 extensions,
40 hg,
40 hg,
41 mdiff,
41 mdiff,
42 merge,
42 merge,
43 revlog,
43 revlog,
44 util,
44 util,
45 )
45 )
46
46
47 # for "historical portability":
47 # for "historical portability":
48 # try to import modules separately (in dict order), and ignore
48 # try to import modules separately (in dict order), and ignore
49 # failure, because these aren't available with early Mercurial
49 # failure, because these aren't available with early Mercurial
50 try:
50 try:
51 from mercurial import branchmap # since 2.5 (or bcee63733aad)
51 from mercurial import branchmap # since 2.5 (or bcee63733aad)
52 except ImportError:
52 except ImportError:
53 pass
53 pass
54 try:
54 try:
55 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
55 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
56 except ImportError:
56 except ImportError:
57 pass
57 pass
58 try:
58 try:
59 from mercurial import registrar # since 3.7 (or 37d50250b696)
59 from mercurial import registrar # since 3.7 (or 37d50250b696)
60 dir(registrar) # forcibly load it
60 dir(registrar) # forcibly load it
61 except ImportError:
61 except ImportError:
62 registrar = None
62 registrar = None
63 try:
63 try:
64 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
64 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
65 except ImportError:
65 except ImportError:
66 pass
66 pass
67 try:
67 try:
68 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
68 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
69 except ImportError:
69 except ImportError:
70 pass
70 pass
71 try:
71 try:
72 from mercurial import setdiscovery # since 1.9 (or cb98fed52495)
72 from mercurial import setdiscovery # since 1.9 (or cb98fed52495)
73 except ImportError:
73 except ImportError:
74 pass
74 pass
75
75
76
76
77 def identity(a):
77 def identity(a):
78 return a
78 return a
79
79
80 try:
80 try:
81 from mercurial import pycompat
81 from mercurial import pycompat
82 getargspec = pycompat.getargspec # added to module after 4.5
82 getargspec = pycompat.getargspec # added to module after 4.5
83 _byteskwargs = pycompat.byteskwargs # since 4.1 (or fbc3f73dc802)
83 _byteskwargs = pycompat.byteskwargs # since 4.1 (or fbc3f73dc802)
84 _sysstr = pycompat.sysstr # since 4.0 (or 2219f4f82ede)
84 _sysstr = pycompat.sysstr # since 4.0 (or 2219f4f82ede)
85 _xrange = pycompat.xrange # since 4.8 (or 7eba8f83129b)
85 _xrange = pycompat.xrange # since 4.8 (or 7eba8f83129b)
86 fsencode = pycompat.fsencode # since 3.9 (or f4a5e0e86a7e)
86 fsencode = pycompat.fsencode # since 3.9 (or f4a5e0e86a7e)
87 if pycompat.ispy3:
87 if pycompat.ispy3:
88 _maxint = sys.maxsize # per py3 docs for replacing maxint
88 _maxint = sys.maxsize # per py3 docs for replacing maxint
89 else:
89 else:
90 _maxint = sys.maxint
90 _maxint = sys.maxint
91 except (ImportError, AttributeError):
91 except (ImportError, AttributeError):
92 import inspect
92 import inspect
93 getargspec = inspect.getargspec
93 getargspec = inspect.getargspec
94 _byteskwargs = identity
94 _byteskwargs = identity
95 fsencode = identity # no py3 support
95 fsencode = identity # no py3 support
96 _maxint = sys.maxint # no py3 support
96 _maxint = sys.maxint # no py3 support
97 _sysstr = lambda x: x # no py3 support
97 _sysstr = lambda x: x # no py3 support
98 _xrange = xrange
98 _xrange = xrange
99
99
100 try:
100 try:
101 # 4.7+
101 # 4.7+
102 queue = pycompat.queue.Queue
102 queue = pycompat.queue.Queue
103 except (AttributeError, ImportError):
103 except (AttributeError, ImportError):
104 # <4.7.
104 # <4.7.
105 try:
105 try:
106 queue = pycompat.queue
106 queue = pycompat.queue
107 except (AttributeError, ImportError):
107 except (AttributeError, ImportError):
108 queue = util.queue
108 queue = util.queue
109
109
110 try:
110 try:
111 from mercurial import logcmdutil
111 from mercurial import logcmdutil
112 makelogtemplater = logcmdutil.maketemplater
112 makelogtemplater = logcmdutil.maketemplater
113 except (AttributeError, ImportError):
113 except (AttributeError, ImportError):
114 try:
114 try:
115 makelogtemplater = cmdutil.makelogtemplater
115 makelogtemplater = cmdutil.makelogtemplater
116 except (AttributeError, ImportError):
116 except (AttributeError, ImportError):
117 makelogtemplater = None
117 makelogtemplater = None
118
118
119 # for "historical portability":
119 # for "historical portability":
120 # define util.safehasattr forcibly, because util.safehasattr has been
120 # define util.safehasattr forcibly, because util.safehasattr has been
121 # available since 1.9.3 (or 94b200a11cf7)
121 # available since 1.9.3 (or 94b200a11cf7)
122 _undefined = object()
122 _undefined = object()
123 def safehasattr(thing, attr):
123 def safehasattr(thing, attr):
124 return getattr(thing, _sysstr(attr), _undefined) is not _undefined
124 return getattr(thing, _sysstr(attr), _undefined) is not _undefined
125 setattr(util, 'safehasattr', safehasattr)
125 setattr(util, 'safehasattr', safehasattr)
126
126
127 # for "historical portability":
127 # for "historical portability":
128 # define util.timer forcibly, because util.timer has been available
128 # define util.timer forcibly, because util.timer has been available
129 # since ae5d60bb70c9
129 # since ae5d60bb70c9
130 if safehasattr(time, 'perf_counter'):
130 if safehasattr(time, 'perf_counter'):
131 util.timer = time.perf_counter
131 util.timer = time.perf_counter
132 elif os.name == b'nt':
132 elif os.name == b'nt':
133 util.timer = time.clock
133 util.timer = time.clock
134 else:
134 else:
135 util.timer = time.time
135 util.timer = time.time
136
136
137 # for "historical portability":
137 # for "historical portability":
138 # use locally defined empty option list, if formatteropts isn't
138 # use locally defined empty option list, if formatteropts isn't
139 # available, because commands.formatteropts has been available since
139 # available, because commands.formatteropts has been available since
140 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
140 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
141 # available since 2.2 (or ae5f92e154d3)
141 # available since 2.2 (or ae5f92e154d3)
142 formatteropts = getattr(cmdutil, "formatteropts",
142 formatteropts = getattr(cmdutil, "formatteropts",
143 getattr(commands, "formatteropts", []))
143 getattr(commands, "formatteropts", []))
144
144
145 # for "historical portability":
145 # for "historical portability":
146 # use locally defined option list, if debugrevlogopts isn't available,
146 # use locally defined option list, if debugrevlogopts isn't available,
147 # because commands.debugrevlogopts has been available since 3.7 (or
147 # because commands.debugrevlogopts has been available since 3.7 (or
148 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
148 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
149 # since 1.9 (or a79fea6b3e77).
149 # since 1.9 (or a79fea6b3e77).
150 revlogopts = getattr(cmdutil, "debugrevlogopts",
150 revlogopts = getattr(cmdutil, "debugrevlogopts",
151 getattr(commands, "debugrevlogopts", [
151 getattr(commands, "debugrevlogopts", [
152 (b'c', b'changelog', False, (b'open changelog')),
152 (b'c', b'changelog', False, (b'open changelog')),
153 (b'm', b'manifest', False, (b'open manifest')),
153 (b'm', b'manifest', False, (b'open manifest')),
154 (b'', b'dir', False, (b'open directory manifest')),
154 (b'', b'dir', False, (b'open directory manifest')),
155 ]))
155 ]))
156
156
157 cmdtable = {}
157 cmdtable = {}
158
158
159 # for "historical portability":
159 # for "historical portability":
160 # define parsealiases locally, because cmdutil.parsealiases has been
160 # define parsealiases locally, because cmdutil.parsealiases has been
161 # available since 1.5 (or 6252852b4332)
161 # available since 1.5 (or 6252852b4332)
162 def parsealiases(cmd):
162 def parsealiases(cmd):
163 return cmd.split(b"|")
163 return cmd.split(b"|")
164
164
165 if safehasattr(registrar, 'command'):
165 if safehasattr(registrar, 'command'):
166 command = registrar.command(cmdtable)
166 command = registrar.command(cmdtable)
167 elif safehasattr(cmdutil, 'command'):
167 elif safehasattr(cmdutil, 'command'):
168 command = cmdutil.command(cmdtable)
168 command = cmdutil.command(cmdtable)
169 if b'norepo' not in getargspec(command).args:
169 if b'norepo' not in getargspec(command).args:
170 # for "historical portability":
170 # for "historical portability":
171 # wrap original cmdutil.command, because "norepo" option has
171 # wrap original cmdutil.command, because "norepo" option has
172 # been available since 3.1 (or 75a96326cecb)
172 # been available since 3.1 (or 75a96326cecb)
173 _command = command
173 _command = command
174 def command(name, options=(), synopsis=None, norepo=False):
174 def command(name, options=(), synopsis=None, norepo=False):
175 if norepo:
175 if norepo:
176 commands.norepo += b' %s' % b' '.join(parsealiases(name))
176 commands.norepo += b' %s' % b' '.join(parsealiases(name))
177 return _command(name, list(options), synopsis)
177 return _command(name, list(options), synopsis)
178 else:
178 else:
179 # for "historical portability":
179 # for "historical portability":
180 # define "@command" annotation locally, because cmdutil.command
180 # define "@command" annotation locally, because cmdutil.command
181 # has been available since 1.9 (or 2daa5179e73f)
181 # has been available since 1.9 (or 2daa5179e73f)
182 def command(name, options=(), synopsis=None, norepo=False):
182 def command(name, options=(), synopsis=None, norepo=False):
183 def decorator(func):
183 def decorator(func):
184 if synopsis:
184 if synopsis:
185 cmdtable[name] = func, list(options), synopsis
185 cmdtable[name] = func, list(options), synopsis
186 else:
186 else:
187 cmdtable[name] = func, list(options)
187 cmdtable[name] = func, list(options)
188 if norepo:
188 if norepo:
189 commands.norepo += b' %s' % b' '.join(parsealiases(name))
189 commands.norepo += b' %s' % b' '.join(parsealiases(name))
190 return func
190 return func
191 return decorator
191 return decorator
192
192
193 try:
193 try:
194 import mercurial.registrar
194 import mercurial.registrar
195 import mercurial.configitems
195 import mercurial.configitems
196 configtable = {}
196 configtable = {}
197 configitem = mercurial.registrar.configitem(configtable)
197 configitem = mercurial.registrar.configitem(configtable)
198 configitem(b'perf', b'presleep',
198 configitem(b'perf', b'presleep',
199 default=mercurial.configitems.dynamicdefault,
199 default=mercurial.configitems.dynamicdefault,
200 )
200 )
201 configitem(b'perf', b'stub',
201 configitem(b'perf', b'stub',
202 default=mercurial.configitems.dynamicdefault,
202 default=mercurial.configitems.dynamicdefault,
203 )
203 )
204 configitem(b'perf', b'parentscount',
204 configitem(b'perf', b'parentscount',
205 default=mercurial.configitems.dynamicdefault,
205 default=mercurial.configitems.dynamicdefault,
206 )
206 )
207 configitem(b'perf', b'all-timing',
207 configitem(b'perf', b'all-timing',
208 default=mercurial.configitems.dynamicdefault,
208 default=mercurial.configitems.dynamicdefault,
209 )
209 )
210 except (ImportError, AttributeError):
210 except (ImportError, AttributeError):
211 pass
211 pass
212
212
213 def getlen(ui):
213 def getlen(ui):
214 if ui.configbool(b"perf", b"stub", False):
214 if ui.configbool(b"perf", b"stub", False):
215 return lambda x: 1
215 return lambda x: 1
216 return len
216 return len
217
217
218 def gettimer(ui, opts=None):
218 def gettimer(ui, opts=None):
219 """return a timer function and formatter: (timer, formatter)
219 """return a timer function and formatter: (timer, formatter)
220
220
221 This function exists to gather the creation of formatter in a single
221 This function exists to gather the creation of formatter in a single
222 place instead of duplicating it in all performance commands."""
222 place instead of duplicating it in all performance commands."""
223
223
224 # enforce an idle period before execution to counteract power management
224 # enforce an idle period before execution to counteract power management
225 # experimental config: perf.presleep
225 # experimental config: perf.presleep
226 time.sleep(getint(ui, b"perf", b"presleep", 1))
226 time.sleep(getint(ui, b"perf", b"presleep", 1))
227
227
228 if opts is None:
228 if opts is None:
229 opts = {}
229 opts = {}
230 # redirect all to stderr unless buffer api is in use
230 # redirect all to stderr unless buffer api is in use
231 if not ui._buffers:
231 if not ui._buffers:
232 ui = ui.copy()
232 ui = ui.copy()
233 uifout = safeattrsetter(ui, b'fout', ignoremissing=True)
233 uifout = safeattrsetter(ui, b'fout', ignoremissing=True)
234 if uifout:
234 if uifout:
235 # for "historical portability":
235 # for "historical portability":
236 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
236 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
237 uifout.set(ui.ferr)
237 uifout.set(ui.ferr)
238
238
239 # get a formatter
239 # get a formatter
240 uiformatter = getattr(ui, 'formatter', None)
240 uiformatter = getattr(ui, 'formatter', None)
241 if uiformatter:
241 if uiformatter:
242 fm = uiformatter(b'perf', opts)
242 fm = uiformatter(b'perf', opts)
243 else:
243 else:
244 # for "historical portability":
244 # for "historical portability":
245 # define formatter locally, because ui.formatter has been
245 # define formatter locally, because ui.formatter has been
246 # available since 2.2 (or ae5f92e154d3)
246 # available since 2.2 (or ae5f92e154d3)
247 from mercurial import node
247 from mercurial import node
248 class defaultformatter(object):
248 class defaultformatter(object):
249 """Minimized composition of baseformatter and plainformatter
249 """Minimized composition of baseformatter and plainformatter
250 """
250 """
251 def __init__(self, ui, topic, opts):
251 def __init__(self, ui, topic, opts):
252 self._ui = ui
252 self._ui = ui
253 if ui.debugflag:
253 if ui.debugflag:
254 self.hexfunc = node.hex
254 self.hexfunc = node.hex
255 else:
255 else:
256 self.hexfunc = node.short
256 self.hexfunc = node.short
257 def __nonzero__(self):
257 def __nonzero__(self):
258 return False
258 return False
259 __bool__ = __nonzero__
259 __bool__ = __nonzero__
260 def startitem(self):
260 def startitem(self):
261 pass
261 pass
262 def data(self, **data):
262 def data(self, **data):
263 pass
263 pass
264 def write(self, fields, deftext, *fielddata, **opts):
264 def write(self, fields, deftext, *fielddata, **opts):
265 self._ui.write(deftext % fielddata, **opts)
265 self._ui.write(deftext % fielddata, **opts)
266 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
266 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
267 if cond:
267 if cond:
268 self._ui.write(deftext % fielddata, **opts)
268 self._ui.write(deftext % fielddata, **opts)
269 def plain(self, text, **opts):
269 def plain(self, text, **opts):
270 self._ui.write(text, **opts)
270 self._ui.write(text, **opts)
271 def end(self):
271 def end(self):
272 pass
272 pass
273 fm = defaultformatter(ui, b'perf', opts)
273 fm = defaultformatter(ui, b'perf', opts)
274
274
275 # stub function, runs code only once instead of in a loop
275 # stub function, runs code only once instead of in a loop
276 # experimental config: perf.stub
276 # experimental config: perf.stub
277 if ui.configbool(b"perf", b"stub", False):
277 if ui.configbool(b"perf", b"stub", False):
278 return functools.partial(stub_timer, fm), fm
278 return functools.partial(stub_timer, fm), fm
279
279
280 # experimental config: perf.all-timing
280 # experimental config: perf.all-timing
281 displayall = ui.configbool(b"perf", b"all-timing", False)
281 displayall = ui.configbool(b"perf", b"all-timing", False)
282 return functools.partial(_timer, fm, displayall=displayall), fm
282 return functools.partial(_timer, fm, displayall=displayall), fm
283
283
284 def stub_timer(fm, func, setup=None, title=None):
284 def stub_timer(fm, func, setup=None, title=None):
285 if setup is not None:
285 if setup is not None:
286 setup()
286 setup()
287 func()
287 func()
288
288
289 @contextlib.contextmanager
289 @contextlib.contextmanager
290 def timeone():
290 def timeone():
291 r = []
291 r = []
292 ostart = os.times()
292 ostart = os.times()
293 cstart = util.timer()
293 cstart = util.timer()
294 yield r
294 yield r
295 cstop = util.timer()
295 cstop = util.timer()
296 ostop = os.times()
296 ostop = os.times()
297 a, b = ostart, ostop
297 a, b = ostart, ostop
298 r.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
298 r.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
299
299
300 def _timer(fm, func, setup=None, title=None, displayall=False):
300 def _timer(fm, func, setup=None, title=None, displayall=False):
301 gc.collect()
301 gc.collect()
302 results = []
302 results = []
303 begin = util.timer()
303 begin = util.timer()
304 count = 0
304 count = 0
305 while True:
305 while True:
306 if setup is not None:
306 if setup is not None:
307 setup()
307 setup()
308 with timeone() as item:
308 with timeone() as item:
309 r = func()
309 r = func()
310 count += 1
310 count += 1
311 results.append(item[0])
311 results.append(item[0])
312 cstop = util.timer()
312 cstop = util.timer()
313 if cstop - begin > 3 and count >= 100:
313 if cstop - begin > 3 and count >= 100:
314 break
314 break
315 if cstop - begin > 10 and count >= 3:
315 if cstop - begin > 10 and count >= 3:
316 break
316 break
317
317
318 formatone(fm, results, title=title, result=r,
318 formatone(fm, results, title=title, result=r,
319 displayall=displayall)
319 displayall=displayall)
320
320
321 def formatone(fm, timings, title=None, result=None, displayall=False):
321 def formatone(fm, timings, title=None, result=None, displayall=False):
322
322
323 count = len(timings)
323 count = len(timings)
324
324
325 fm.startitem()
325 fm.startitem()
326
326
327 if title:
327 if title:
328 fm.write(b'title', b'! %s\n', title)
328 fm.write(b'title', b'! %s\n', title)
329 if result:
329 if result:
330 fm.write(b'result', b'! result: %s\n', result)
330 fm.write(b'result', b'! result: %s\n', result)
331 def display(role, entry):
331 def display(role, entry):
332 prefix = b''
332 prefix = b''
333 if role != b'best':
333 if role != b'best':
334 prefix = b'%s.' % role
334 prefix = b'%s.' % role
335 fm.plain(b'!')
335 fm.plain(b'!')
336 fm.write(prefix + b'wall', b' wall %f', entry[0])
336 fm.write(prefix + b'wall', b' wall %f', entry[0])
337 fm.write(prefix + b'comb', b' comb %f', entry[1] + entry[2])
337 fm.write(prefix + b'comb', b' comb %f', entry[1] + entry[2])
338 fm.write(prefix + b'user', b' user %f', entry[1])
338 fm.write(prefix + b'user', b' user %f', entry[1])
339 fm.write(prefix + b'sys', b' sys %f', entry[2])
339 fm.write(prefix + b'sys', b' sys %f', entry[2])
340 fm.write(prefix + b'count', b' (%s of %%d)' % role, count)
340 fm.write(prefix + b'count', b' (%s of %%d)' % role, count)
341 fm.plain(b'\n')
341 fm.plain(b'\n')
342 timings.sort()
342 timings.sort()
343 min_val = timings[0]
343 min_val = timings[0]
344 display(b'best', min_val)
344 display(b'best', min_val)
345 if displayall:
345 if displayall:
346 max_val = timings[-1]
346 max_val = timings[-1]
347 display(b'max', max_val)
347 display(b'max', max_val)
348 avg = tuple([sum(x) / count for x in zip(*timings)])
348 avg = tuple([sum(x) / count for x in zip(*timings)])
349 display(b'avg', avg)
349 display(b'avg', avg)
350 median = timings[len(timings) // 2]
350 median = timings[len(timings) // 2]
351 display(b'median', median)
351 display(b'median', median)
352
352
353 # utilities for historical portability
353 # utilities for historical portability
354
354
355 def getint(ui, section, name, default):
355 def getint(ui, section, name, default):
356 # for "historical portability":
356 # for "historical portability":
357 # ui.configint has been available since 1.9 (or fa2b596db182)
357 # ui.configint has been available since 1.9 (or fa2b596db182)
358 v = ui.config(section, name, None)
358 v = ui.config(section, name, None)
359 if v is None:
359 if v is None:
360 return default
360 return default
361 try:
361 try:
362 return int(v)
362 return int(v)
363 except ValueError:
363 except ValueError:
364 raise error.ConfigError((b"%s.%s is not an integer ('%s')")
364 raise error.ConfigError((b"%s.%s is not an integer ('%s')")
365 % (section, name, v))
365 % (section, name, v))
366
366
367 def safeattrsetter(obj, name, ignoremissing=False):
367 def safeattrsetter(obj, name, ignoremissing=False):
368 """Ensure that 'obj' has 'name' attribute before subsequent setattr
368 """Ensure that 'obj' has 'name' attribute before subsequent setattr
369
369
370 This function is aborted, if 'obj' doesn't have 'name' attribute
370 This function is aborted, if 'obj' doesn't have 'name' attribute
371 at runtime. This avoids overlooking removal of an attribute, which
371 at runtime. This avoids overlooking removal of an attribute, which
372 breaks assumption of performance measurement, in the future.
372 breaks assumption of performance measurement, in the future.
373
373
374 This function returns the object to (1) assign a new value, and
374 This function returns the object to (1) assign a new value, and
375 (2) restore an original value to the attribute.
375 (2) restore an original value to the attribute.
376
376
377 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
377 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
378 abortion, and this function returns None. This is useful to
378 abortion, and this function returns None. This is useful to
379 examine an attribute, which isn't ensured in all Mercurial
379 examine an attribute, which isn't ensured in all Mercurial
380 versions.
380 versions.
381 """
381 """
382 if not util.safehasattr(obj, name):
382 if not util.safehasattr(obj, name):
383 if ignoremissing:
383 if ignoremissing:
384 return None
384 return None
385 raise error.Abort((b"missing attribute %s of %s might break assumption"
385 raise error.Abort((b"missing attribute %s of %s might break assumption"
386 b" of performance measurement") % (name, obj))
386 b" of performance measurement") % (name, obj))
387
387
388 origvalue = getattr(obj, _sysstr(name))
388 origvalue = getattr(obj, _sysstr(name))
389 class attrutil(object):
389 class attrutil(object):
390 def set(self, newvalue):
390 def set(self, newvalue):
391 setattr(obj, _sysstr(name), newvalue)
391 setattr(obj, _sysstr(name), newvalue)
392 def restore(self):
392 def restore(self):
393 setattr(obj, _sysstr(name), origvalue)
393 setattr(obj, _sysstr(name), origvalue)
394
394
395 return attrutil()
395 return attrutil()
396
396
397 # utilities to examine each internal API changes
397 # utilities to examine each internal API changes
398
398
399 def getbranchmapsubsettable():
399 def getbranchmapsubsettable():
400 # for "historical portability":
400 # for "historical portability":
401 # subsettable is defined in:
401 # subsettable is defined in:
402 # - branchmap since 2.9 (or 175c6fd8cacc)
402 # - branchmap since 2.9 (or 175c6fd8cacc)
403 # - repoview since 2.5 (or 59a9f18d4587)
403 # - repoview since 2.5 (or 59a9f18d4587)
404 for mod in (branchmap, repoview):
404 for mod in (branchmap, repoview):
405 subsettable = getattr(mod, 'subsettable', None)
405 subsettable = getattr(mod, 'subsettable', None)
406 if subsettable:
406 if subsettable:
407 return subsettable
407 return subsettable
408
408
409 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
409 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
410 # branchmap and repoview modules exist, but subsettable attribute
410 # branchmap and repoview modules exist, but subsettable attribute
411 # doesn't)
411 # doesn't)
412 raise error.Abort((b"perfbranchmap not available with this Mercurial"),
412 raise error.Abort((b"perfbranchmap not available with this Mercurial"),
413 hint=b"use 2.5 or later")
413 hint=b"use 2.5 or later")
414
414
415 def getsvfs(repo):
415 def getsvfs(repo):
416 """Return appropriate object to access files under .hg/store
416 """Return appropriate object to access files under .hg/store
417 """
417 """
418 # for "historical portability":
418 # for "historical portability":
419 # repo.svfs has been available since 2.3 (or 7034365089bf)
419 # repo.svfs has been available since 2.3 (or 7034365089bf)
420 svfs = getattr(repo, 'svfs', None)
420 svfs = getattr(repo, 'svfs', None)
421 if svfs:
421 if svfs:
422 return svfs
422 return svfs
423 else:
423 else:
424 return getattr(repo, 'sopener')
424 return getattr(repo, 'sopener')
425
425
426 def getvfs(repo):
426 def getvfs(repo):
427 """Return appropriate object to access files under .hg
427 """Return appropriate object to access files under .hg
428 """
428 """
429 # for "historical portability":
429 # for "historical portability":
430 # repo.vfs has been available since 2.3 (or 7034365089bf)
430 # repo.vfs has been available since 2.3 (or 7034365089bf)
431 vfs = getattr(repo, 'vfs', None)
431 vfs = getattr(repo, 'vfs', None)
432 if vfs:
432 if vfs:
433 return vfs
433 return vfs
434 else:
434 else:
435 return getattr(repo, 'opener')
435 return getattr(repo, 'opener')
436
436
437 def repocleartagscachefunc(repo):
437 def repocleartagscachefunc(repo):
438 """Return the function to clear tags cache according to repo internal API
438 """Return the function to clear tags cache according to repo internal API
439 """
439 """
440 if util.safehasattr(repo, b'_tagscache'): # since 2.0 (or 9dca7653b525)
440 if util.safehasattr(repo, b'_tagscache'): # since 2.0 (or 9dca7653b525)
441 # in this case, setattr(repo, '_tagscache', None) or so isn't
441 # in this case, setattr(repo, '_tagscache', None) or so isn't
442 # correct way to clear tags cache, because existing code paths
442 # correct way to clear tags cache, because existing code paths
443 # expect _tagscache to be a structured object.
443 # expect _tagscache to be a structured object.
444 def clearcache():
444 def clearcache():
445 # _tagscache has been filteredpropertycache since 2.5 (or
445 # _tagscache has been filteredpropertycache since 2.5 (or
446 # 98c867ac1330), and delattr() can't work in such case
446 # 98c867ac1330), and delattr() can't work in such case
447 if b'_tagscache' in vars(repo):
447 if b'_tagscache' in vars(repo):
448 del repo.__dict__[b'_tagscache']
448 del repo.__dict__[b'_tagscache']
449 return clearcache
449 return clearcache
450
450
451 repotags = safeattrsetter(repo, b'_tags', ignoremissing=True)
451 repotags = safeattrsetter(repo, b'_tags', ignoremissing=True)
452 if repotags: # since 1.4 (or 5614a628d173)
452 if repotags: # since 1.4 (or 5614a628d173)
453 return lambda : repotags.set(None)
453 return lambda : repotags.set(None)
454
454
455 repotagscache = safeattrsetter(repo, b'tagscache', ignoremissing=True)
455 repotagscache = safeattrsetter(repo, b'tagscache', ignoremissing=True)
456 if repotagscache: # since 0.6 (or d7df759d0e97)
456 if repotagscache: # since 0.6 (or d7df759d0e97)
457 return lambda : repotagscache.set(None)
457 return lambda : repotagscache.set(None)
458
458
459 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
459 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
460 # this point, but it isn't so problematic, because:
460 # this point, but it isn't so problematic, because:
461 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
461 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
462 # in perftags() causes failure soon
462 # in perftags() causes failure soon
463 # - perf.py itself has been available since 1.1 (or eb240755386d)
463 # - perf.py itself has been available since 1.1 (or eb240755386d)
464 raise error.Abort((b"tags API of this hg command is unknown"))
464 raise error.Abort((b"tags API of this hg command is unknown"))
465
465
466 # utilities to clear cache
466 # utilities to clear cache
467
467
468 def clearfilecache(obj, attrname):
468 def clearfilecache(obj, attrname):
469 unfiltered = getattr(obj, 'unfiltered', None)
469 unfiltered = getattr(obj, 'unfiltered', None)
470 if unfiltered is not None:
470 if unfiltered is not None:
471 obj = obj.unfiltered()
471 obj = obj.unfiltered()
472 if attrname in vars(obj):
472 if attrname in vars(obj):
473 delattr(obj, attrname)
473 delattr(obj, attrname)
474 obj._filecache.pop(attrname, None)
474 obj._filecache.pop(attrname, None)
475
475
476 def clearchangelog(repo):
476 def clearchangelog(repo):
477 if repo is not repo.unfiltered():
477 if repo is not repo.unfiltered():
478 object.__setattr__(repo, r'_clcachekey', None)
478 object.__setattr__(repo, r'_clcachekey', None)
479 object.__setattr__(repo, r'_clcache', None)
479 object.__setattr__(repo, r'_clcache', None)
480 clearfilecache(repo.unfiltered(), 'changelog')
480 clearfilecache(repo.unfiltered(), 'changelog')
481
481
482 # perf commands
482 # perf commands
483
483
484 @command(b'perfwalk', formatteropts)
484 @command(b'perfwalk', formatteropts)
485 def perfwalk(ui, repo, *pats, **opts):
485 def perfwalk(ui, repo, *pats, **opts):
486 opts = _byteskwargs(opts)
486 opts = _byteskwargs(opts)
487 timer, fm = gettimer(ui, opts)
487 timer, fm = gettimer(ui, opts)
488 m = scmutil.match(repo[None], pats, {})
488 m = scmutil.match(repo[None], pats, {})
489 timer(lambda: len(list(repo.dirstate.walk(m, subrepos=[], unknown=True,
489 timer(lambda: len(list(repo.dirstate.walk(m, subrepos=[], unknown=True,
490 ignored=False))))
490 ignored=False))))
491 fm.end()
491 fm.end()
492
492
493 @command(b'perfannotate', formatteropts)
493 @command(b'perfannotate', formatteropts)
494 def perfannotate(ui, repo, f, **opts):
494 def perfannotate(ui, repo, f, **opts):
495 opts = _byteskwargs(opts)
495 opts = _byteskwargs(opts)
496 timer, fm = gettimer(ui, opts)
496 timer, fm = gettimer(ui, opts)
497 fc = repo[b'.'][f]
497 fc = repo[b'.'][f]
498 timer(lambda: len(fc.annotate(True)))
498 timer(lambda: len(fc.annotate(True)))
499 fm.end()
499 fm.end()
500
500
501 @command(b'perfstatus',
501 @command(b'perfstatus',
502 [(b'u', b'unknown', False,
502 [(b'u', b'unknown', False,
503 b'ask status to look for unknown files')] + formatteropts)
503 b'ask status to look for unknown files')] + formatteropts)
504 def perfstatus(ui, repo, **opts):
504 def perfstatus(ui, repo, **opts):
505 opts = _byteskwargs(opts)
505 opts = _byteskwargs(opts)
506 #m = match.always(repo.root, repo.getcwd())
506 #m = match.always(repo.root, repo.getcwd())
507 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
507 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
508 # False))))
508 # False))))
509 timer, fm = gettimer(ui, opts)
509 timer, fm = gettimer(ui, opts)
510 timer(lambda: sum(map(len, repo.status(unknown=opts[b'unknown']))))
510 timer(lambda: sum(map(len, repo.status(unknown=opts[b'unknown']))))
511 fm.end()
511 fm.end()
512
512
513 @command(b'perfaddremove', formatteropts)
513 @command(b'perfaddremove', formatteropts)
514 def perfaddremove(ui, repo, **opts):
514 def perfaddremove(ui, repo, **opts):
515 opts = _byteskwargs(opts)
515 opts = _byteskwargs(opts)
516 timer, fm = gettimer(ui, opts)
516 timer, fm = gettimer(ui, opts)
517 try:
517 try:
518 oldquiet = repo.ui.quiet
518 oldquiet = repo.ui.quiet
519 repo.ui.quiet = True
519 repo.ui.quiet = True
520 matcher = scmutil.match(repo[None])
520 matcher = scmutil.match(repo[None])
521 opts[b'dry_run'] = True
521 opts[b'dry_run'] = True
522 timer(lambda: scmutil.addremove(repo, matcher, b"", opts))
522 timer(lambda: scmutil.addremove(repo, matcher, b"", opts))
523 finally:
523 finally:
524 repo.ui.quiet = oldquiet
524 repo.ui.quiet = oldquiet
525 fm.end()
525 fm.end()
526
526
527 def clearcaches(cl):
527 def clearcaches(cl):
528 # behave somewhat consistently across internal API changes
528 # behave somewhat consistently across internal API changes
529 if util.safehasattr(cl, b'clearcaches'):
529 if util.safehasattr(cl, b'clearcaches'):
530 cl.clearcaches()
530 cl.clearcaches()
531 elif util.safehasattr(cl, b'_nodecache'):
531 elif util.safehasattr(cl, b'_nodecache'):
532 from mercurial.node import nullid, nullrev
532 from mercurial.node import nullid, nullrev
533 cl._nodecache = {nullid: nullrev}
533 cl._nodecache = {nullid: nullrev}
534 cl._nodepos = None
534 cl._nodepos = None
535
535
536 @command(b'perfheads', formatteropts)
536 @command(b'perfheads', formatteropts)
537 def perfheads(ui, repo, **opts):
537 def perfheads(ui, repo, **opts):
538 """benchmark the computation of a changelog heads"""
538 """benchmark the computation of a changelog heads"""
539 opts = _byteskwargs(opts)
539 opts = _byteskwargs(opts)
540 timer, fm = gettimer(ui, opts)
540 timer, fm = gettimer(ui, opts)
541 cl = repo.changelog
541 cl = repo.changelog
542 def s():
542 def s():
543 clearcaches(cl)
543 clearcaches(cl)
544 def d():
544 def d():
545 len(cl.headrevs())
545 len(cl.headrevs())
546 timer(d, setup=s)
546 timer(d, setup=s)
547 fm.end()
547 fm.end()
548
548
549 @command(b'perftags', formatteropts+
549 @command(b'perftags', formatteropts+
550 [
550 [
551 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
551 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
552 ])
552 ])
553 def perftags(ui, repo, **opts):
553 def perftags(ui, repo, **opts):
554 opts = _byteskwargs(opts)
554 opts = _byteskwargs(opts)
555 timer, fm = gettimer(ui, opts)
555 timer, fm = gettimer(ui, opts)
556 repocleartagscache = repocleartagscachefunc(repo)
556 repocleartagscache = repocleartagscachefunc(repo)
557 clearrevlogs = opts[b'clear_revlogs']
557 clearrevlogs = opts[b'clear_revlogs']
558 def s():
558 def s():
559 if clearrevlogs:
559 if clearrevlogs:
560 clearchangelog(repo)
560 clearchangelog(repo)
561 clearfilecache(repo.unfiltered(), 'manifest')
561 clearfilecache(repo.unfiltered(), 'manifest')
562 repocleartagscache()
562 repocleartagscache()
563 def t():
563 def t():
564 return len(repo.tags())
564 return len(repo.tags())
565 timer(t, setup=s)
565 timer(t, setup=s)
566 fm.end()
566 fm.end()
567
567
568 @command(b'perfancestors', formatteropts)
568 @command(b'perfancestors', formatteropts)
569 def perfancestors(ui, repo, **opts):
569 def perfancestors(ui, repo, **opts):
570 opts = _byteskwargs(opts)
570 opts = _byteskwargs(opts)
571 timer, fm = gettimer(ui, opts)
571 timer, fm = gettimer(ui, opts)
572 heads = repo.changelog.headrevs()
572 heads = repo.changelog.headrevs()
573 def d():
573 def d():
574 for a in repo.changelog.ancestors(heads):
574 for a in repo.changelog.ancestors(heads):
575 pass
575 pass
576 timer(d)
576 timer(d)
577 fm.end()
577 fm.end()
578
578
579 @command(b'perfancestorset', formatteropts)
579 @command(b'perfancestorset', formatteropts)
580 def perfancestorset(ui, repo, revset, **opts):
580 def perfancestorset(ui, repo, revset, **opts):
581 opts = _byteskwargs(opts)
581 opts = _byteskwargs(opts)
582 timer, fm = gettimer(ui, opts)
582 timer, fm = gettimer(ui, opts)
583 revs = repo.revs(revset)
583 revs = repo.revs(revset)
584 heads = repo.changelog.headrevs()
584 heads = repo.changelog.headrevs()
585 def d():
585 def d():
586 s = repo.changelog.ancestors(heads)
586 s = repo.changelog.ancestors(heads)
587 for rev in revs:
587 for rev in revs:
588 rev in s
588 rev in s
589 timer(d)
589 timer(d)
590 fm.end()
590 fm.end()
591
591
592 @command(b'perfdiscovery', formatteropts, b'PATH')
592 @command(b'perfdiscovery', formatteropts, b'PATH')
593 def perfdiscovery(ui, repo, path, **opts):
593 def perfdiscovery(ui, repo, path, **opts):
594 """benchmark discovery between local repo and the peer at given path
594 """benchmark discovery between local repo and the peer at given path
595 """
595 """
596 repos = [repo, None]
596 repos = [repo, None]
597 timer, fm = gettimer(ui, opts)
597 timer, fm = gettimer(ui, opts)
598 path = ui.expandpath(path)
598 path = ui.expandpath(path)
599
599
600 def s():
600 def s():
601 repos[1] = hg.peer(ui, opts, path)
601 repos[1] = hg.peer(ui, opts, path)
602 def d():
602 def d():
603 setdiscovery.findcommonheads(ui, *repos)
603 setdiscovery.findcommonheads(ui, *repos)
604 timer(d, setup=s)
604 timer(d, setup=s)
605 fm.end()
605 fm.end()
606
606
607 @command(b'perfbookmarks', formatteropts +
607 @command(b'perfbookmarks', formatteropts +
608 [
608 [
609 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
609 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
610 ])
610 ])
611 def perfbookmarks(ui, repo, **opts):
611 def perfbookmarks(ui, repo, **opts):
612 """benchmark parsing bookmarks from disk to memory"""
612 """benchmark parsing bookmarks from disk to memory"""
613 opts = _byteskwargs(opts)
613 opts = _byteskwargs(opts)
614 timer, fm = gettimer(ui, opts)
614 timer, fm = gettimer(ui, opts)
615
615
616 clearrevlogs = opts[b'clear_revlogs']
616 clearrevlogs = opts[b'clear_revlogs']
617 def s():
617 def s():
618 if clearrevlogs:
618 if clearrevlogs:
619 clearchangelog(repo)
619 clearchangelog(repo)
620 clearfilecache(repo, b'_bookmarks')
620 clearfilecache(repo, b'_bookmarks')
621 def d():
621 def d():
622 repo._bookmarks
622 repo._bookmarks
623 timer(d, setup=s)
623 timer(d, setup=s)
624 fm.end()
624 fm.end()
625
625
626 @command(b'perfbundleread', formatteropts, b'BUNDLE')
626 @command(b'perfbundleread', formatteropts, b'BUNDLE')
627 def perfbundleread(ui, repo, bundlepath, **opts):
627 def perfbundleread(ui, repo, bundlepath, **opts):
628 """Benchmark reading of bundle files.
628 """Benchmark reading of bundle files.
629
629
630 This command is meant to isolate the I/O part of bundle reading as
630 This command is meant to isolate the I/O part of bundle reading as
631 much as possible.
631 much as possible.
632 """
632 """
633 from mercurial import (
633 from mercurial import (
634 bundle2,
634 bundle2,
635 exchange,
635 exchange,
636 streamclone,
636 streamclone,
637 )
637 )
638
638
639 opts = _byteskwargs(opts)
639 opts = _byteskwargs(opts)
640
640
641 def makebench(fn):
641 def makebench(fn):
642 def run():
642 def run():
643 with open(bundlepath, b'rb') as fh:
643 with open(bundlepath, b'rb') as fh:
644 bundle = exchange.readbundle(ui, fh, bundlepath)
644 bundle = exchange.readbundle(ui, fh, bundlepath)
645 fn(bundle)
645 fn(bundle)
646
646
647 return run
647 return run
648
648
649 def makereadnbytes(size):
649 def makereadnbytes(size):
650 def run():
650 def run():
651 with open(bundlepath, b'rb') as fh:
651 with open(bundlepath, b'rb') as fh:
652 bundle = exchange.readbundle(ui, fh, bundlepath)
652 bundle = exchange.readbundle(ui, fh, bundlepath)
653 while bundle.read(size):
653 while bundle.read(size):
654 pass
654 pass
655
655
656 return run
656 return run
657
657
658 def makestdioread(size):
658 def makestdioread(size):
659 def run():
659 def run():
660 with open(bundlepath, b'rb') as fh:
660 with open(bundlepath, b'rb') as fh:
661 while fh.read(size):
661 while fh.read(size):
662 pass
662 pass
663
663
664 return run
664 return run
665
665
666 # bundle1
666 # bundle1
667
667
668 def deltaiter(bundle):
668 def deltaiter(bundle):
669 for delta in bundle.deltaiter():
669 for delta in bundle.deltaiter():
670 pass
670 pass
671
671
672 def iterchunks(bundle):
672 def iterchunks(bundle):
673 for chunk in bundle.getchunks():
673 for chunk in bundle.getchunks():
674 pass
674 pass
675
675
676 # bundle2
676 # bundle2
677
677
678 def forwardchunks(bundle):
678 def forwardchunks(bundle):
679 for chunk in bundle._forwardchunks():
679 for chunk in bundle._forwardchunks():
680 pass
680 pass
681
681
682 def iterparts(bundle):
682 def iterparts(bundle):
683 for part in bundle.iterparts():
683 for part in bundle.iterparts():
684 pass
684 pass
685
685
686 def iterpartsseekable(bundle):
686 def iterpartsseekable(bundle):
687 for part in bundle.iterparts(seekable=True):
687 for part in bundle.iterparts(seekable=True):
688 pass
688 pass
689
689
690 def seek(bundle):
690 def seek(bundle):
691 for part in bundle.iterparts(seekable=True):
691 for part in bundle.iterparts(seekable=True):
692 part.seek(0, os.SEEK_END)
692 part.seek(0, os.SEEK_END)
693
693
694 def makepartreadnbytes(size):
694 def makepartreadnbytes(size):
695 def run():
695 def run():
696 with open(bundlepath, b'rb') as fh:
696 with open(bundlepath, b'rb') as fh:
697 bundle = exchange.readbundle(ui, fh, bundlepath)
697 bundle = exchange.readbundle(ui, fh, bundlepath)
698 for part in bundle.iterparts():
698 for part in bundle.iterparts():
699 while part.read(size):
699 while part.read(size):
700 pass
700 pass
701
701
702 return run
702 return run
703
703
704 benches = [
704 benches = [
705 (makestdioread(8192), b'read(8k)'),
705 (makestdioread(8192), b'read(8k)'),
706 (makestdioread(16384), b'read(16k)'),
706 (makestdioread(16384), b'read(16k)'),
707 (makestdioread(32768), b'read(32k)'),
707 (makestdioread(32768), b'read(32k)'),
708 (makestdioread(131072), b'read(128k)'),
708 (makestdioread(131072), b'read(128k)'),
709 ]
709 ]
710
710
711 with open(bundlepath, b'rb') as fh:
711 with open(bundlepath, b'rb') as fh:
712 bundle = exchange.readbundle(ui, fh, bundlepath)
712 bundle = exchange.readbundle(ui, fh, bundlepath)
713
713
714 if isinstance(bundle, changegroup.cg1unpacker):
714 if isinstance(bundle, changegroup.cg1unpacker):
715 benches.extend([
715 benches.extend([
716 (makebench(deltaiter), b'cg1 deltaiter()'),
716 (makebench(deltaiter), b'cg1 deltaiter()'),
717 (makebench(iterchunks), b'cg1 getchunks()'),
717 (makebench(iterchunks), b'cg1 getchunks()'),
718 (makereadnbytes(8192), b'cg1 read(8k)'),
718 (makereadnbytes(8192), b'cg1 read(8k)'),
719 (makereadnbytes(16384), b'cg1 read(16k)'),
719 (makereadnbytes(16384), b'cg1 read(16k)'),
720 (makereadnbytes(32768), b'cg1 read(32k)'),
720 (makereadnbytes(32768), b'cg1 read(32k)'),
721 (makereadnbytes(131072), b'cg1 read(128k)'),
721 (makereadnbytes(131072), b'cg1 read(128k)'),
722 ])
722 ])
723 elif isinstance(bundle, bundle2.unbundle20):
723 elif isinstance(bundle, bundle2.unbundle20):
724 benches.extend([
724 benches.extend([
725 (makebench(forwardchunks), b'bundle2 forwardchunks()'),
725 (makebench(forwardchunks), b'bundle2 forwardchunks()'),
726 (makebench(iterparts), b'bundle2 iterparts()'),
726 (makebench(iterparts), b'bundle2 iterparts()'),
727 (makebench(iterpartsseekable), b'bundle2 iterparts() seekable'),
727 (makebench(iterpartsseekable), b'bundle2 iterparts() seekable'),
728 (makebench(seek), b'bundle2 part seek()'),
728 (makebench(seek), b'bundle2 part seek()'),
729 (makepartreadnbytes(8192), b'bundle2 part read(8k)'),
729 (makepartreadnbytes(8192), b'bundle2 part read(8k)'),
730 (makepartreadnbytes(16384), b'bundle2 part read(16k)'),
730 (makepartreadnbytes(16384), b'bundle2 part read(16k)'),
731 (makepartreadnbytes(32768), b'bundle2 part read(32k)'),
731 (makepartreadnbytes(32768), b'bundle2 part read(32k)'),
732 (makepartreadnbytes(131072), b'bundle2 part read(128k)'),
732 (makepartreadnbytes(131072), b'bundle2 part read(128k)'),
733 ])
733 ])
734 elif isinstance(bundle, streamclone.streamcloneapplier):
734 elif isinstance(bundle, streamclone.streamcloneapplier):
735 raise error.Abort(b'stream clone bundles not supported')
735 raise error.Abort(b'stream clone bundles not supported')
736 else:
736 else:
737 raise error.Abort(b'unhandled bundle type: %s' % type(bundle))
737 raise error.Abort(b'unhandled bundle type: %s' % type(bundle))
738
738
739 for fn, title in benches:
739 for fn, title in benches:
740 timer, fm = gettimer(ui, opts)
740 timer, fm = gettimer(ui, opts)
741 timer(fn, title=title)
741 timer(fn, title=title)
742 fm.end()
742 fm.end()
743
743
744 @command(b'perfchangegroupchangelog', formatteropts +
744 @command(b'perfchangegroupchangelog', formatteropts +
745 [(b'', b'cgversion', b'02', b'changegroup version'),
745 [(b'', b'cgversion', b'02', b'changegroup version'),
746 (b'r', b'rev', b'', b'revisions to add to changegroup')])
746 (b'r', b'rev', b'', b'revisions to add to changegroup')])
747 def perfchangegroupchangelog(ui, repo, cgversion=b'02', rev=None, **opts):
747 def perfchangegroupchangelog(ui, repo, cgversion=b'02', rev=None, **opts):
748 """Benchmark producing a changelog group for a changegroup.
748 """Benchmark producing a changelog group for a changegroup.
749
749
750 This measures the time spent processing the changelog during a
750 This measures the time spent processing the changelog during a
751 bundle operation. This occurs during `hg bundle` and on a server
751 bundle operation. This occurs during `hg bundle` and on a server
752 processing a `getbundle` wire protocol request (handles clones
752 processing a `getbundle` wire protocol request (handles clones
753 and pull requests).
753 and pull requests).
754
754
755 By default, all revisions are added to the changegroup.
755 By default, all revisions are added to the changegroup.
756 """
756 """
757 opts = _byteskwargs(opts)
757 opts = _byteskwargs(opts)
758 cl = repo.changelog
758 cl = repo.changelog
759 nodes = [cl.lookup(r) for r in repo.revs(rev or b'all()')]
759 nodes = [cl.lookup(r) for r in repo.revs(rev or b'all()')]
760 bundler = changegroup.getbundler(cgversion, repo)
760 bundler = changegroup.getbundler(cgversion, repo)
761
761
762 def d():
762 def d():
763 state, chunks = bundler._generatechangelog(cl, nodes)
763 state, chunks = bundler._generatechangelog(cl, nodes)
764 for chunk in chunks:
764 for chunk in chunks:
765 pass
765 pass
766
766
767 timer, fm = gettimer(ui, opts)
767 timer, fm = gettimer(ui, opts)
768
768
769 # Terminal printing can interfere with timing. So disable it.
769 # Terminal printing can interfere with timing. So disable it.
770 with ui.configoverride({(b'progress', b'disable'): True}):
770 with ui.configoverride({(b'progress', b'disable'): True}):
771 timer(d)
771 timer(d)
772
772
773 fm.end()
773 fm.end()
774
774
775 @command(b'perfdirs', formatteropts)
775 @command(b'perfdirs', formatteropts)
776 def perfdirs(ui, repo, **opts):
776 def perfdirs(ui, repo, **opts):
777 opts = _byteskwargs(opts)
777 opts = _byteskwargs(opts)
778 timer, fm = gettimer(ui, opts)
778 timer, fm = gettimer(ui, opts)
779 dirstate = repo.dirstate
779 dirstate = repo.dirstate
780 b'a' in dirstate
780 b'a' in dirstate
781 def d():
781 def d():
782 dirstate.hasdir(b'a')
782 dirstate.hasdir(b'a')
783 del dirstate._map._dirs
783 del dirstate._map._dirs
784 timer(d)
784 timer(d)
785 fm.end()
785 fm.end()
786
786
787 @command(b'perfdirstate', formatteropts)
787 @command(b'perfdirstate', formatteropts)
788 def perfdirstate(ui, repo, **opts):
788 def perfdirstate(ui, repo, **opts):
789 opts = _byteskwargs(opts)
789 opts = _byteskwargs(opts)
790 timer, fm = gettimer(ui, opts)
790 timer, fm = gettimer(ui, opts)
791 b"a" in repo.dirstate
791 b"a" in repo.dirstate
792 def d():
792 def d():
793 repo.dirstate.invalidate()
793 repo.dirstate.invalidate()
794 b"a" in repo.dirstate
794 b"a" in repo.dirstate
795 timer(d)
795 timer(d)
796 fm.end()
796 fm.end()
797
797
798 @command(b'perfdirstatedirs', formatteropts)
798 @command(b'perfdirstatedirs', formatteropts)
799 def perfdirstatedirs(ui, repo, **opts):
799 def perfdirstatedirs(ui, repo, **opts):
800 opts = _byteskwargs(opts)
800 opts = _byteskwargs(opts)
801 timer, fm = gettimer(ui, opts)
801 timer, fm = gettimer(ui, opts)
802 b"a" in repo.dirstate
802 b"a" in repo.dirstate
803 def d():
803 def d():
804 repo.dirstate.hasdir(b"a")
804 repo.dirstate.hasdir(b"a")
805 del repo.dirstate._map._dirs
805 del repo.dirstate._map._dirs
806 timer(d)
806 timer(d)
807 fm.end()
807 fm.end()
808
808
809 @command(b'perfdirstatefoldmap', formatteropts)
809 @command(b'perfdirstatefoldmap', formatteropts)
810 def perfdirstatefoldmap(ui, repo, **opts):
810 def perfdirstatefoldmap(ui, repo, **opts):
811 opts = _byteskwargs(opts)
811 opts = _byteskwargs(opts)
812 timer, fm = gettimer(ui, opts)
812 timer, fm = gettimer(ui, opts)
813 dirstate = repo.dirstate
813 dirstate = repo.dirstate
814 b'a' in dirstate
814 b'a' in dirstate
815 def d():
815 def d():
816 dirstate._map.filefoldmap.get(b'a')
816 dirstate._map.filefoldmap.get(b'a')
817 del dirstate._map.filefoldmap
817 del dirstate._map.filefoldmap
818 timer(d)
818 timer(d)
819 fm.end()
819 fm.end()
820
820
821 @command(b'perfdirfoldmap', formatteropts)
821 @command(b'perfdirfoldmap', formatteropts)
822 def perfdirfoldmap(ui, repo, **opts):
822 def perfdirfoldmap(ui, repo, **opts):
823 opts = _byteskwargs(opts)
823 opts = _byteskwargs(opts)
824 timer, fm = gettimer(ui, opts)
824 timer, fm = gettimer(ui, opts)
825 dirstate = repo.dirstate
825 dirstate = repo.dirstate
826 b'a' in dirstate
826 b'a' in dirstate
827 def d():
827 def d():
828 dirstate._map.dirfoldmap.get(b'a')
828 dirstate._map.dirfoldmap.get(b'a')
829 del dirstate._map.dirfoldmap
829 del dirstate._map.dirfoldmap
830 del dirstate._map._dirs
830 del dirstate._map._dirs
831 timer(d)
831 timer(d)
832 fm.end()
832 fm.end()
833
833
834 @command(b'perfdirstatewrite', formatteropts)
834 @command(b'perfdirstatewrite', formatteropts)
835 def perfdirstatewrite(ui, repo, **opts):
835 def perfdirstatewrite(ui, repo, **opts):
836 opts = _byteskwargs(opts)
836 opts = _byteskwargs(opts)
837 timer, fm = gettimer(ui, opts)
837 timer, fm = gettimer(ui, opts)
838 ds = repo.dirstate
838 ds = repo.dirstate
839 b"a" in ds
839 b"a" in ds
840 def d():
840 def d():
841 ds._dirty = True
841 ds._dirty = True
842 ds.write(repo.currenttransaction())
842 ds.write(repo.currenttransaction())
843 timer(d)
843 timer(d)
844 fm.end()
844 fm.end()
845
845
846 @command(b'perfmergecalculate',
846 @command(b'perfmergecalculate',
847 [(b'r', b'rev', b'.', b'rev to merge against')] + formatteropts)
847 [(b'r', b'rev', b'.', b'rev to merge against')] + formatteropts)
848 def perfmergecalculate(ui, repo, rev, **opts):
848 def perfmergecalculate(ui, repo, rev, **opts):
849 opts = _byteskwargs(opts)
849 opts = _byteskwargs(opts)
850 timer, fm = gettimer(ui, opts)
850 timer, fm = gettimer(ui, opts)
851 wctx = repo[None]
851 wctx = repo[None]
852 rctx = scmutil.revsingle(repo, rev, rev)
852 rctx = scmutil.revsingle(repo, rev, rev)
853 ancestor = wctx.ancestor(rctx)
853 ancestor = wctx.ancestor(rctx)
854 # we don't want working dir files to be stat'd in the benchmark, so prime
854 # we don't want working dir files to be stat'd in the benchmark, so prime
855 # that cache
855 # that cache
856 wctx.dirty()
856 wctx.dirty()
857 def d():
857 def d():
858 # acceptremote is True because we don't want prompts in the middle of
858 # acceptremote is True because we don't want prompts in the middle of
859 # our benchmark
859 # our benchmark
860 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
860 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
861 acceptremote=True, followcopies=True)
861 acceptremote=True, followcopies=True)
862 timer(d)
862 timer(d)
863 fm.end()
863 fm.end()
864
864
865 @command(b'perfpathcopies', [], b"REV REV")
865 @command(b'perfpathcopies', [], b"REV REV")
866 def perfpathcopies(ui, repo, rev1, rev2, **opts):
866 def perfpathcopies(ui, repo, rev1, rev2, **opts):
867 """benchmark the copy tracing logic"""
867 """benchmark the copy tracing logic"""
868 opts = _byteskwargs(opts)
868 opts = _byteskwargs(opts)
869 timer, fm = gettimer(ui, opts)
869 timer, fm = gettimer(ui, opts)
870 ctx1 = scmutil.revsingle(repo, rev1, rev1)
870 ctx1 = scmutil.revsingle(repo, rev1, rev1)
871 ctx2 = scmutil.revsingle(repo, rev2, rev2)
871 ctx2 = scmutil.revsingle(repo, rev2, rev2)
872 def d():
872 def d():
873 copies.pathcopies(ctx1, ctx2)
873 copies.pathcopies(ctx1, ctx2)
874 timer(d)
874 timer(d)
875 fm.end()
875 fm.end()
876
876
877 @command(b'perfphases',
877 @command(b'perfphases',
878 [(b'', b'full', False, b'include file reading time too'),
878 [(b'', b'full', False, b'include file reading time too'),
879 ], b"")
879 ], b"")
880 def perfphases(ui, repo, **opts):
880 def perfphases(ui, repo, **opts):
881 """benchmark phasesets computation"""
881 """benchmark phasesets computation"""
882 opts = _byteskwargs(opts)
882 opts = _byteskwargs(opts)
883 timer, fm = gettimer(ui, opts)
883 timer, fm = gettimer(ui, opts)
884 _phases = repo._phasecache
884 _phases = repo._phasecache
885 full = opts.get(b'full')
885 full = opts.get(b'full')
886 def d():
886 def d():
887 phases = _phases
887 phases = _phases
888 if full:
888 if full:
889 clearfilecache(repo, b'_phasecache')
889 clearfilecache(repo, b'_phasecache')
890 phases = repo._phasecache
890 phases = repo._phasecache
891 phases.invalidate()
891 phases.invalidate()
892 phases.loadphaserevs(repo)
892 phases.loadphaserevs(repo)
893 timer(d)
893 timer(d)
894 fm.end()
894 fm.end()
895
895
896 @command(b'perfphasesremote',
896 @command(b'perfphasesremote',
897 [], b"[DEST]")
897 [], b"[DEST]")
898 def perfphasesremote(ui, repo, dest=None, **opts):
898 def perfphasesremote(ui, repo, dest=None, **opts):
899 """benchmark time needed to analyse phases of the remote server"""
899 """benchmark time needed to analyse phases of the remote server"""
900 from mercurial.node import (
900 from mercurial.node import (
901 bin,
901 bin,
902 )
902 )
903 from mercurial import (
903 from mercurial import (
904 exchange,
904 exchange,
905 hg,
905 hg,
906 phases,
906 phases,
907 )
907 )
908 opts = _byteskwargs(opts)
908 opts = _byteskwargs(opts)
909 timer, fm = gettimer(ui, opts)
909 timer, fm = gettimer(ui, opts)
910
910
911 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
911 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
912 if not path:
912 if not path:
913 raise error.Abort((b'default repository not configured!'),
913 raise error.Abort((b'default repository not configured!'),
914 hint=(b"see 'hg help config.paths'"))
914 hint=(b"see 'hg help config.paths'"))
915 dest = path.pushloc or path.loc
915 dest = path.pushloc or path.loc
916 ui.status((b'analysing phase of %s\n') % util.hidepassword(dest))
916 ui.status((b'analysing phase of %s\n') % util.hidepassword(dest))
917 other = hg.peer(repo, opts, dest)
917 other = hg.peer(repo, opts, dest)
918
918
919 # easier to perform discovery through the operation
919 # easier to perform discovery through the operation
920 op = exchange.pushoperation(repo, other)
920 op = exchange.pushoperation(repo, other)
921 exchange._pushdiscoverychangeset(op)
921 exchange._pushdiscoverychangeset(op)
922
922
923 remotesubset = op.fallbackheads
923 remotesubset = op.fallbackheads
924
924
925 with other.commandexecutor() as e:
925 with other.commandexecutor() as e:
926 remotephases = e.callcommand(b'listkeys',
926 remotephases = e.callcommand(b'listkeys',
927 {b'namespace': b'phases'}).result()
927 {b'namespace': b'phases'}).result()
928 del other
928 del other
929 publishing = remotephases.get(b'publishing', False)
929 publishing = remotephases.get(b'publishing', False)
930 if publishing:
930 if publishing:
931 ui.status((b'publishing: yes\n'))
931 ui.status((b'publishing: yes\n'))
932 else:
932 else:
933 ui.status((b'publishing: no\n'))
933 ui.status((b'publishing: no\n'))
934
934
935 nodemap = repo.changelog.nodemap
935 nodemap = repo.changelog.nodemap
936 nonpublishroots = 0
936 nonpublishroots = 0
937 for nhex, phase in remotephases.iteritems():
937 for nhex, phase in remotephases.iteritems():
938 if nhex == b'publishing': # ignore data related to publish option
938 if nhex == b'publishing': # ignore data related to publish option
939 continue
939 continue
940 node = bin(nhex)
940 node = bin(nhex)
941 if node in nodemap and int(phase):
941 if node in nodemap and int(phase):
942 nonpublishroots += 1
942 nonpublishroots += 1
943 ui.status((b'number of roots: %d\n') % len(remotephases))
943 ui.status((b'number of roots: %d\n') % len(remotephases))
944 ui.status((b'number of known non public roots: %d\n') % nonpublishroots)
944 ui.status((b'number of known non public roots: %d\n') % nonpublishroots)
945 def d():
945 def d():
946 phases.remotephasessummary(repo,
946 phases.remotephasessummary(repo,
947 remotesubset,
947 remotesubset,
948 remotephases)
948 remotephases)
949 timer(d)
949 timer(d)
950 fm.end()
950 fm.end()
951
951
952 @command(b'perfmanifest',[
952 @command(b'perfmanifest',[
953 (b'm', b'manifest-rev', False, b'Look up a manifest node revision'),
953 (b'm', b'manifest-rev', False, b'Look up a manifest node revision'),
954 (b'', b'clear-disk', False, b'clear on-disk caches too'),
954 (b'', b'clear-disk', False, b'clear on-disk caches too'),
955 ] + formatteropts, b'REV|NODE')
955 ] + formatteropts, b'REV|NODE')
956 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
956 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
957 """benchmark the time to read a manifest from disk and return a usable
957 """benchmark the time to read a manifest from disk and return a usable
958 dict-like object
958 dict-like object
959
959
960 Manifest caches are cleared before retrieval."""
960 Manifest caches are cleared before retrieval."""
961 opts = _byteskwargs(opts)
961 opts = _byteskwargs(opts)
962 timer, fm = gettimer(ui, opts)
962 timer, fm = gettimer(ui, opts)
963 if not manifest_rev:
963 if not manifest_rev:
964 ctx = scmutil.revsingle(repo, rev, rev)
964 ctx = scmutil.revsingle(repo, rev, rev)
965 t = ctx.manifestnode()
965 t = ctx.manifestnode()
966 else:
966 else:
967 from mercurial.node import bin
967 from mercurial.node import bin
968
968
969 if len(rev) == 40:
969 if len(rev) == 40:
970 t = bin(rev)
970 t = bin(rev)
971 else:
971 else:
972 try:
972 try:
973 rev = int(rev)
973 rev = int(rev)
974
974
975 if util.safehasattr(repo.manifestlog, b'getstorage'):
975 if util.safehasattr(repo.manifestlog, b'getstorage'):
976 t = repo.manifestlog.getstorage(b'').node(rev)
976 t = repo.manifestlog.getstorage(b'').node(rev)
977 else:
977 else:
978 t = repo.manifestlog._revlog.lookup(rev)
978 t = repo.manifestlog._revlog.lookup(rev)
979 except ValueError:
979 except ValueError:
980 raise error.Abort(b'manifest revision must be integer or full '
980 raise error.Abort(b'manifest revision must be integer or full '
981 b'node')
981 b'node')
982 def d():
982 def d():
983 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
983 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
984 repo.manifestlog[t].read()
984 repo.manifestlog[t].read()
985 timer(d)
985 timer(d)
986 fm.end()
986 fm.end()
987
987
988 @command(b'perfchangeset', formatteropts)
988 @command(b'perfchangeset', formatteropts)
989 def perfchangeset(ui, repo, rev, **opts):
989 def perfchangeset(ui, repo, rev, **opts):
990 opts = _byteskwargs(opts)
990 opts = _byteskwargs(opts)
991 timer, fm = gettimer(ui, opts)
991 timer, fm = gettimer(ui, opts)
992 n = scmutil.revsingle(repo, rev).node()
992 n = scmutil.revsingle(repo, rev).node()
993 def d():
993 def d():
994 repo.changelog.read(n)
994 repo.changelog.read(n)
995 #repo.changelog._cache = None
995 #repo.changelog._cache = None
996 timer(d)
996 timer(d)
997 fm.end()
997 fm.end()
998
998
999 @command(b'perfignore', formatteropts)
999 @command(b'perfignore', formatteropts)
1000 def perfignore(ui, repo, **opts):
1000 def perfignore(ui, repo, **opts):
1001 """benchmark operation related to computing ignore"""
1001 """benchmark operation related to computing ignore"""
1002 opts = _byteskwargs(opts)
1002 opts = _byteskwargs(opts)
1003 timer, fm = gettimer(ui, opts)
1003 timer, fm = gettimer(ui, opts)
1004 dirstate = repo.dirstate
1004 dirstate = repo.dirstate
1005
1005
1006 def setupone():
1006 def setupone():
1007 dirstate.invalidate()
1007 dirstate.invalidate()
1008 clearfilecache(dirstate, b'_ignore')
1008 clearfilecache(dirstate, b'_ignore')
1009
1009
1010 def runone():
1010 def runone():
1011 dirstate._ignore
1011 dirstate._ignore
1012
1012
1013 timer(runone, setup=setupone, title=b"load")
1013 timer(runone, setup=setupone, title=b"load")
1014 fm.end()
1014 fm.end()
1015
1015
1016 @command(b'perfindex', [
1016 @command(b'perfindex', [
1017 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1017 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1018 (b'', b'no-lookup', None, b'do not revision lookup post creation'),
1018 (b'', b'no-lookup', None, b'do not revision lookup post creation'),
1019 ] + formatteropts)
1019 ] + formatteropts)
1020 def perfindex(ui, repo, **opts):
1020 def perfindex(ui, repo, **opts):
1021 """benchmark index creation time followed by a lookup
1021 """benchmark index creation time followed by a lookup
1022
1022
1023 The default is to look `tip` up. Depending on the index implementation,
1023 The default is to look `tip` up. Depending on the index implementation,
1024 the revision looked up can matters. For example, an implementation
1024 the revision looked up can matters. For example, an implementation
1025 scanning the index will have a faster lookup time for `--rev tip` than for
1025 scanning the index will have a faster lookup time for `--rev tip` than for
1026 `--rev 0`. The number of looked up revisions and their order can also
1026 `--rev 0`. The number of looked up revisions and their order can also
1027 matters.
1027 matters.
1028
1028
1029 Example of useful set to test:
1029 Example of useful set to test:
1030 * tip
1030 * tip
1031 * 0
1031 * 0
1032 * -10:
1032 * -10:
1033 * :10
1033 * :10
1034 * -10: + :10
1034 * -10: + :10
1035 * :10: + -10:
1035 * :10: + -10:
1036 * -10000:
1036 * -10000:
1037 * -10000: + 0
1037 * -10000: + 0
1038
1038
1039 It is not currently possible to check for lookup of a missing node."""
1039 It is not currently possible to check for lookup of a missing node. For
1040 deeper lookup benchmarking, checkout the `perfnodemap` command."""
1040 import mercurial.revlog
1041 import mercurial.revlog
1041 opts = _byteskwargs(opts)
1042 opts = _byteskwargs(opts)
1042 timer, fm = gettimer(ui, opts)
1043 timer, fm = gettimer(ui, opts)
1043 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
1044 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
1044 if opts[b'no_lookup']:
1045 if opts[b'no_lookup']:
1045 if opts['rev']:
1046 if opts['rev']:
1046 raise error.Abort('--no-lookup and --rev are mutually exclusive')
1047 raise error.Abort('--no-lookup and --rev are mutually exclusive')
1047 nodes = []
1048 nodes = []
1048 elif not opts[b'rev']:
1049 elif not opts[b'rev']:
1049 nodes = [repo[b"tip"].node()]
1050 nodes = [repo[b"tip"].node()]
1050 else:
1051 else:
1051 revs = scmutil.revrange(repo, opts[b'rev'])
1052 revs = scmutil.revrange(repo, opts[b'rev'])
1052 cl = repo.changelog
1053 cl = repo.changelog
1053 nodes = [cl.node(r) for r in revs]
1054 nodes = [cl.node(r) for r in revs]
1054
1055
1055 unfi = repo.unfiltered()
1056 unfi = repo.unfiltered()
1056 # find the filecache func directly
1057 # find the filecache func directly
1057 # This avoid polluting the benchmark with the filecache logic
1058 # This avoid polluting the benchmark with the filecache logic
1058 makecl = unfi.__class__.changelog.func
1059 makecl = unfi.__class__.changelog.func
1059 def setup():
1060 def setup():
1060 # probably not necessary, but for good measure
1061 # probably not necessary, but for good measure
1061 clearchangelog(unfi)
1062 clearchangelog(unfi)
1062 def d():
1063 def d():
1063 cl = makecl(unfi)
1064 cl = makecl(unfi)
1064 for n in nodes:
1065 for n in nodes:
1065 cl.rev(n)
1066 cl.rev(n)
1066 timer(d, setup=setup)
1067 timer(d, setup=setup)
1067 fm.end()
1068 fm.end()
1068
1069
1070 @command(b'perfnodemap', [
1071 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1072 ] + formatteropts)
1073 def perfnodemap(ui, repo, **opts):
1074 """benchmark the time necessary to look up revision from a cold nodemap
1075
1076 Depending on the implementation, the amount and order of revision we look
1077 up can varies. Example of useful set to test:
1078 * tip
1079 * 0
1080 * -10:
1081 * :10
1082 * -10: + :10
1083 * :10: + -10:
1084 * -10000:
1085 * -10000: + 0
1086
1087 The command currently focus on valid binary lookup. Benchmarking for
1088 hexlookup, prefix lookup and missing lookup would also be valuable.
1089 """
1090 import mercurial.revlog
1091 opts = _byteskwargs(opts)
1092 timer, fm = gettimer(ui, opts)
1093 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
1094
1095 unfi = repo.unfiltered()
1096 # find the filecache func directly
1097 # This avoid polluting the benchmark with the filecache logic
1098 makecl = unfi.__class__.changelog.func
1099 if not opts[b'rev']:
1100 raise error.Abort('use --rev to specify revisions to look up')
1101 revs = scmutil.revrange(repo, opts[b'rev'])
1102 cl = repo.changelog
1103 nodes = [cl.node(r) for r in revs]
1104
1105 # use a list to pass reference to a nodemap from one closure to the next
1106 nodeget = [None]
1107 def setnodeget():
1108 # probably not necessary, but for good measure
1109 clearchangelog(unfi)
1110 nodeget[0] = makecl(unfi).nodemap.get
1111
1112 def setup():
1113 setnodeget()
1114 def d():
1115 get = nodeget[0]
1116 for n in nodes:
1117 get(n)
1118
1119 timer(d, setup=setup)
1120 fm.end()
1121
1069 @command(b'perfstartup', formatteropts)
1122 @command(b'perfstartup', formatteropts)
1070 def perfstartup(ui, repo, **opts):
1123 def perfstartup(ui, repo, **opts):
1071 opts = _byteskwargs(opts)
1124 opts = _byteskwargs(opts)
1072 timer, fm = gettimer(ui, opts)
1125 timer, fm = gettimer(ui, opts)
1073 def d():
1126 def d():
1074 if os.name != r'nt':
1127 if os.name != r'nt':
1075 os.system(b"HGRCPATH= %s version -q > /dev/null" %
1128 os.system(b"HGRCPATH= %s version -q > /dev/null" %
1076 fsencode(sys.argv[0]))
1129 fsencode(sys.argv[0]))
1077 else:
1130 else:
1078 os.environ[r'HGRCPATH'] = r' '
1131 os.environ[r'HGRCPATH'] = r' '
1079 os.system(r"%s version -q > NUL" % sys.argv[0])
1132 os.system(r"%s version -q > NUL" % sys.argv[0])
1080 timer(d)
1133 timer(d)
1081 fm.end()
1134 fm.end()
1082
1135
1083 @command(b'perfparents', formatteropts)
1136 @command(b'perfparents', formatteropts)
1084 def perfparents(ui, repo, **opts):
1137 def perfparents(ui, repo, **opts):
1085 opts = _byteskwargs(opts)
1138 opts = _byteskwargs(opts)
1086 timer, fm = gettimer(ui, opts)
1139 timer, fm = gettimer(ui, opts)
1087 # control the number of commits perfparents iterates over
1140 # control the number of commits perfparents iterates over
1088 # experimental config: perf.parentscount
1141 # experimental config: perf.parentscount
1089 count = getint(ui, b"perf", b"parentscount", 1000)
1142 count = getint(ui, b"perf", b"parentscount", 1000)
1090 if len(repo.changelog) < count:
1143 if len(repo.changelog) < count:
1091 raise error.Abort(b"repo needs %d commits for this test" % count)
1144 raise error.Abort(b"repo needs %d commits for this test" % count)
1092 repo = repo.unfiltered()
1145 repo = repo.unfiltered()
1093 nl = [repo.changelog.node(i) for i in _xrange(count)]
1146 nl = [repo.changelog.node(i) for i in _xrange(count)]
1094 def d():
1147 def d():
1095 for n in nl:
1148 for n in nl:
1096 repo.changelog.parents(n)
1149 repo.changelog.parents(n)
1097 timer(d)
1150 timer(d)
1098 fm.end()
1151 fm.end()
1099
1152
1100 @command(b'perfctxfiles', formatteropts)
1153 @command(b'perfctxfiles', formatteropts)
1101 def perfctxfiles(ui, repo, x, **opts):
1154 def perfctxfiles(ui, repo, x, **opts):
1102 opts = _byteskwargs(opts)
1155 opts = _byteskwargs(opts)
1103 x = int(x)
1156 x = int(x)
1104 timer, fm = gettimer(ui, opts)
1157 timer, fm = gettimer(ui, opts)
1105 def d():
1158 def d():
1106 len(repo[x].files())
1159 len(repo[x].files())
1107 timer(d)
1160 timer(d)
1108 fm.end()
1161 fm.end()
1109
1162
1110 @command(b'perfrawfiles', formatteropts)
1163 @command(b'perfrawfiles', formatteropts)
1111 def perfrawfiles(ui, repo, x, **opts):
1164 def perfrawfiles(ui, repo, x, **opts):
1112 opts = _byteskwargs(opts)
1165 opts = _byteskwargs(opts)
1113 x = int(x)
1166 x = int(x)
1114 timer, fm = gettimer(ui, opts)
1167 timer, fm = gettimer(ui, opts)
1115 cl = repo.changelog
1168 cl = repo.changelog
1116 def d():
1169 def d():
1117 len(cl.read(x)[3])
1170 len(cl.read(x)[3])
1118 timer(d)
1171 timer(d)
1119 fm.end()
1172 fm.end()
1120
1173
1121 @command(b'perflookup', formatteropts)
1174 @command(b'perflookup', formatteropts)
1122 def perflookup(ui, repo, rev, **opts):
1175 def perflookup(ui, repo, rev, **opts):
1123 opts = _byteskwargs(opts)
1176 opts = _byteskwargs(opts)
1124 timer, fm = gettimer(ui, opts)
1177 timer, fm = gettimer(ui, opts)
1125 timer(lambda: len(repo.lookup(rev)))
1178 timer(lambda: len(repo.lookup(rev)))
1126 fm.end()
1179 fm.end()
1127
1180
1128 @command(b'perflinelogedits',
1181 @command(b'perflinelogedits',
1129 [(b'n', b'edits', 10000, b'number of edits'),
1182 [(b'n', b'edits', 10000, b'number of edits'),
1130 (b'', b'max-hunk-lines', 10, b'max lines in a hunk'),
1183 (b'', b'max-hunk-lines', 10, b'max lines in a hunk'),
1131 ], norepo=True)
1184 ], norepo=True)
1132 def perflinelogedits(ui, **opts):
1185 def perflinelogedits(ui, **opts):
1133 from mercurial import linelog
1186 from mercurial import linelog
1134
1187
1135 opts = _byteskwargs(opts)
1188 opts = _byteskwargs(opts)
1136
1189
1137 edits = opts[b'edits']
1190 edits = opts[b'edits']
1138 maxhunklines = opts[b'max_hunk_lines']
1191 maxhunklines = opts[b'max_hunk_lines']
1139
1192
1140 maxb1 = 100000
1193 maxb1 = 100000
1141 random.seed(0)
1194 random.seed(0)
1142 randint = random.randint
1195 randint = random.randint
1143 currentlines = 0
1196 currentlines = 0
1144 arglist = []
1197 arglist = []
1145 for rev in _xrange(edits):
1198 for rev in _xrange(edits):
1146 a1 = randint(0, currentlines)
1199 a1 = randint(0, currentlines)
1147 a2 = randint(a1, min(currentlines, a1 + maxhunklines))
1200 a2 = randint(a1, min(currentlines, a1 + maxhunklines))
1148 b1 = randint(0, maxb1)
1201 b1 = randint(0, maxb1)
1149 b2 = randint(b1, b1 + maxhunklines)
1202 b2 = randint(b1, b1 + maxhunklines)
1150 currentlines += (b2 - b1) - (a2 - a1)
1203 currentlines += (b2 - b1) - (a2 - a1)
1151 arglist.append((rev, a1, a2, b1, b2))
1204 arglist.append((rev, a1, a2, b1, b2))
1152
1205
1153 def d():
1206 def d():
1154 ll = linelog.linelog()
1207 ll = linelog.linelog()
1155 for args in arglist:
1208 for args in arglist:
1156 ll.replacelines(*args)
1209 ll.replacelines(*args)
1157
1210
1158 timer, fm = gettimer(ui, opts)
1211 timer, fm = gettimer(ui, opts)
1159 timer(d)
1212 timer(d)
1160 fm.end()
1213 fm.end()
1161
1214
1162 @command(b'perfrevrange', formatteropts)
1215 @command(b'perfrevrange', formatteropts)
1163 def perfrevrange(ui, repo, *specs, **opts):
1216 def perfrevrange(ui, repo, *specs, **opts):
1164 opts = _byteskwargs(opts)
1217 opts = _byteskwargs(opts)
1165 timer, fm = gettimer(ui, opts)
1218 timer, fm = gettimer(ui, opts)
1166 revrange = scmutil.revrange
1219 revrange = scmutil.revrange
1167 timer(lambda: len(revrange(repo, specs)))
1220 timer(lambda: len(revrange(repo, specs)))
1168 fm.end()
1221 fm.end()
1169
1222
1170 @command(b'perfnodelookup', formatteropts)
1223 @command(b'perfnodelookup', formatteropts)
1171 def perfnodelookup(ui, repo, rev, **opts):
1224 def perfnodelookup(ui, repo, rev, **opts):
1172 opts = _byteskwargs(opts)
1225 opts = _byteskwargs(opts)
1173 timer, fm = gettimer(ui, opts)
1226 timer, fm = gettimer(ui, opts)
1174 import mercurial.revlog
1227 import mercurial.revlog
1175 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
1228 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
1176 n = scmutil.revsingle(repo, rev).node()
1229 n = scmutil.revsingle(repo, rev).node()
1177 cl = mercurial.revlog.revlog(getsvfs(repo), b"00changelog.i")
1230 cl = mercurial.revlog.revlog(getsvfs(repo), b"00changelog.i")
1178 def d():
1231 def d():
1179 cl.rev(n)
1232 cl.rev(n)
1180 clearcaches(cl)
1233 clearcaches(cl)
1181 timer(d)
1234 timer(d)
1182 fm.end()
1235 fm.end()
1183
1236
1184 @command(b'perflog',
1237 @command(b'perflog',
1185 [(b'', b'rename', False, b'ask log to follow renames')
1238 [(b'', b'rename', False, b'ask log to follow renames')
1186 ] + formatteropts)
1239 ] + formatteropts)
1187 def perflog(ui, repo, rev=None, **opts):
1240 def perflog(ui, repo, rev=None, **opts):
1188 opts = _byteskwargs(opts)
1241 opts = _byteskwargs(opts)
1189 if rev is None:
1242 if rev is None:
1190 rev=[]
1243 rev=[]
1191 timer, fm = gettimer(ui, opts)
1244 timer, fm = gettimer(ui, opts)
1192 ui.pushbuffer()
1245 ui.pushbuffer()
1193 timer(lambda: commands.log(ui, repo, rev=rev, date=b'', user=b'',
1246 timer(lambda: commands.log(ui, repo, rev=rev, date=b'', user=b'',
1194 copies=opts.get(b'rename')))
1247 copies=opts.get(b'rename')))
1195 ui.popbuffer()
1248 ui.popbuffer()
1196 fm.end()
1249 fm.end()
1197
1250
1198 @command(b'perfmoonwalk', formatteropts)
1251 @command(b'perfmoonwalk', formatteropts)
1199 def perfmoonwalk(ui, repo, **opts):
1252 def perfmoonwalk(ui, repo, **opts):
1200 """benchmark walking the changelog backwards
1253 """benchmark walking the changelog backwards
1201
1254
1202 This also loads the changelog data for each revision in the changelog.
1255 This also loads the changelog data for each revision in the changelog.
1203 """
1256 """
1204 opts = _byteskwargs(opts)
1257 opts = _byteskwargs(opts)
1205 timer, fm = gettimer(ui, opts)
1258 timer, fm = gettimer(ui, opts)
1206 def moonwalk():
1259 def moonwalk():
1207 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
1260 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
1208 ctx = repo[i]
1261 ctx = repo[i]
1209 ctx.branch() # read changelog data (in addition to the index)
1262 ctx.branch() # read changelog data (in addition to the index)
1210 timer(moonwalk)
1263 timer(moonwalk)
1211 fm.end()
1264 fm.end()
1212
1265
1213 @command(b'perftemplating',
1266 @command(b'perftemplating',
1214 [(b'r', b'rev', [], b'revisions to run the template on'),
1267 [(b'r', b'rev', [], b'revisions to run the template on'),
1215 ] + formatteropts)
1268 ] + formatteropts)
1216 def perftemplating(ui, repo, testedtemplate=None, **opts):
1269 def perftemplating(ui, repo, testedtemplate=None, **opts):
1217 """test the rendering time of a given template"""
1270 """test the rendering time of a given template"""
1218 if makelogtemplater is None:
1271 if makelogtemplater is None:
1219 raise error.Abort((b"perftemplating not available with this Mercurial"),
1272 raise error.Abort((b"perftemplating not available with this Mercurial"),
1220 hint=b"use 4.3 or later")
1273 hint=b"use 4.3 or later")
1221
1274
1222 opts = _byteskwargs(opts)
1275 opts = _byteskwargs(opts)
1223
1276
1224 nullui = ui.copy()
1277 nullui = ui.copy()
1225 nullui.fout = open(os.devnull, r'wb')
1278 nullui.fout = open(os.devnull, r'wb')
1226 nullui.disablepager()
1279 nullui.disablepager()
1227 revs = opts.get(b'rev')
1280 revs = opts.get(b'rev')
1228 if not revs:
1281 if not revs:
1229 revs = [b'all()']
1282 revs = [b'all()']
1230 revs = list(scmutil.revrange(repo, revs))
1283 revs = list(scmutil.revrange(repo, revs))
1231
1284
1232 defaulttemplate = (b'{date|shortdate} [{rev}:{node|short}]'
1285 defaulttemplate = (b'{date|shortdate} [{rev}:{node|short}]'
1233 b' {author|person}: {desc|firstline}\n')
1286 b' {author|person}: {desc|firstline}\n')
1234 if testedtemplate is None:
1287 if testedtemplate is None:
1235 testedtemplate = defaulttemplate
1288 testedtemplate = defaulttemplate
1236 displayer = makelogtemplater(nullui, repo, testedtemplate)
1289 displayer = makelogtemplater(nullui, repo, testedtemplate)
1237 def format():
1290 def format():
1238 for r in revs:
1291 for r in revs:
1239 ctx = repo[r]
1292 ctx = repo[r]
1240 displayer.show(ctx)
1293 displayer.show(ctx)
1241 displayer.flush(ctx)
1294 displayer.flush(ctx)
1242
1295
1243 timer, fm = gettimer(ui, opts)
1296 timer, fm = gettimer(ui, opts)
1244 timer(format)
1297 timer(format)
1245 fm.end()
1298 fm.end()
1246
1299
1247 @command(b'perfhelper-pathcopies', formatteropts +
1300 @command(b'perfhelper-pathcopies', formatteropts +
1248 [
1301 [
1249 (b'r', b'revs', [], b'restrict search to these revisions'),
1302 (b'r', b'revs', [], b'restrict search to these revisions'),
1250 (b'', b'timing', False, b'provides extra data (costly)'),
1303 (b'', b'timing', False, b'provides extra data (costly)'),
1251 ])
1304 ])
1252 def perfhelperpathcopies(ui, repo, revs=[], **opts):
1305 def perfhelperpathcopies(ui, repo, revs=[], **opts):
1253 """find statistic about potential parameters for the `perftracecopies`
1306 """find statistic about potential parameters for the `perftracecopies`
1254
1307
1255 This command find source-destination pair relevant for copytracing testing.
1308 This command find source-destination pair relevant for copytracing testing.
1256 It report value for some of the parameters that impact copy tracing time.
1309 It report value for some of the parameters that impact copy tracing time.
1257
1310
1258 If `--timing` is set, rename detection is run and the associated timing
1311 If `--timing` is set, rename detection is run and the associated timing
1259 will be reported. The extra details comes at the cost of a slower command
1312 will be reported. The extra details comes at the cost of a slower command
1260 execution.
1313 execution.
1261
1314
1262 Since the rename detection is only run once, other factors might easily
1315 Since the rename detection is only run once, other factors might easily
1263 affect the precision of the timing. However it should give a good
1316 affect the precision of the timing. However it should give a good
1264 approximation of which revision pairs are very costly.
1317 approximation of which revision pairs are very costly.
1265 """
1318 """
1266 opts = _byteskwargs(opts)
1319 opts = _byteskwargs(opts)
1267 fm = ui.formatter(b'perf', opts)
1320 fm = ui.formatter(b'perf', opts)
1268 dotiming = opts[b'timing']
1321 dotiming = opts[b'timing']
1269
1322
1270 if dotiming:
1323 if dotiming:
1271 header = '%12s %12s %12s %12s %12s %12s\n'
1324 header = '%12s %12s %12s %12s %12s %12s\n'
1272 output = ("%(source)12s %(destination)12s "
1325 output = ("%(source)12s %(destination)12s "
1273 "%(nbrevs)12d %(nbmissingfiles)12d "
1326 "%(nbrevs)12d %(nbmissingfiles)12d "
1274 "%(nbrenamedfiles)12d %(time)18.5f\n")
1327 "%(nbrenamedfiles)12d %(time)18.5f\n")
1275 header_names = ("source", "destination", "nb-revs", "nb-files",
1328 header_names = ("source", "destination", "nb-revs", "nb-files",
1276 "nb-renames", "time")
1329 "nb-renames", "time")
1277 fm.plain(header % header_names)
1330 fm.plain(header % header_names)
1278 else:
1331 else:
1279 header = '%12s %12s %12s %12s\n'
1332 header = '%12s %12s %12s %12s\n'
1280 output = ("%(source)12s %(destination)12s "
1333 output = ("%(source)12s %(destination)12s "
1281 "%(nbrevs)12d %(nbmissingfiles)12d\n")
1334 "%(nbrevs)12d %(nbmissingfiles)12d\n")
1282 fm.plain(header % ("source", "destination", "nb-revs", "nb-files"))
1335 fm.plain(header % ("source", "destination", "nb-revs", "nb-files"))
1283
1336
1284 if not revs:
1337 if not revs:
1285 revs = ['all()']
1338 revs = ['all()']
1286 revs = scmutil.revrange(repo, revs)
1339 revs = scmutil.revrange(repo, revs)
1287
1340
1288 roi = repo.revs('merge() and %ld', revs)
1341 roi = repo.revs('merge() and %ld', revs)
1289 for r in roi:
1342 for r in roi:
1290 ctx = repo[r]
1343 ctx = repo[r]
1291 p1 = ctx.p1().rev()
1344 p1 = ctx.p1().rev()
1292 p2 = ctx.p2().rev()
1345 p2 = ctx.p2().rev()
1293 bases = repo.changelog._commonancestorsheads(p1, p2)
1346 bases = repo.changelog._commonancestorsheads(p1, p2)
1294 for p in (p1, p2):
1347 for p in (p1, p2):
1295 for b in bases:
1348 for b in bases:
1296 base = repo[b]
1349 base = repo[b]
1297 parent = repo[p]
1350 parent = repo[p]
1298 missing = copies._computeforwardmissing(base, parent)
1351 missing = copies._computeforwardmissing(base, parent)
1299 if not missing:
1352 if not missing:
1300 continue
1353 continue
1301 data = {
1354 data = {
1302 b'source': base.hex(),
1355 b'source': base.hex(),
1303 b'destination': parent.hex(),
1356 b'destination': parent.hex(),
1304 b'nbrevs': len(repo.revs('%d::%d', b, p)),
1357 b'nbrevs': len(repo.revs('%d::%d', b, p)),
1305 b'nbmissingfiles': len(missing),
1358 b'nbmissingfiles': len(missing),
1306 }
1359 }
1307 if dotiming:
1360 if dotiming:
1308 begin = util.timer()
1361 begin = util.timer()
1309 renames = copies.pathcopies(base, parent)
1362 renames = copies.pathcopies(base, parent)
1310 end = util.timer()
1363 end = util.timer()
1311 # not very stable timing since we did only one run
1364 # not very stable timing since we did only one run
1312 data['time'] = end - begin
1365 data['time'] = end - begin
1313 data['nbrenamedfiles'] = len(renames)
1366 data['nbrenamedfiles'] = len(renames)
1314 fm.startitem()
1367 fm.startitem()
1315 fm.data(**data)
1368 fm.data(**data)
1316 out = data.copy()
1369 out = data.copy()
1317 out['source'] = fm.hexfunc(base.node())
1370 out['source'] = fm.hexfunc(base.node())
1318 out['destination'] = fm.hexfunc(parent.node())
1371 out['destination'] = fm.hexfunc(parent.node())
1319 fm.plain(output % out)
1372 fm.plain(output % out)
1320
1373
1321 fm.end()
1374 fm.end()
1322
1375
1323 @command(b'perfcca', formatteropts)
1376 @command(b'perfcca', formatteropts)
1324 def perfcca(ui, repo, **opts):
1377 def perfcca(ui, repo, **opts):
1325 opts = _byteskwargs(opts)
1378 opts = _byteskwargs(opts)
1326 timer, fm = gettimer(ui, opts)
1379 timer, fm = gettimer(ui, opts)
1327 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
1380 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
1328 fm.end()
1381 fm.end()
1329
1382
1330 @command(b'perffncacheload', formatteropts)
1383 @command(b'perffncacheload', formatteropts)
1331 def perffncacheload(ui, repo, **opts):
1384 def perffncacheload(ui, repo, **opts):
1332 opts = _byteskwargs(opts)
1385 opts = _byteskwargs(opts)
1333 timer, fm = gettimer(ui, opts)
1386 timer, fm = gettimer(ui, opts)
1334 s = repo.store
1387 s = repo.store
1335 def d():
1388 def d():
1336 s.fncache._load()
1389 s.fncache._load()
1337 timer(d)
1390 timer(d)
1338 fm.end()
1391 fm.end()
1339
1392
1340 @command(b'perffncachewrite', formatteropts)
1393 @command(b'perffncachewrite', formatteropts)
1341 def perffncachewrite(ui, repo, **opts):
1394 def perffncachewrite(ui, repo, **opts):
1342 opts = _byteskwargs(opts)
1395 opts = _byteskwargs(opts)
1343 timer, fm = gettimer(ui, opts)
1396 timer, fm = gettimer(ui, opts)
1344 s = repo.store
1397 s = repo.store
1345 lock = repo.lock()
1398 lock = repo.lock()
1346 s.fncache._load()
1399 s.fncache._load()
1347 tr = repo.transaction(b'perffncachewrite')
1400 tr = repo.transaction(b'perffncachewrite')
1348 tr.addbackup(b'fncache')
1401 tr.addbackup(b'fncache')
1349 def d():
1402 def d():
1350 s.fncache._dirty = True
1403 s.fncache._dirty = True
1351 s.fncache.write(tr)
1404 s.fncache.write(tr)
1352 timer(d)
1405 timer(d)
1353 tr.close()
1406 tr.close()
1354 lock.release()
1407 lock.release()
1355 fm.end()
1408 fm.end()
1356
1409
1357 @command(b'perffncacheencode', formatteropts)
1410 @command(b'perffncacheencode', formatteropts)
1358 def perffncacheencode(ui, repo, **opts):
1411 def perffncacheencode(ui, repo, **opts):
1359 opts = _byteskwargs(opts)
1412 opts = _byteskwargs(opts)
1360 timer, fm = gettimer(ui, opts)
1413 timer, fm = gettimer(ui, opts)
1361 s = repo.store
1414 s = repo.store
1362 s.fncache._load()
1415 s.fncache._load()
1363 def d():
1416 def d():
1364 for p in s.fncache.entries:
1417 for p in s.fncache.entries:
1365 s.encode(p)
1418 s.encode(p)
1366 timer(d)
1419 timer(d)
1367 fm.end()
1420 fm.end()
1368
1421
1369 def _bdiffworker(q, blocks, xdiff, ready, done):
1422 def _bdiffworker(q, blocks, xdiff, ready, done):
1370 while not done.is_set():
1423 while not done.is_set():
1371 pair = q.get()
1424 pair = q.get()
1372 while pair is not None:
1425 while pair is not None:
1373 if xdiff:
1426 if xdiff:
1374 mdiff.bdiff.xdiffblocks(*pair)
1427 mdiff.bdiff.xdiffblocks(*pair)
1375 elif blocks:
1428 elif blocks:
1376 mdiff.bdiff.blocks(*pair)
1429 mdiff.bdiff.blocks(*pair)
1377 else:
1430 else:
1378 mdiff.textdiff(*pair)
1431 mdiff.textdiff(*pair)
1379 q.task_done()
1432 q.task_done()
1380 pair = q.get()
1433 pair = q.get()
1381 q.task_done() # for the None one
1434 q.task_done() # for the None one
1382 with ready:
1435 with ready:
1383 ready.wait()
1436 ready.wait()
1384
1437
1385 def _manifestrevision(repo, mnode):
1438 def _manifestrevision(repo, mnode):
1386 ml = repo.manifestlog
1439 ml = repo.manifestlog
1387
1440
1388 if util.safehasattr(ml, b'getstorage'):
1441 if util.safehasattr(ml, b'getstorage'):
1389 store = ml.getstorage(b'')
1442 store = ml.getstorage(b'')
1390 else:
1443 else:
1391 store = ml._revlog
1444 store = ml._revlog
1392
1445
1393 return store.revision(mnode)
1446 return store.revision(mnode)
1394
1447
1395 @command(b'perfbdiff', revlogopts + formatteropts + [
1448 @command(b'perfbdiff', revlogopts + formatteropts + [
1396 (b'', b'count', 1, b'number of revisions to test (when using --startrev)'),
1449 (b'', b'count', 1, b'number of revisions to test (when using --startrev)'),
1397 (b'', b'alldata', False, b'test bdiffs for all associated revisions'),
1450 (b'', b'alldata', False, b'test bdiffs for all associated revisions'),
1398 (b'', b'threads', 0, b'number of thread to use (disable with 0)'),
1451 (b'', b'threads', 0, b'number of thread to use (disable with 0)'),
1399 (b'', b'blocks', False, b'test computing diffs into blocks'),
1452 (b'', b'blocks', False, b'test computing diffs into blocks'),
1400 (b'', b'xdiff', False, b'use xdiff algorithm'),
1453 (b'', b'xdiff', False, b'use xdiff algorithm'),
1401 ],
1454 ],
1402
1455
1403 b'-c|-m|FILE REV')
1456 b'-c|-m|FILE REV')
1404 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1457 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1405 """benchmark a bdiff between revisions
1458 """benchmark a bdiff between revisions
1406
1459
1407 By default, benchmark a bdiff between its delta parent and itself.
1460 By default, benchmark a bdiff between its delta parent and itself.
1408
1461
1409 With ``--count``, benchmark bdiffs between delta parents and self for N
1462 With ``--count``, benchmark bdiffs between delta parents and self for N
1410 revisions starting at the specified revision.
1463 revisions starting at the specified revision.
1411
1464
1412 With ``--alldata``, assume the requested revision is a changeset and
1465 With ``--alldata``, assume the requested revision is a changeset and
1413 measure bdiffs for all changes related to that changeset (manifest
1466 measure bdiffs for all changes related to that changeset (manifest
1414 and filelogs).
1467 and filelogs).
1415 """
1468 """
1416 opts = _byteskwargs(opts)
1469 opts = _byteskwargs(opts)
1417
1470
1418 if opts[b'xdiff'] and not opts[b'blocks']:
1471 if opts[b'xdiff'] and not opts[b'blocks']:
1419 raise error.CommandError(b'perfbdiff', b'--xdiff requires --blocks')
1472 raise error.CommandError(b'perfbdiff', b'--xdiff requires --blocks')
1420
1473
1421 if opts[b'alldata']:
1474 if opts[b'alldata']:
1422 opts[b'changelog'] = True
1475 opts[b'changelog'] = True
1423
1476
1424 if opts.get(b'changelog') or opts.get(b'manifest'):
1477 if opts.get(b'changelog') or opts.get(b'manifest'):
1425 file_, rev = None, file_
1478 file_, rev = None, file_
1426 elif rev is None:
1479 elif rev is None:
1427 raise error.CommandError(b'perfbdiff', b'invalid arguments')
1480 raise error.CommandError(b'perfbdiff', b'invalid arguments')
1428
1481
1429 blocks = opts[b'blocks']
1482 blocks = opts[b'blocks']
1430 xdiff = opts[b'xdiff']
1483 xdiff = opts[b'xdiff']
1431 textpairs = []
1484 textpairs = []
1432
1485
1433 r = cmdutil.openrevlog(repo, b'perfbdiff', file_, opts)
1486 r = cmdutil.openrevlog(repo, b'perfbdiff', file_, opts)
1434
1487
1435 startrev = r.rev(r.lookup(rev))
1488 startrev = r.rev(r.lookup(rev))
1436 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1489 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1437 if opts[b'alldata']:
1490 if opts[b'alldata']:
1438 # Load revisions associated with changeset.
1491 # Load revisions associated with changeset.
1439 ctx = repo[rev]
1492 ctx = repo[rev]
1440 mtext = _manifestrevision(repo, ctx.manifestnode())
1493 mtext = _manifestrevision(repo, ctx.manifestnode())
1441 for pctx in ctx.parents():
1494 for pctx in ctx.parents():
1442 pman = _manifestrevision(repo, pctx.manifestnode())
1495 pman = _manifestrevision(repo, pctx.manifestnode())
1443 textpairs.append((pman, mtext))
1496 textpairs.append((pman, mtext))
1444
1497
1445 # Load filelog revisions by iterating manifest delta.
1498 # Load filelog revisions by iterating manifest delta.
1446 man = ctx.manifest()
1499 man = ctx.manifest()
1447 pman = ctx.p1().manifest()
1500 pman = ctx.p1().manifest()
1448 for filename, change in pman.diff(man).items():
1501 for filename, change in pman.diff(man).items():
1449 fctx = repo.file(filename)
1502 fctx = repo.file(filename)
1450 f1 = fctx.revision(change[0][0] or -1)
1503 f1 = fctx.revision(change[0][0] or -1)
1451 f2 = fctx.revision(change[1][0] or -1)
1504 f2 = fctx.revision(change[1][0] or -1)
1452 textpairs.append((f1, f2))
1505 textpairs.append((f1, f2))
1453 else:
1506 else:
1454 dp = r.deltaparent(rev)
1507 dp = r.deltaparent(rev)
1455 textpairs.append((r.revision(dp), r.revision(rev)))
1508 textpairs.append((r.revision(dp), r.revision(rev)))
1456
1509
1457 withthreads = threads > 0
1510 withthreads = threads > 0
1458 if not withthreads:
1511 if not withthreads:
1459 def d():
1512 def d():
1460 for pair in textpairs:
1513 for pair in textpairs:
1461 if xdiff:
1514 if xdiff:
1462 mdiff.bdiff.xdiffblocks(*pair)
1515 mdiff.bdiff.xdiffblocks(*pair)
1463 elif blocks:
1516 elif blocks:
1464 mdiff.bdiff.blocks(*pair)
1517 mdiff.bdiff.blocks(*pair)
1465 else:
1518 else:
1466 mdiff.textdiff(*pair)
1519 mdiff.textdiff(*pair)
1467 else:
1520 else:
1468 q = queue()
1521 q = queue()
1469 for i in _xrange(threads):
1522 for i in _xrange(threads):
1470 q.put(None)
1523 q.put(None)
1471 ready = threading.Condition()
1524 ready = threading.Condition()
1472 done = threading.Event()
1525 done = threading.Event()
1473 for i in _xrange(threads):
1526 for i in _xrange(threads):
1474 threading.Thread(target=_bdiffworker,
1527 threading.Thread(target=_bdiffworker,
1475 args=(q, blocks, xdiff, ready, done)).start()
1528 args=(q, blocks, xdiff, ready, done)).start()
1476 q.join()
1529 q.join()
1477 def d():
1530 def d():
1478 for pair in textpairs:
1531 for pair in textpairs:
1479 q.put(pair)
1532 q.put(pair)
1480 for i in _xrange(threads):
1533 for i in _xrange(threads):
1481 q.put(None)
1534 q.put(None)
1482 with ready:
1535 with ready:
1483 ready.notify_all()
1536 ready.notify_all()
1484 q.join()
1537 q.join()
1485 timer, fm = gettimer(ui, opts)
1538 timer, fm = gettimer(ui, opts)
1486 timer(d)
1539 timer(d)
1487 fm.end()
1540 fm.end()
1488
1541
1489 if withthreads:
1542 if withthreads:
1490 done.set()
1543 done.set()
1491 for i in _xrange(threads):
1544 for i in _xrange(threads):
1492 q.put(None)
1545 q.put(None)
1493 with ready:
1546 with ready:
1494 ready.notify_all()
1547 ready.notify_all()
1495
1548
1496 @command(b'perfunidiff', revlogopts + formatteropts + [
1549 @command(b'perfunidiff', revlogopts + formatteropts + [
1497 (b'', b'count', 1, b'number of revisions to test (when using --startrev)'),
1550 (b'', b'count', 1, b'number of revisions to test (when using --startrev)'),
1498 (b'', b'alldata', False, b'test unidiffs for all associated revisions'),
1551 (b'', b'alldata', False, b'test unidiffs for all associated revisions'),
1499 ], b'-c|-m|FILE REV')
1552 ], b'-c|-m|FILE REV')
1500 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1553 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1501 """benchmark a unified diff between revisions
1554 """benchmark a unified diff between revisions
1502
1555
1503 This doesn't include any copy tracing - it's just a unified diff
1556 This doesn't include any copy tracing - it's just a unified diff
1504 of the texts.
1557 of the texts.
1505
1558
1506 By default, benchmark a diff between its delta parent and itself.
1559 By default, benchmark a diff between its delta parent and itself.
1507
1560
1508 With ``--count``, benchmark diffs between delta parents and self for N
1561 With ``--count``, benchmark diffs between delta parents and self for N
1509 revisions starting at the specified revision.
1562 revisions starting at the specified revision.
1510
1563
1511 With ``--alldata``, assume the requested revision is a changeset and
1564 With ``--alldata``, assume the requested revision is a changeset and
1512 measure diffs for all changes related to that changeset (manifest
1565 measure diffs for all changes related to that changeset (manifest
1513 and filelogs).
1566 and filelogs).
1514 """
1567 """
1515 opts = _byteskwargs(opts)
1568 opts = _byteskwargs(opts)
1516 if opts[b'alldata']:
1569 if opts[b'alldata']:
1517 opts[b'changelog'] = True
1570 opts[b'changelog'] = True
1518
1571
1519 if opts.get(b'changelog') or opts.get(b'manifest'):
1572 if opts.get(b'changelog') or opts.get(b'manifest'):
1520 file_, rev = None, file_
1573 file_, rev = None, file_
1521 elif rev is None:
1574 elif rev is None:
1522 raise error.CommandError(b'perfunidiff', b'invalid arguments')
1575 raise error.CommandError(b'perfunidiff', b'invalid arguments')
1523
1576
1524 textpairs = []
1577 textpairs = []
1525
1578
1526 r = cmdutil.openrevlog(repo, b'perfunidiff', file_, opts)
1579 r = cmdutil.openrevlog(repo, b'perfunidiff', file_, opts)
1527
1580
1528 startrev = r.rev(r.lookup(rev))
1581 startrev = r.rev(r.lookup(rev))
1529 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1582 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1530 if opts[b'alldata']:
1583 if opts[b'alldata']:
1531 # Load revisions associated with changeset.
1584 # Load revisions associated with changeset.
1532 ctx = repo[rev]
1585 ctx = repo[rev]
1533 mtext = _manifestrevision(repo, ctx.manifestnode())
1586 mtext = _manifestrevision(repo, ctx.manifestnode())
1534 for pctx in ctx.parents():
1587 for pctx in ctx.parents():
1535 pman = _manifestrevision(repo, pctx.manifestnode())
1588 pman = _manifestrevision(repo, pctx.manifestnode())
1536 textpairs.append((pman, mtext))
1589 textpairs.append((pman, mtext))
1537
1590
1538 # Load filelog revisions by iterating manifest delta.
1591 # Load filelog revisions by iterating manifest delta.
1539 man = ctx.manifest()
1592 man = ctx.manifest()
1540 pman = ctx.p1().manifest()
1593 pman = ctx.p1().manifest()
1541 for filename, change in pman.diff(man).items():
1594 for filename, change in pman.diff(man).items():
1542 fctx = repo.file(filename)
1595 fctx = repo.file(filename)
1543 f1 = fctx.revision(change[0][0] or -1)
1596 f1 = fctx.revision(change[0][0] or -1)
1544 f2 = fctx.revision(change[1][0] or -1)
1597 f2 = fctx.revision(change[1][0] or -1)
1545 textpairs.append((f1, f2))
1598 textpairs.append((f1, f2))
1546 else:
1599 else:
1547 dp = r.deltaparent(rev)
1600 dp = r.deltaparent(rev)
1548 textpairs.append((r.revision(dp), r.revision(rev)))
1601 textpairs.append((r.revision(dp), r.revision(rev)))
1549
1602
1550 def d():
1603 def d():
1551 for left, right in textpairs:
1604 for left, right in textpairs:
1552 # The date strings don't matter, so we pass empty strings.
1605 # The date strings don't matter, so we pass empty strings.
1553 headerlines, hunks = mdiff.unidiff(
1606 headerlines, hunks = mdiff.unidiff(
1554 left, b'', right, b'', b'left', b'right', binary=False)
1607 left, b'', right, b'', b'left', b'right', binary=False)
1555 # consume iterators in roughly the way patch.py does
1608 # consume iterators in roughly the way patch.py does
1556 b'\n'.join(headerlines)
1609 b'\n'.join(headerlines)
1557 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1610 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1558 timer, fm = gettimer(ui, opts)
1611 timer, fm = gettimer(ui, opts)
1559 timer(d)
1612 timer(d)
1560 fm.end()
1613 fm.end()
1561
1614
1562 @command(b'perfdiffwd', formatteropts)
1615 @command(b'perfdiffwd', formatteropts)
1563 def perfdiffwd(ui, repo, **opts):
1616 def perfdiffwd(ui, repo, **opts):
1564 """Profile diff of working directory changes"""
1617 """Profile diff of working directory changes"""
1565 opts = _byteskwargs(opts)
1618 opts = _byteskwargs(opts)
1566 timer, fm = gettimer(ui, opts)
1619 timer, fm = gettimer(ui, opts)
1567 options = {
1620 options = {
1568 'w': 'ignore_all_space',
1621 'w': 'ignore_all_space',
1569 'b': 'ignore_space_change',
1622 'b': 'ignore_space_change',
1570 'B': 'ignore_blank_lines',
1623 'B': 'ignore_blank_lines',
1571 }
1624 }
1572
1625
1573 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1626 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1574 opts = dict((options[c], b'1') for c in diffopt)
1627 opts = dict((options[c], b'1') for c in diffopt)
1575 def d():
1628 def d():
1576 ui.pushbuffer()
1629 ui.pushbuffer()
1577 commands.diff(ui, repo, **opts)
1630 commands.diff(ui, repo, **opts)
1578 ui.popbuffer()
1631 ui.popbuffer()
1579 diffopt = diffopt.encode('ascii')
1632 diffopt = diffopt.encode('ascii')
1580 title = b'diffopts: %s' % (diffopt and (b'-' + diffopt) or b'none')
1633 title = b'diffopts: %s' % (diffopt and (b'-' + diffopt) or b'none')
1581 timer(d, title=title)
1634 timer(d, title=title)
1582 fm.end()
1635 fm.end()
1583
1636
1584 @command(b'perfrevlogindex', revlogopts + formatteropts,
1637 @command(b'perfrevlogindex', revlogopts + formatteropts,
1585 b'-c|-m|FILE')
1638 b'-c|-m|FILE')
1586 def perfrevlogindex(ui, repo, file_=None, **opts):
1639 def perfrevlogindex(ui, repo, file_=None, **opts):
1587 """Benchmark operations against a revlog index.
1640 """Benchmark operations against a revlog index.
1588
1641
1589 This tests constructing a revlog instance, reading index data,
1642 This tests constructing a revlog instance, reading index data,
1590 parsing index data, and performing various operations related to
1643 parsing index data, and performing various operations related to
1591 index data.
1644 index data.
1592 """
1645 """
1593
1646
1594 opts = _byteskwargs(opts)
1647 opts = _byteskwargs(opts)
1595
1648
1596 rl = cmdutil.openrevlog(repo, b'perfrevlogindex', file_, opts)
1649 rl = cmdutil.openrevlog(repo, b'perfrevlogindex', file_, opts)
1597
1650
1598 opener = getattr(rl, 'opener') # trick linter
1651 opener = getattr(rl, 'opener') # trick linter
1599 indexfile = rl.indexfile
1652 indexfile = rl.indexfile
1600 data = opener.read(indexfile)
1653 data = opener.read(indexfile)
1601
1654
1602 header = struct.unpack(b'>I', data[0:4])[0]
1655 header = struct.unpack(b'>I', data[0:4])[0]
1603 version = header & 0xFFFF
1656 version = header & 0xFFFF
1604 if version == 1:
1657 if version == 1:
1605 revlogio = revlog.revlogio()
1658 revlogio = revlog.revlogio()
1606 inline = header & (1 << 16)
1659 inline = header & (1 << 16)
1607 else:
1660 else:
1608 raise error.Abort((b'unsupported revlog version: %d') % version)
1661 raise error.Abort((b'unsupported revlog version: %d') % version)
1609
1662
1610 rllen = len(rl)
1663 rllen = len(rl)
1611
1664
1612 node0 = rl.node(0)
1665 node0 = rl.node(0)
1613 node25 = rl.node(rllen // 4)
1666 node25 = rl.node(rllen // 4)
1614 node50 = rl.node(rllen // 2)
1667 node50 = rl.node(rllen // 2)
1615 node75 = rl.node(rllen // 4 * 3)
1668 node75 = rl.node(rllen // 4 * 3)
1616 node100 = rl.node(rllen - 1)
1669 node100 = rl.node(rllen - 1)
1617
1670
1618 allrevs = range(rllen)
1671 allrevs = range(rllen)
1619 allrevsrev = list(reversed(allrevs))
1672 allrevsrev = list(reversed(allrevs))
1620 allnodes = [rl.node(rev) for rev in range(rllen)]
1673 allnodes = [rl.node(rev) for rev in range(rllen)]
1621 allnodesrev = list(reversed(allnodes))
1674 allnodesrev = list(reversed(allnodes))
1622
1675
1623 def constructor():
1676 def constructor():
1624 revlog.revlog(opener, indexfile)
1677 revlog.revlog(opener, indexfile)
1625
1678
1626 def read():
1679 def read():
1627 with opener(indexfile) as fh:
1680 with opener(indexfile) as fh:
1628 fh.read()
1681 fh.read()
1629
1682
1630 def parseindex():
1683 def parseindex():
1631 revlogio.parseindex(data, inline)
1684 revlogio.parseindex(data, inline)
1632
1685
1633 def getentry(revornode):
1686 def getentry(revornode):
1634 index = revlogio.parseindex(data, inline)[0]
1687 index = revlogio.parseindex(data, inline)[0]
1635 index[revornode]
1688 index[revornode]
1636
1689
1637 def getentries(revs, count=1):
1690 def getentries(revs, count=1):
1638 index = revlogio.parseindex(data, inline)[0]
1691 index = revlogio.parseindex(data, inline)[0]
1639
1692
1640 for i in range(count):
1693 for i in range(count):
1641 for rev in revs:
1694 for rev in revs:
1642 index[rev]
1695 index[rev]
1643
1696
1644 def resolvenode(node):
1697 def resolvenode(node):
1645 nodemap = revlogio.parseindex(data, inline)[1]
1698 nodemap = revlogio.parseindex(data, inline)[1]
1646 # This only works for the C code.
1699 # This only works for the C code.
1647 if nodemap is None:
1700 if nodemap is None:
1648 return
1701 return
1649
1702
1650 try:
1703 try:
1651 nodemap[node]
1704 nodemap[node]
1652 except error.RevlogError:
1705 except error.RevlogError:
1653 pass
1706 pass
1654
1707
1655 def resolvenodes(nodes, count=1):
1708 def resolvenodes(nodes, count=1):
1656 nodemap = revlogio.parseindex(data, inline)[1]
1709 nodemap = revlogio.parseindex(data, inline)[1]
1657 if nodemap is None:
1710 if nodemap is None:
1658 return
1711 return
1659
1712
1660 for i in range(count):
1713 for i in range(count):
1661 for node in nodes:
1714 for node in nodes:
1662 try:
1715 try:
1663 nodemap[node]
1716 nodemap[node]
1664 except error.RevlogError:
1717 except error.RevlogError:
1665 pass
1718 pass
1666
1719
1667 benches = [
1720 benches = [
1668 (constructor, b'revlog constructor'),
1721 (constructor, b'revlog constructor'),
1669 (read, b'read'),
1722 (read, b'read'),
1670 (parseindex, b'create index object'),
1723 (parseindex, b'create index object'),
1671 (lambda: getentry(0), b'retrieve index entry for rev 0'),
1724 (lambda: getentry(0), b'retrieve index entry for rev 0'),
1672 (lambda: resolvenode(b'a' * 20), b'look up missing node'),
1725 (lambda: resolvenode(b'a' * 20), b'look up missing node'),
1673 (lambda: resolvenode(node0), b'look up node at rev 0'),
1726 (lambda: resolvenode(node0), b'look up node at rev 0'),
1674 (lambda: resolvenode(node25), b'look up node at 1/4 len'),
1727 (lambda: resolvenode(node25), b'look up node at 1/4 len'),
1675 (lambda: resolvenode(node50), b'look up node at 1/2 len'),
1728 (lambda: resolvenode(node50), b'look up node at 1/2 len'),
1676 (lambda: resolvenode(node75), b'look up node at 3/4 len'),
1729 (lambda: resolvenode(node75), b'look up node at 3/4 len'),
1677 (lambda: resolvenode(node100), b'look up node at tip'),
1730 (lambda: resolvenode(node100), b'look up node at tip'),
1678 # 2x variation is to measure caching impact.
1731 # 2x variation is to measure caching impact.
1679 (lambda: resolvenodes(allnodes),
1732 (lambda: resolvenodes(allnodes),
1680 b'look up all nodes (forward)'),
1733 b'look up all nodes (forward)'),
1681 (lambda: resolvenodes(allnodes, 2),
1734 (lambda: resolvenodes(allnodes, 2),
1682 b'look up all nodes 2x (forward)'),
1735 b'look up all nodes 2x (forward)'),
1683 (lambda: resolvenodes(allnodesrev),
1736 (lambda: resolvenodes(allnodesrev),
1684 b'look up all nodes (reverse)'),
1737 b'look up all nodes (reverse)'),
1685 (lambda: resolvenodes(allnodesrev, 2),
1738 (lambda: resolvenodes(allnodesrev, 2),
1686 b'look up all nodes 2x (reverse)'),
1739 b'look up all nodes 2x (reverse)'),
1687 (lambda: getentries(allrevs),
1740 (lambda: getentries(allrevs),
1688 b'retrieve all index entries (forward)'),
1741 b'retrieve all index entries (forward)'),
1689 (lambda: getentries(allrevs, 2),
1742 (lambda: getentries(allrevs, 2),
1690 b'retrieve all index entries 2x (forward)'),
1743 b'retrieve all index entries 2x (forward)'),
1691 (lambda: getentries(allrevsrev),
1744 (lambda: getentries(allrevsrev),
1692 b'retrieve all index entries (reverse)'),
1745 b'retrieve all index entries (reverse)'),
1693 (lambda: getentries(allrevsrev, 2),
1746 (lambda: getentries(allrevsrev, 2),
1694 b'retrieve all index entries 2x (reverse)'),
1747 b'retrieve all index entries 2x (reverse)'),
1695 ]
1748 ]
1696
1749
1697 for fn, title in benches:
1750 for fn, title in benches:
1698 timer, fm = gettimer(ui, opts)
1751 timer, fm = gettimer(ui, opts)
1699 timer(fn, title=title)
1752 timer(fn, title=title)
1700 fm.end()
1753 fm.end()
1701
1754
1702 @command(b'perfrevlogrevisions', revlogopts + formatteropts +
1755 @command(b'perfrevlogrevisions', revlogopts + formatteropts +
1703 [(b'd', b'dist', 100, b'distance between the revisions'),
1756 [(b'd', b'dist', 100, b'distance between the revisions'),
1704 (b's', b'startrev', 0, b'revision to start reading at'),
1757 (b's', b'startrev', 0, b'revision to start reading at'),
1705 (b'', b'reverse', False, b'read in reverse')],
1758 (b'', b'reverse', False, b'read in reverse')],
1706 b'-c|-m|FILE')
1759 b'-c|-m|FILE')
1707 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1760 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1708 **opts):
1761 **opts):
1709 """Benchmark reading a series of revisions from a revlog.
1762 """Benchmark reading a series of revisions from a revlog.
1710
1763
1711 By default, we read every ``-d/--dist`` revision from 0 to tip of
1764 By default, we read every ``-d/--dist`` revision from 0 to tip of
1712 the specified revlog.
1765 the specified revlog.
1713
1766
1714 The start revision can be defined via ``-s/--startrev``.
1767 The start revision can be defined via ``-s/--startrev``.
1715 """
1768 """
1716 opts = _byteskwargs(opts)
1769 opts = _byteskwargs(opts)
1717
1770
1718 rl = cmdutil.openrevlog(repo, b'perfrevlogrevisions', file_, opts)
1771 rl = cmdutil.openrevlog(repo, b'perfrevlogrevisions', file_, opts)
1719 rllen = getlen(ui)(rl)
1772 rllen = getlen(ui)(rl)
1720
1773
1721 if startrev < 0:
1774 if startrev < 0:
1722 startrev = rllen + startrev
1775 startrev = rllen + startrev
1723
1776
1724 def d():
1777 def d():
1725 rl.clearcaches()
1778 rl.clearcaches()
1726
1779
1727 beginrev = startrev
1780 beginrev = startrev
1728 endrev = rllen
1781 endrev = rllen
1729 dist = opts[b'dist']
1782 dist = opts[b'dist']
1730
1783
1731 if reverse:
1784 if reverse:
1732 beginrev, endrev = endrev - 1, beginrev - 1
1785 beginrev, endrev = endrev - 1, beginrev - 1
1733 dist = -1 * dist
1786 dist = -1 * dist
1734
1787
1735 for x in _xrange(beginrev, endrev, dist):
1788 for x in _xrange(beginrev, endrev, dist):
1736 # Old revisions don't support passing int.
1789 # Old revisions don't support passing int.
1737 n = rl.node(x)
1790 n = rl.node(x)
1738 rl.revision(n)
1791 rl.revision(n)
1739
1792
1740 timer, fm = gettimer(ui, opts)
1793 timer, fm = gettimer(ui, opts)
1741 timer(d)
1794 timer(d)
1742 fm.end()
1795 fm.end()
1743
1796
1744 @command(b'perfrevlogwrite', revlogopts + formatteropts +
1797 @command(b'perfrevlogwrite', revlogopts + formatteropts +
1745 [(b's', b'startrev', 1000, b'revision to start writing at'),
1798 [(b's', b'startrev', 1000, b'revision to start writing at'),
1746 (b'', b'stoprev', -1, b'last revision to write'),
1799 (b'', b'stoprev', -1, b'last revision to write'),
1747 (b'', b'count', 3, b'last revision to write'),
1800 (b'', b'count', 3, b'last revision to write'),
1748 (b'', b'details', False, b'print timing for every revisions tested'),
1801 (b'', b'details', False, b'print timing for every revisions tested'),
1749 (b'', b'source', b'full', b'the kind of data feed in the revlog'),
1802 (b'', b'source', b'full', b'the kind of data feed in the revlog'),
1750 (b'', b'lazydeltabase', True, b'try the provided delta first'),
1803 (b'', b'lazydeltabase', True, b'try the provided delta first'),
1751 (b'', b'clear-caches', True, b'clear revlog cache between calls'),
1804 (b'', b'clear-caches', True, b'clear revlog cache between calls'),
1752 ],
1805 ],
1753 b'-c|-m|FILE')
1806 b'-c|-m|FILE')
1754 def perfrevlogwrite(ui, repo, file_=None, startrev=1000, stoprev=-1, **opts):
1807 def perfrevlogwrite(ui, repo, file_=None, startrev=1000, stoprev=-1, **opts):
1755 """Benchmark writing a series of revisions to a revlog.
1808 """Benchmark writing a series of revisions to a revlog.
1756
1809
1757 Possible source values are:
1810 Possible source values are:
1758 * `full`: add from a full text (default).
1811 * `full`: add from a full text (default).
1759 * `parent-1`: add from a delta to the first parent
1812 * `parent-1`: add from a delta to the first parent
1760 * `parent-2`: add from a delta to the second parent if it exists
1813 * `parent-2`: add from a delta to the second parent if it exists
1761 (use a delta from the first parent otherwise)
1814 (use a delta from the first parent otherwise)
1762 * `parent-smallest`: add from the smallest delta (either p1 or p2)
1815 * `parent-smallest`: add from the smallest delta (either p1 or p2)
1763 * `storage`: add from the existing precomputed deltas
1816 * `storage`: add from the existing precomputed deltas
1764 """
1817 """
1765 opts = _byteskwargs(opts)
1818 opts = _byteskwargs(opts)
1766
1819
1767 rl = cmdutil.openrevlog(repo, b'perfrevlogwrite', file_, opts)
1820 rl = cmdutil.openrevlog(repo, b'perfrevlogwrite', file_, opts)
1768 rllen = getlen(ui)(rl)
1821 rllen = getlen(ui)(rl)
1769 if startrev < 0:
1822 if startrev < 0:
1770 startrev = rllen + startrev
1823 startrev = rllen + startrev
1771 if stoprev < 0:
1824 if stoprev < 0:
1772 stoprev = rllen + stoprev
1825 stoprev = rllen + stoprev
1773
1826
1774 lazydeltabase = opts['lazydeltabase']
1827 lazydeltabase = opts['lazydeltabase']
1775 source = opts['source']
1828 source = opts['source']
1776 clearcaches = opts['clear_caches']
1829 clearcaches = opts['clear_caches']
1777 validsource = (b'full', b'parent-1', b'parent-2', b'parent-smallest',
1830 validsource = (b'full', b'parent-1', b'parent-2', b'parent-smallest',
1778 b'storage')
1831 b'storage')
1779 if source not in validsource:
1832 if source not in validsource:
1780 raise error.Abort('invalid source type: %s' % source)
1833 raise error.Abort('invalid source type: %s' % source)
1781
1834
1782 ### actually gather results
1835 ### actually gather results
1783 count = opts['count']
1836 count = opts['count']
1784 if count <= 0:
1837 if count <= 0:
1785 raise error.Abort('invalide run count: %d' % count)
1838 raise error.Abort('invalide run count: %d' % count)
1786 allresults = []
1839 allresults = []
1787 for c in range(count):
1840 for c in range(count):
1788 timing = _timeonewrite(ui, rl, source, startrev, stoprev, c + 1,
1841 timing = _timeonewrite(ui, rl, source, startrev, stoprev, c + 1,
1789 lazydeltabase=lazydeltabase,
1842 lazydeltabase=lazydeltabase,
1790 clearcaches=clearcaches)
1843 clearcaches=clearcaches)
1791 allresults.append(timing)
1844 allresults.append(timing)
1792
1845
1793 ### consolidate the results in a single list
1846 ### consolidate the results in a single list
1794 results = []
1847 results = []
1795 for idx, (rev, t) in enumerate(allresults[0]):
1848 for idx, (rev, t) in enumerate(allresults[0]):
1796 ts = [t]
1849 ts = [t]
1797 for other in allresults[1:]:
1850 for other in allresults[1:]:
1798 orev, ot = other[idx]
1851 orev, ot = other[idx]
1799 assert orev == rev
1852 assert orev == rev
1800 ts.append(ot)
1853 ts.append(ot)
1801 results.append((rev, ts))
1854 results.append((rev, ts))
1802 resultcount = len(results)
1855 resultcount = len(results)
1803
1856
1804 ### Compute and display relevant statistics
1857 ### Compute and display relevant statistics
1805
1858
1806 # get a formatter
1859 # get a formatter
1807 fm = ui.formatter(b'perf', opts)
1860 fm = ui.formatter(b'perf', opts)
1808 displayall = ui.configbool(b"perf", b"all-timing", False)
1861 displayall = ui.configbool(b"perf", b"all-timing", False)
1809
1862
1810 # print individual details if requested
1863 # print individual details if requested
1811 if opts['details']:
1864 if opts['details']:
1812 for idx, item in enumerate(results, 1):
1865 for idx, item in enumerate(results, 1):
1813 rev, data = item
1866 rev, data = item
1814 title = 'revisions #%d of %d, rev %d' % (idx, resultcount, rev)
1867 title = 'revisions #%d of %d, rev %d' % (idx, resultcount, rev)
1815 formatone(fm, data, title=title, displayall=displayall)
1868 formatone(fm, data, title=title, displayall=displayall)
1816
1869
1817 # sorts results by median time
1870 # sorts results by median time
1818 results.sort(key=lambda x: sorted(x[1])[len(x[1]) // 2])
1871 results.sort(key=lambda x: sorted(x[1])[len(x[1]) // 2])
1819 # list of (name, index) to display)
1872 # list of (name, index) to display)
1820 relevants = [
1873 relevants = [
1821 ("min", 0),
1874 ("min", 0),
1822 ("10%", resultcount * 10 // 100),
1875 ("10%", resultcount * 10 // 100),
1823 ("25%", resultcount * 25 // 100),
1876 ("25%", resultcount * 25 // 100),
1824 ("50%", resultcount * 70 // 100),
1877 ("50%", resultcount * 70 // 100),
1825 ("75%", resultcount * 75 // 100),
1878 ("75%", resultcount * 75 // 100),
1826 ("90%", resultcount * 90 // 100),
1879 ("90%", resultcount * 90 // 100),
1827 ("95%", resultcount * 95 // 100),
1880 ("95%", resultcount * 95 // 100),
1828 ("99%", resultcount * 99 // 100),
1881 ("99%", resultcount * 99 // 100),
1829 ("99.9%", resultcount * 999 // 1000),
1882 ("99.9%", resultcount * 999 // 1000),
1830 ("99.99%", resultcount * 9999 // 10000),
1883 ("99.99%", resultcount * 9999 // 10000),
1831 ("99.999%", resultcount * 99999 // 100000),
1884 ("99.999%", resultcount * 99999 // 100000),
1832 ("max", -1),
1885 ("max", -1),
1833 ]
1886 ]
1834 if not ui.quiet:
1887 if not ui.quiet:
1835 for name, idx in relevants:
1888 for name, idx in relevants:
1836 data = results[idx]
1889 data = results[idx]
1837 title = '%s of %d, rev %d' % (name, resultcount, data[0])
1890 title = '%s of %d, rev %d' % (name, resultcount, data[0])
1838 formatone(fm, data[1], title=title, displayall=displayall)
1891 formatone(fm, data[1], title=title, displayall=displayall)
1839
1892
1840 # XXX summing that many float will not be very precise, we ignore this fact
1893 # XXX summing that many float will not be very precise, we ignore this fact
1841 # for now
1894 # for now
1842 totaltime = []
1895 totaltime = []
1843 for item in allresults:
1896 for item in allresults:
1844 totaltime.append((sum(x[1][0] for x in item),
1897 totaltime.append((sum(x[1][0] for x in item),
1845 sum(x[1][1] for x in item),
1898 sum(x[1][1] for x in item),
1846 sum(x[1][2] for x in item),)
1899 sum(x[1][2] for x in item),)
1847 )
1900 )
1848 formatone(fm, totaltime, title="total time (%d revs)" % resultcount,
1901 formatone(fm, totaltime, title="total time (%d revs)" % resultcount,
1849 displayall=displayall)
1902 displayall=displayall)
1850 fm.end()
1903 fm.end()
1851
1904
1852 class _faketr(object):
1905 class _faketr(object):
1853 def add(s, x, y, z=None):
1906 def add(s, x, y, z=None):
1854 return None
1907 return None
1855
1908
1856 def _timeonewrite(ui, orig, source, startrev, stoprev, runidx=None,
1909 def _timeonewrite(ui, orig, source, startrev, stoprev, runidx=None,
1857 lazydeltabase=True, clearcaches=True):
1910 lazydeltabase=True, clearcaches=True):
1858 timings = []
1911 timings = []
1859 tr = _faketr()
1912 tr = _faketr()
1860 with _temprevlog(ui, orig, startrev) as dest:
1913 with _temprevlog(ui, orig, startrev) as dest:
1861 dest._lazydeltabase = lazydeltabase
1914 dest._lazydeltabase = lazydeltabase
1862 revs = list(orig.revs(startrev, stoprev))
1915 revs = list(orig.revs(startrev, stoprev))
1863 total = len(revs)
1916 total = len(revs)
1864 topic = 'adding'
1917 topic = 'adding'
1865 if runidx is not None:
1918 if runidx is not None:
1866 topic += ' (run #%d)' % runidx
1919 topic += ' (run #%d)' % runidx
1867 # Support both old and new progress API
1920 # Support both old and new progress API
1868 if util.safehasattr(ui, 'makeprogress'):
1921 if util.safehasattr(ui, 'makeprogress'):
1869 progress = ui.makeprogress(topic, unit='revs', total=total)
1922 progress = ui.makeprogress(topic, unit='revs', total=total)
1870 def updateprogress(pos):
1923 def updateprogress(pos):
1871 progress.update(pos)
1924 progress.update(pos)
1872 def completeprogress():
1925 def completeprogress():
1873 progress.complete()
1926 progress.complete()
1874 else:
1927 else:
1875 def updateprogress(pos):
1928 def updateprogress(pos):
1876 ui.progress(topic, pos, unit='revs', total=total)
1929 ui.progress(topic, pos, unit='revs', total=total)
1877 def completeprogress():
1930 def completeprogress():
1878 ui.progress(topic, None, unit='revs', total=total)
1931 ui.progress(topic, None, unit='revs', total=total)
1879
1932
1880 for idx, rev in enumerate(revs):
1933 for idx, rev in enumerate(revs):
1881 updateprogress(idx)
1934 updateprogress(idx)
1882 addargs, addkwargs = _getrevisionseed(orig, rev, tr, source)
1935 addargs, addkwargs = _getrevisionseed(orig, rev, tr, source)
1883 if clearcaches:
1936 if clearcaches:
1884 dest.index.clearcaches()
1937 dest.index.clearcaches()
1885 dest.clearcaches()
1938 dest.clearcaches()
1886 with timeone() as r:
1939 with timeone() as r:
1887 dest.addrawrevision(*addargs, **addkwargs)
1940 dest.addrawrevision(*addargs, **addkwargs)
1888 timings.append((rev, r[0]))
1941 timings.append((rev, r[0]))
1889 updateprogress(total)
1942 updateprogress(total)
1890 completeprogress()
1943 completeprogress()
1891 return timings
1944 return timings
1892
1945
1893 def _getrevisionseed(orig, rev, tr, source):
1946 def _getrevisionseed(orig, rev, tr, source):
1894 from mercurial.node import nullid
1947 from mercurial.node import nullid
1895
1948
1896 linkrev = orig.linkrev(rev)
1949 linkrev = orig.linkrev(rev)
1897 node = orig.node(rev)
1950 node = orig.node(rev)
1898 p1, p2 = orig.parents(node)
1951 p1, p2 = orig.parents(node)
1899 flags = orig.flags(rev)
1952 flags = orig.flags(rev)
1900 cachedelta = None
1953 cachedelta = None
1901 text = None
1954 text = None
1902
1955
1903 if source == b'full':
1956 if source == b'full':
1904 text = orig.revision(rev)
1957 text = orig.revision(rev)
1905 elif source == b'parent-1':
1958 elif source == b'parent-1':
1906 baserev = orig.rev(p1)
1959 baserev = orig.rev(p1)
1907 cachedelta = (baserev, orig.revdiff(p1, rev))
1960 cachedelta = (baserev, orig.revdiff(p1, rev))
1908 elif source == b'parent-2':
1961 elif source == b'parent-2':
1909 parent = p2
1962 parent = p2
1910 if p2 == nullid:
1963 if p2 == nullid:
1911 parent = p1
1964 parent = p1
1912 baserev = orig.rev(parent)
1965 baserev = orig.rev(parent)
1913 cachedelta = (baserev, orig.revdiff(parent, rev))
1966 cachedelta = (baserev, orig.revdiff(parent, rev))
1914 elif source == b'parent-smallest':
1967 elif source == b'parent-smallest':
1915 p1diff = orig.revdiff(p1, rev)
1968 p1diff = orig.revdiff(p1, rev)
1916 parent = p1
1969 parent = p1
1917 diff = p1diff
1970 diff = p1diff
1918 if p2 != nullid:
1971 if p2 != nullid:
1919 p2diff = orig.revdiff(p2, rev)
1972 p2diff = orig.revdiff(p2, rev)
1920 if len(p1diff) > len(p2diff):
1973 if len(p1diff) > len(p2diff):
1921 parent = p2
1974 parent = p2
1922 diff = p2diff
1975 diff = p2diff
1923 baserev = orig.rev(parent)
1976 baserev = orig.rev(parent)
1924 cachedelta = (baserev, diff)
1977 cachedelta = (baserev, diff)
1925 elif source == b'storage':
1978 elif source == b'storage':
1926 baserev = orig.deltaparent(rev)
1979 baserev = orig.deltaparent(rev)
1927 cachedelta = (baserev, orig.revdiff(orig.node(baserev), rev))
1980 cachedelta = (baserev, orig.revdiff(orig.node(baserev), rev))
1928
1981
1929 return ((text, tr, linkrev, p1, p2),
1982 return ((text, tr, linkrev, p1, p2),
1930 {'node': node, 'flags': flags, 'cachedelta': cachedelta})
1983 {'node': node, 'flags': flags, 'cachedelta': cachedelta})
1931
1984
1932 @contextlib.contextmanager
1985 @contextlib.contextmanager
1933 def _temprevlog(ui, orig, truncaterev):
1986 def _temprevlog(ui, orig, truncaterev):
1934 from mercurial import vfs as vfsmod
1987 from mercurial import vfs as vfsmod
1935
1988
1936 if orig._inline:
1989 if orig._inline:
1937 raise error.Abort('not supporting inline revlog (yet)')
1990 raise error.Abort('not supporting inline revlog (yet)')
1938
1991
1939 origindexpath = orig.opener.join(orig.indexfile)
1992 origindexpath = orig.opener.join(orig.indexfile)
1940 origdatapath = orig.opener.join(orig.datafile)
1993 origdatapath = orig.opener.join(orig.datafile)
1941 indexname = 'revlog.i'
1994 indexname = 'revlog.i'
1942 dataname = 'revlog.d'
1995 dataname = 'revlog.d'
1943
1996
1944 tmpdir = tempfile.mkdtemp(prefix='tmp-hgperf-')
1997 tmpdir = tempfile.mkdtemp(prefix='tmp-hgperf-')
1945 try:
1998 try:
1946 # copy the data file in a temporary directory
1999 # copy the data file in a temporary directory
1947 ui.debug('copying data in %s\n' % tmpdir)
2000 ui.debug('copying data in %s\n' % tmpdir)
1948 destindexpath = os.path.join(tmpdir, 'revlog.i')
2001 destindexpath = os.path.join(tmpdir, 'revlog.i')
1949 destdatapath = os.path.join(tmpdir, 'revlog.d')
2002 destdatapath = os.path.join(tmpdir, 'revlog.d')
1950 shutil.copyfile(origindexpath, destindexpath)
2003 shutil.copyfile(origindexpath, destindexpath)
1951 shutil.copyfile(origdatapath, destdatapath)
2004 shutil.copyfile(origdatapath, destdatapath)
1952
2005
1953 # remove the data we want to add again
2006 # remove the data we want to add again
1954 ui.debug('truncating data to be rewritten\n')
2007 ui.debug('truncating data to be rewritten\n')
1955 with open(destindexpath, 'ab') as index:
2008 with open(destindexpath, 'ab') as index:
1956 index.seek(0)
2009 index.seek(0)
1957 index.truncate(truncaterev * orig._io.size)
2010 index.truncate(truncaterev * orig._io.size)
1958 with open(destdatapath, 'ab') as data:
2011 with open(destdatapath, 'ab') as data:
1959 data.seek(0)
2012 data.seek(0)
1960 data.truncate(orig.start(truncaterev))
2013 data.truncate(orig.start(truncaterev))
1961
2014
1962 # instantiate a new revlog from the temporary copy
2015 # instantiate a new revlog from the temporary copy
1963 ui.debug('truncating adding to be rewritten\n')
2016 ui.debug('truncating adding to be rewritten\n')
1964 vfs = vfsmod.vfs(tmpdir)
2017 vfs = vfsmod.vfs(tmpdir)
1965 vfs.options = getattr(orig.opener, 'options', None)
2018 vfs.options = getattr(orig.opener, 'options', None)
1966
2019
1967 dest = revlog.revlog(vfs,
2020 dest = revlog.revlog(vfs,
1968 indexfile=indexname,
2021 indexfile=indexname,
1969 datafile=dataname)
2022 datafile=dataname)
1970 if dest._inline:
2023 if dest._inline:
1971 raise error.Abort('not supporting inline revlog (yet)')
2024 raise error.Abort('not supporting inline revlog (yet)')
1972 # make sure internals are initialized
2025 # make sure internals are initialized
1973 dest.revision(len(dest) - 1)
2026 dest.revision(len(dest) - 1)
1974 yield dest
2027 yield dest
1975 del dest, vfs
2028 del dest, vfs
1976 finally:
2029 finally:
1977 shutil.rmtree(tmpdir, True)
2030 shutil.rmtree(tmpdir, True)
1978
2031
1979 @command(b'perfrevlogchunks', revlogopts + formatteropts +
2032 @command(b'perfrevlogchunks', revlogopts + formatteropts +
1980 [(b'e', b'engines', b'', b'compression engines to use'),
2033 [(b'e', b'engines', b'', b'compression engines to use'),
1981 (b's', b'startrev', 0, b'revision to start at')],
2034 (b's', b'startrev', 0, b'revision to start at')],
1982 b'-c|-m|FILE')
2035 b'-c|-m|FILE')
1983 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
2036 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1984 """Benchmark operations on revlog chunks.
2037 """Benchmark operations on revlog chunks.
1985
2038
1986 Logically, each revlog is a collection of fulltext revisions. However,
2039 Logically, each revlog is a collection of fulltext revisions. However,
1987 stored within each revlog are "chunks" of possibly compressed data. This
2040 stored within each revlog are "chunks" of possibly compressed data. This
1988 data needs to be read and decompressed or compressed and written.
2041 data needs to be read and decompressed or compressed and written.
1989
2042
1990 This command measures the time it takes to read+decompress and recompress
2043 This command measures the time it takes to read+decompress and recompress
1991 chunks in a revlog. It effectively isolates I/O and compression performance.
2044 chunks in a revlog. It effectively isolates I/O and compression performance.
1992 For measurements of higher-level operations like resolving revisions,
2045 For measurements of higher-level operations like resolving revisions,
1993 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
2046 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1994 """
2047 """
1995 opts = _byteskwargs(opts)
2048 opts = _byteskwargs(opts)
1996
2049
1997 rl = cmdutil.openrevlog(repo, b'perfrevlogchunks', file_, opts)
2050 rl = cmdutil.openrevlog(repo, b'perfrevlogchunks', file_, opts)
1998
2051
1999 # _chunkraw was renamed to _getsegmentforrevs.
2052 # _chunkraw was renamed to _getsegmentforrevs.
2000 try:
2053 try:
2001 segmentforrevs = rl._getsegmentforrevs
2054 segmentforrevs = rl._getsegmentforrevs
2002 except AttributeError:
2055 except AttributeError:
2003 segmentforrevs = rl._chunkraw
2056 segmentforrevs = rl._chunkraw
2004
2057
2005 # Verify engines argument.
2058 # Verify engines argument.
2006 if engines:
2059 if engines:
2007 engines = set(e.strip() for e in engines.split(b','))
2060 engines = set(e.strip() for e in engines.split(b','))
2008 for engine in engines:
2061 for engine in engines:
2009 try:
2062 try:
2010 util.compressionengines[engine]
2063 util.compressionengines[engine]
2011 except KeyError:
2064 except KeyError:
2012 raise error.Abort(b'unknown compression engine: %s' % engine)
2065 raise error.Abort(b'unknown compression engine: %s' % engine)
2013 else:
2066 else:
2014 engines = []
2067 engines = []
2015 for e in util.compengines:
2068 for e in util.compengines:
2016 engine = util.compengines[e]
2069 engine = util.compengines[e]
2017 try:
2070 try:
2018 if engine.available():
2071 if engine.available():
2019 engine.revlogcompressor().compress(b'dummy')
2072 engine.revlogcompressor().compress(b'dummy')
2020 engines.append(e)
2073 engines.append(e)
2021 except NotImplementedError:
2074 except NotImplementedError:
2022 pass
2075 pass
2023
2076
2024 revs = list(rl.revs(startrev, len(rl) - 1))
2077 revs = list(rl.revs(startrev, len(rl) - 1))
2025
2078
2026 def rlfh(rl):
2079 def rlfh(rl):
2027 if rl._inline:
2080 if rl._inline:
2028 return getsvfs(repo)(rl.indexfile)
2081 return getsvfs(repo)(rl.indexfile)
2029 else:
2082 else:
2030 return getsvfs(repo)(rl.datafile)
2083 return getsvfs(repo)(rl.datafile)
2031
2084
2032 def doread():
2085 def doread():
2033 rl.clearcaches()
2086 rl.clearcaches()
2034 for rev in revs:
2087 for rev in revs:
2035 segmentforrevs(rev, rev)
2088 segmentforrevs(rev, rev)
2036
2089
2037 def doreadcachedfh():
2090 def doreadcachedfh():
2038 rl.clearcaches()
2091 rl.clearcaches()
2039 fh = rlfh(rl)
2092 fh = rlfh(rl)
2040 for rev in revs:
2093 for rev in revs:
2041 segmentforrevs(rev, rev, df=fh)
2094 segmentforrevs(rev, rev, df=fh)
2042
2095
2043 def doreadbatch():
2096 def doreadbatch():
2044 rl.clearcaches()
2097 rl.clearcaches()
2045 segmentforrevs(revs[0], revs[-1])
2098 segmentforrevs(revs[0], revs[-1])
2046
2099
2047 def doreadbatchcachedfh():
2100 def doreadbatchcachedfh():
2048 rl.clearcaches()
2101 rl.clearcaches()
2049 fh = rlfh(rl)
2102 fh = rlfh(rl)
2050 segmentforrevs(revs[0], revs[-1], df=fh)
2103 segmentforrevs(revs[0], revs[-1], df=fh)
2051
2104
2052 def dochunk():
2105 def dochunk():
2053 rl.clearcaches()
2106 rl.clearcaches()
2054 fh = rlfh(rl)
2107 fh = rlfh(rl)
2055 for rev in revs:
2108 for rev in revs:
2056 rl._chunk(rev, df=fh)
2109 rl._chunk(rev, df=fh)
2057
2110
2058 chunks = [None]
2111 chunks = [None]
2059
2112
2060 def dochunkbatch():
2113 def dochunkbatch():
2061 rl.clearcaches()
2114 rl.clearcaches()
2062 fh = rlfh(rl)
2115 fh = rlfh(rl)
2063 # Save chunks as a side-effect.
2116 # Save chunks as a side-effect.
2064 chunks[0] = rl._chunks(revs, df=fh)
2117 chunks[0] = rl._chunks(revs, df=fh)
2065
2118
2066 def docompress(compressor):
2119 def docompress(compressor):
2067 rl.clearcaches()
2120 rl.clearcaches()
2068
2121
2069 try:
2122 try:
2070 # Swap in the requested compression engine.
2123 # Swap in the requested compression engine.
2071 oldcompressor = rl._compressor
2124 oldcompressor = rl._compressor
2072 rl._compressor = compressor
2125 rl._compressor = compressor
2073 for chunk in chunks[0]:
2126 for chunk in chunks[0]:
2074 rl.compress(chunk)
2127 rl.compress(chunk)
2075 finally:
2128 finally:
2076 rl._compressor = oldcompressor
2129 rl._compressor = oldcompressor
2077
2130
2078 benches = [
2131 benches = [
2079 (lambda: doread(), b'read'),
2132 (lambda: doread(), b'read'),
2080 (lambda: doreadcachedfh(), b'read w/ reused fd'),
2133 (lambda: doreadcachedfh(), b'read w/ reused fd'),
2081 (lambda: doreadbatch(), b'read batch'),
2134 (lambda: doreadbatch(), b'read batch'),
2082 (lambda: doreadbatchcachedfh(), b'read batch w/ reused fd'),
2135 (lambda: doreadbatchcachedfh(), b'read batch w/ reused fd'),
2083 (lambda: dochunk(), b'chunk'),
2136 (lambda: dochunk(), b'chunk'),
2084 (lambda: dochunkbatch(), b'chunk batch'),
2137 (lambda: dochunkbatch(), b'chunk batch'),
2085 ]
2138 ]
2086
2139
2087 for engine in sorted(engines):
2140 for engine in sorted(engines):
2088 compressor = util.compengines[engine].revlogcompressor()
2141 compressor = util.compengines[engine].revlogcompressor()
2089 benches.append((functools.partial(docompress, compressor),
2142 benches.append((functools.partial(docompress, compressor),
2090 b'compress w/ %s' % engine))
2143 b'compress w/ %s' % engine))
2091
2144
2092 for fn, title in benches:
2145 for fn, title in benches:
2093 timer, fm = gettimer(ui, opts)
2146 timer, fm = gettimer(ui, opts)
2094 timer(fn, title=title)
2147 timer(fn, title=title)
2095 fm.end()
2148 fm.end()
2096
2149
2097 @command(b'perfrevlogrevision', revlogopts + formatteropts +
2150 @command(b'perfrevlogrevision', revlogopts + formatteropts +
2098 [(b'', b'cache', False, b'use caches instead of clearing')],
2151 [(b'', b'cache', False, b'use caches instead of clearing')],
2099 b'-c|-m|FILE REV')
2152 b'-c|-m|FILE REV')
2100 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
2153 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
2101 """Benchmark obtaining a revlog revision.
2154 """Benchmark obtaining a revlog revision.
2102
2155
2103 Obtaining a revlog revision consists of roughly the following steps:
2156 Obtaining a revlog revision consists of roughly the following steps:
2104
2157
2105 1. Compute the delta chain
2158 1. Compute the delta chain
2106 2. Slice the delta chain if applicable
2159 2. Slice the delta chain if applicable
2107 3. Obtain the raw chunks for that delta chain
2160 3. Obtain the raw chunks for that delta chain
2108 4. Decompress each raw chunk
2161 4. Decompress each raw chunk
2109 5. Apply binary patches to obtain fulltext
2162 5. Apply binary patches to obtain fulltext
2110 6. Verify hash of fulltext
2163 6. Verify hash of fulltext
2111
2164
2112 This command measures the time spent in each of these phases.
2165 This command measures the time spent in each of these phases.
2113 """
2166 """
2114 opts = _byteskwargs(opts)
2167 opts = _byteskwargs(opts)
2115
2168
2116 if opts.get(b'changelog') or opts.get(b'manifest'):
2169 if opts.get(b'changelog') or opts.get(b'manifest'):
2117 file_, rev = None, file_
2170 file_, rev = None, file_
2118 elif rev is None:
2171 elif rev is None:
2119 raise error.CommandError(b'perfrevlogrevision', b'invalid arguments')
2172 raise error.CommandError(b'perfrevlogrevision', b'invalid arguments')
2120
2173
2121 r = cmdutil.openrevlog(repo, b'perfrevlogrevision', file_, opts)
2174 r = cmdutil.openrevlog(repo, b'perfrevlogrevision', file_, opts)
2122
2175
2123 # _chunkraw was renamed to _getsegmentforrevs.
2176 # _chunkraw was renamed to _getsegmentforrevs.
2124 try:
2177 try:
2125 segmentforrevs = r._getsegmentforrevs
2178 segmentforrevs = r._getsegmentforrevs
2126 except AttributeError:
2179 except AttributeError:
2127 segmentforrevs = r._chunkraw
2180 segmentforrevs = r._chunkraw
2128
2181
2129 node = r.lookup(rev)
2182 node = r.lookup(rev)
2130 rev = r.rev(node)
2183 rev = r.rev(node)
2131
2184
2132 def getrawchunks(data, chain):
2185 def getrawchunks(data, chain):
2133 start = r.start
2186 start = r.start
2134 length = r.length
2187 length = r.length
2135 inline = r._inline
2188 inline = r._inline
2136 iosize = r._io.size
2189 iosize = r._io.size
2137 buffer = util.buffer
2190 buffer = util.buffer
2138
2191
2139 chunks = []
2192 chunks = []
2140 ladd = chunks.append
2193 ladd = chunks.append
2141 for idx, item in enumerate(chain):
2194 for idx, item in enumerate(chain):
2142 offset = start(item[0])
2195 offset = start(item[0])
2143 bits = data[idx]
2196 bits = data[idx]
2144 for rev in item:
2197 for rev in item:
2145 chunkstart = start(rev)
2198 chunkstart = start(rev)
2146 if inline:
2199 if inline:
2147 chunkstart += (rev + 1) * iosize
2200 chunkstart += (rev + 1) * iosize
2148 chunklength = length(rev)
2201 chunklength = length(rev)
2149 ladd(buffer(bits, chunkstart - offset, chunklength))
2202 ladd(buffer(bits, chunkstart - offset, chunklength))
2150
2203
2151 return chunks
2204 return chunks
2152
2205
2153 def dodeltachain(rev):
2206 def dodeltachain(rev):
2154 if not cache:
2207 if not cache:
2155 r.clearcaches()
2208 r.clearcaches()
2156 r._deltachain(rev)
2209 r._deltachain(rev)
2157
2210
2158 def doread(chain):
2211 def doread(chain):
2159 if not cache:
2212 if not cache:
2160 r.clearcaches()
2213 r.clearcaches()
2161 for item in slicedchain:
2214 for item in slicedchain:
2162 segmentforrevs(item[0], item[-1])
2215 segmentforrevs(item[0], item[-1])
2163
2216
2164 def doslice(r, chain, size):
2217 def doslice(r, chain, size):
2165 for s in slicechunk(r, chain, targetsize=size):
2218 for s in slicechunk(r, chain, targetsize=size):
2166 pass
2219 pass
2167
2220
2168 def dorawchunks(data, chain):
2221 def dorawchunks(data, chain):
2169 if not cache:
2222 if not cache:
2170 r.clearcaches()
2223 r.clearcaches()
2171 getrawchunks(data, chain)
2224 getrawchunks(data, chain)
2172
2225
2173 def dodecompress(chunks):
2226 def dodecompress(chunks):
2174 decomp = r.decompress
2227 decomp = r.decompress
2175 for chunk in chunks:
2228 for chunk in chunks:
2176 decomp(chunk)
2229 decomp(chunk)
2177
2230
2178 def dopatch(text, bins):
2231 def dopatch(text, bins):
2179 if not cache:
2232 if not cache:
2180 r.clearcaches()
2233 r.clearcaches()
2181 mdiff.patches(text, bins)
2234 mdiff.patches(text, bins)
2182
2235
2183 def dohash(text):
2236 def dohash(text):
2184 if not cache:
2237 if not cache:
2185 r.clearcaches()
2238 r.clearcaches()
2186 r.checkhash(text, node, rev=rev)
2239 r.checkhash(text, node, rev=rev)
2187
2240
2188 def dorevision():
2241 def dorevision():
2189 if not cache:
2242 if not cache:
2190 r.clearcaches()
2243 r.clearcaches()
2191 r.revision(node)
2244 r.revision(node)
2192
2245
2193 try:
2246 try:
2194 from mercurial.revlogutils.deltas import slicechunk
2247 from mercurial.revlogutils.deltas import slicechunk
2195 except ImportError:
2248 except ImportError:
2196 slicechunk = getattr(revlog, '_slicechunk', None)
2249 slicechunk = getattr(revlog, '_slicechunk', None)
2197
2250
2198 size = r.length(rev)
2251 size = r.length(rev)
2199 chain = r._deltachain(rev)[0]
2252 chain = r._deltachain(rev)[0]
2200 if not getattr(r, '_withsparseread', False):
2253 if not getattr(r, '_withsparseread', False):
2201 slicedchain = (chain,)
2254 slicedchain = (chain,)
2202 else:
2255 else:
2203 slicedchain = tuple(slicechunk(r, chain, targetsize=size))
2256 slicedchain = tuple(slicechunk(r, chain, targetsize=size))
2204 data = [segmentforrevs(seg[0], seg[-1])[1] for seg in slicedchain]
2257 data = [segmentforrevs(seg[0], seg[-1])[1] for seg in slicedchain]
2205 rawchunks = getrawchunks(data, slicedchain)
2258 rawchunks = getrawchunks(data, slicedchain)
2206 bins = r._chunks(chain)
2259 bins = r._chunks(chain)
2207 text = bytes(bins[0])
2260 text = bytes(bins[0])
2208 bins = bins[1:]
2261 bins = bins[1:]
2209 text = mdiff.patches(text, bins)
2262 text = mdiff.patches(text, bins)
2210
2263
2211 benches = [
2264 benches = [
2212 (lambda: dorevision(), b'full'),
2265 (lambda: dorevision(), b'full'),
2213 (lambda: dodeltachain(rev), b'deltachain'),
2266 (lambda: dodeltachain(rev), b'deltachain'),
2214 (lambda: doread(chain), b'read'),
2267 (lambda: doread(chain), b'read'),
2215 ]
2268 ]
2216
2269
2217 if getattr(r, '_withsparseread', False):
2270 if getattr(r, '_withsparseread', False):
2218 slicing = (lambda: doslice(r, chain, size), b'slice-sparse-chain')
2271 slicing = (lambda: doslice(r, chain, size), b'slice-sparse-chain')
2219 benches.append(slicing)
2272 benches.append(slicing)
2220
2273
2221 benches.extend([
2274 benches.extend([
2222 (lambda: dorawchunks(data, slicedchain), b'rawchunks'),
2275 (lambda: dorawchunks(data, slicedchain), b'rawchunks'),
2223 (lambda: dodecompress(rawchunks), b'decompress'),
2276 (lambda: dodecompress(rawchunks), b'decompress'),
2224 (lambda: dopatch(text, bins), b'patch'),
2277 (lambda: dopatch(text, bins), b'patch'),
2225 (lambda: dohash(text), b'hash'),
2278 (lambda: dohash(text), b'hash'),
2226 ])
2279 ])
2227
2280
2228 timer, fm = gettimer(ui, opts)
2281 timer, fm = gettimer(ui, opts)
2229 for fn, title in benches:
2282 for fn, title in benches:
2230 timer(fn, title=title)
2283 timer(fn, title=title)
2231 fm.end()
2284 fm.end()
2232
2285
2233 @command(b'perfrevset',
2286 @command(b'perfrevset',
2234 [(b'C', b'clear', False, b'clear volatile cache between each call.'),
2287 [(b'C', b'clear', False, b'clear volatile cache between each call.'),
2235 (b'', b'contexts', False, b'obtain changectx for each revision')]
2288 (b'', b'contexts', False, b'obtain changectx for each revision')]
2236 + formatteropts, b"REVSET")
2289 + formatteropts, b"REVSET")
2237 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
2290 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
2238 """benchmark the execution time of a revset
2291 """benchmark the execution time of a revset
2239
2292
2240 Use the --clean option if need to evaluate the impact of build volatile
2293 Use the --clean option if need to evaluate the impact of build volatile
2241 revisions set cache on the revset execution. Volatile cache hold filtered
2294 revisions set cache on the revset execution. Volatile cache hold filtered
2242 and obsolete related cache."""
2295 and obsolete related cache."""
2243 opts = _byteskwargs(opts)
2296 opts = _byteskwargs(opts)
2244
2297
2245 timer, fm = gettimer(ui, opts)
2298 timer, fm = gettimer(ui, opts)
2246 def d():
2299 def d():
2247 if clear:
2300 if clear:
2248 repo.invalidatevolatilesets()
2301 repo.invalidatevolatilesets()
2249 if contexts:
2302 if contexts:
2250 for ctx in repo.set(expr): pass
2303 for ctx in repo.set(expr): pass
2251 else:
2304 else:
2252 for r in repo.revs(expr): pass
2305 for r in repo.revs(expr): pass
2253 timer(d)
2306 timer(d)
2254 fm.end()
2307 fm.end()
2255
2308
2256 @command(b'perfvolatilesets',
2309 @command(b'perfvolatilesets',
2257 [(b'', b'clear-obsstore', False, b'drop obsstore between each call.'),
2310 [(b'', b'clear-obsstore', False, b'drop obsstore between each call.'),
2258 ] + formatteropts)
2311 ] + formatteropts)
2259 def perfvolatilesets(ui, repo, *names, **opts):
2312 def perfvolatilesets(ui, repo, *names, **opts):
2260 """benchmark the computation of various volatile set
2313 """benchmark the computation of various volatile set
2261
2314
2262 Volatile set computes element related to filtering and obsolescence."""
2315 Volatile set computes element related to filtering and obsolescence."""
2263 opts = _byteskwargs(opts)
2316 opts = _byteskwargs(opts)
2264 timer, fm = gettimer(ui, opts)
2317 timer, fm = gettimer(ui, opts)
2265 repo = repo.unfiltered()
2318 repo = repo.unfiltered()
2266
2319
2267 def getobs(name):
2320 def getobs(name):
2268 def d():
2321 def d():
2269 repo.invalidatevolatilesets()
2322 repo.invalidatevolatilesets()
2270 if opts[b'clear_obsstore']:
2323 if opts[b'clear_obsstore']:
2271 clearfilecache(repo, b'obsstore')
2324 clearfilecache(repo, b'obsstore')
2272 obsolete.getrevs(repo, name)
2325 obsolete.getrevs(repo, name)
2273 return d
2326 return d
2274
2327
2275 allobs = sorted(obsolete.cachefuncs)
2328 allobs = sorted(obsolete.cachefuncs)
2276 if names:
2329 if names:
2277 allobs = [n for n in allobs if n in names]
2330 allobs = [n for n in allobs if n in names]
2278
2331
2279 for name in allobs:
2332 for name in allobs:
2280 timer(getobs(name), title=name)
2333 timer(getobs(name), title=name)
2281
2334
2282 def getfiltered(name):
2335 def getfiltered(name):
2283 def d():
2336 def d():
2284 repo.invalidatevolatilesets()
2337 repo.invalidatevolatilesets()
2285 if opts[b'clear_obsstore']:
2338 if opts[b'clear_obsstore']:
2286 clearfilecache(repo, b'obsstore')
2339 clearfilecache(repo, b'obsstore')
2287 repoview.filterrevs(repo, name)
2340 repoview.filterrevs(repo, name)
2288 return d
2341 return d
2289
2342
2290 allfilter = sorted(repoview.filtertable)
2343 allfilter = sorted(repoview.filtertable)
2291 if names:
2344 if names:
2292 allfilter = [n for n in allfilter if n in names]
2345 allfilter = [n for n in allfilter if n in names]
2293
2346
2294 for name in allfilter:
2347 for name in allfilter:
2295 timer(getfiltered(name), title=name)
2348 timer(getfiltered(name), title=name)
2296 fm.end()
2349 fm.end()
2297
2350
2298 @command(b'perfbranchmap',
2351 @command(b'perfbranchmap',
2299 [(b'f', b'full', False,
2352 [(b'f', b'full', False,
2300 b'Includes build time of subset'),
2353 b'Includes build time of subset'),
2301 (b'', b'clear-revbranch', False,
2354 (b'', b'clear-revbranch', False,
2302 b'purge the revbranch cache between computation'),
2355 b'purge the revbranch cache between computation'),
2303 ] + formatteropts)
2356 ] + formatteropts)
2304 def perfbranchmap(ui, repo, *filternames, **opts):
2357 def perfbranchmap(ui, repo, *filternames, **opts):
2305 """benchmark the update of a branchmap
2358 """benchmark the update of a branchmap
2306
2359
2307 This benchmarks the full repo.branchmap() call with read and write disabled
2360 This benchmarks the full repo.branchmap() call with read and write disabled
2308 """
2361 """
2309 opts = _byteskwargs(opts)
2362 opts = _byteskwargs(opts)
2310 full = opts.get(b"full", False)
2363 full = opts.get(b"full", False)
2311 clear_revbranch = opts.get(b"clear_revbranch", False)
2364 clear_revbranch = opts.get(b"clear_revbranch", False)
2312 timer, fm = gettimer(ui, opts)
2365 timer, fm = gettimer(ui, opts)
2313 def getbranchmap(filtername):
2366 def getbranchmap(filtername):
2314 """generate a benchmark function for the filtername"""
2367 """generate a benchmark function for the filtername"""
2315 if filtername is None:
2368 if filtername is None:
2316 view = repo
2369 view = repo
2317 else:
2370 else:
2318 view = repo.filtered(filtername)
2371 view = repo.filtered(filtername)
2319 def d():
2372 def d():
2320 if clear_revbranch:
2373 if clear_revbranch:
2321 repo.revbranchcache()._clear()
2374 repo.revbranchcache()._clear()
2322 if full:
2375 if full:
2323 view._branchcaches.clear()
2376 view._branchcaches.clear()
2324 else:
2377 else:
2325 view._branchcaches.pop(filtername, None)
2378 view._branchcaches.pop(filtername, None)
2326 view.branchmap()
2379 view.branchmap()
2327 return d
2380 return d
2328 # add filter in smaller subset to bigger subset
2381 # add filter in smaller subset to bigger subset
2329 possiblefilters = set(repoview.filtertable)
2382 possiblefilters = set(repoview.filtertable)
2330 if filternames:
2383 if filternames:
2331 possiblefilters &= set(filternames)
2384 possiblefilters &= set(filternames)
2332 subsettable = getbranchmapsubsettable()
2385 subsettable = getbranchmapsubsettable()
2333 allfilters = []
2386 allfilters = []
2334 while possiblefilters:
2387 while possiblefilters:
2335 for name in possiblefilters:
2388 for name in possiblefilters:
2336 subset = subsettable.get(name)
2389 subset = subsettable.get(name)
2337 if subset not in possiblefilters:
2390 if subset not in possiblefilters:
2338 break
2391 break
2339 else:
2392 else:
2340 assert False, b'subset cycle %s!' % possiblefilters
2393 assert False, b'subset cycle %s!' % possiblefilters
2341 allfilters.append(name)
2394 allfilters.append(name)
2342 possiblefilters.remove(name)
2395 possiblefilters.remove(name)
2343
2396
2344 # warm the cache
2397 # warm the cache
2345 if not full:
2398 if not full:
2346 for name in allfilters:
2399 for name in allfilters:
2347 repo.filtered(name).branchmap()
2400 repo.filtered(name).branchmap()
2348 if not filternames or b'unfiltered' in filternames:
2401 if not filternames or b'unfiltered' in filternames:
2349 # add unfiltered
2402 # add unfiltered
2350 allfilters.append(None)
2403 allfilters.append(None)
2351
2404
2352 branchcacheread = safeattrsetter(branchmap, b'read')
2405 branchcacheread = safeattrsetter(branchmap, b'read')
2353 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
2406 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
2354 branchcacheread.set(lambda repo: None)
2407 branchcacheread.set(lambda repo: None)
2355 branchcachewrite.set(lambda bc, repo: None)
2408 branchcachewrite.set(lambda bc, repo: None)
2356 try:
2409 try:
2357 for name in allfilters:
2410 for name in allfilters:
2358 printname = name
2411 printname = name
2359 if name is None:
2412 if name is None:
2360 printname = b'unfiltered'
2413 printname = b'unfiltered'
2361 timer(getbranchmap(name), title=str(printname))
2414 timer(getbranchmap(name), title=str(printname))
2362 finally:
2415 finally:
2363 branchcacheread.restore()
2416 branchcacheread.restore()
2364 branchcachewrite.restore()
2417 branchcachewrite.restore()
2365 fm.end()
2418 fm.end()
2366
2419
2367 @command(b'perfbranchmapupdate', [
2420 @command(b'perfbranchmapupdate', [
2368 (b'', b'base', [], b'subset of revision to start from'),
2421 (b'', b'base', [], b'subset of revision to start from'),
2369 (b'', b'target', [], b'subset of revision to end with'),
2422 (b'', b'target', [], b'subset of revision to end with'),
2370 (b'', b'clear-caches', False, b'clear cache between each runs')
2423 (b'', b'clear-caches', False, b'clear cache between each runs')
2371 ] + formatteropts)
2424 ] + formatteropts)
2372 def perfbranchmapupdate(ui, repo, base=(), target=(), **opts):
2425 def perfbranchmapupdate(ui, repo, base=(), target=(), **opts):
2373 """benchmark branchmap update from for <base> revs to <target> revs
2426 """benchmark branchmap update from for <base> revs to <target> revs
2374
2427
2375 If `--clear-caches` is passed, the following items will be reset before
2428 If `--clear-caches` is passed, the following items will be reset before
2376 each update:
2429 each update:
2377 * the changelog instance and associated indexes
2430 * the changelog instance and associated indexes
2378 * the rev-branch-cache instance
2431 * the rev-branch-cache instance
2379
2432
2380 Examples:
2433 Examples:
2381
2434
2382 # update for the one last revision
2435 # update for the one last revision
2383 $ hg perfbranchmapupdate --base 'not tip' --target 'tip'
2436 $ hg perfbranchmapupdate --base 'not tip' --target 'tip'
2384
2437
2385 $ update for change coming with a new branch
2438 $ update for change coming with a new branch
2386 $ hg perfbranchmapupdate --base 'stable' --target 'default'
2439 $ hg perfbranchmapupdate --base 'stable' --target 'default'
2387 """
2440 """
2388 from mercurial import branchmap
2441 from mercurial import branchmap
2389 from mercurial import repoview
2442 from mercurial import repoview
2390 opts = _byteskwargs(opts)
2443 opts = _byteskwargs(opts)
2391 timer, fm = gettimer(ui, opts)
2444 timer, fm = gettimer(ui, opts)
2392 clearcaches = opts[b'clear_caches']
2445 clearcaches = opts[b'clear_caches']
2393 unfi = repo.unfiltered()
2446 unfi = repo.unfiltered()
2394 x = [None] # used to pass data between closure
2447 x = [None] # used to pass data between closure
2395
2448
2396 # we use a `list` here to avoid possible side effect from smartset
2449 # we use a `list` here to avoid possible side effect from smartset
2397 baserevs = list(scmutil.revrange(repo, base))
2450 baserevs = list(scmutil.revrange(repo, base))
2398 targetrevs = list(scmutil.revrange(repo, target))
2451 targetrevs = list(scmutil.revrange(repo, target))
2399 if not baserevs:
2452 if not baserevs:
2400 raise error.Abort(b'no revisions selected for --base')
2453 raise error.Abort(b'no revisions selected for --base')
2401 if not targetrevs:
2454 if not targetrevs:
2402 raise error.Abort(b'no revisions selected for --target')
2455 raise error.Abort(b'no revisions selected for --target')
2403
2456
2404 # make sure the target branchmap also contains the one in the base
2457 # make sure the target branchmap also contains the one in the base
2405 targetrevs = list(set(baserevs) | set(targetrevs))
2458 targetrevs = list(set(baserevs) | set(targetrevs))
2406 targetrevs.sort()
2459 targetrevs.sort()
2407
2460
2408 cl = repo.changelog
2461 cl = repo.changelog
2409 allbaserevs = list(cl.ancestors(baserevs, inclusive=True))
2462 allbaserevs = list(cl.ancestors(baserevs, inclusive=True))
2410 allbaserevs.sort()
2463 allbaserevs.sort()
2411 alltargetrevs = frozenset(cl.ancestors(targetrevs, inclusive=True))
2464 alltargetrevs = frozenset(cl.ancestors(targetrevs, inclusive=True))
2412
2465
2413 newrevs = list(alltargetrevs.difference(allbaserevs))
2466 newrevs = list(alltargetrevs.difference(allbaserevs))
2414 newrevs.sort()
2467 newrevs.sort()
2415
2468
2416 allrevs = frozenset(unfi.changelog.revs())
2469 allrevs = frozenset(unfi.changelog.revs())
2417 basefilterrevs = frozenset(allrevs.difference(allbaserevs))
2470 basefilterrevs = frozenset(allrevs.difference(allbaserevs))
2418 targetfilterrevs = frozenset(allrevs.difference(alltargetrevs))
2471 targetfilterrevs = frozenset(allrevs.difference(alltargetrevs))
2419
2472
2420 def basefilter(repo, visibilityexceptions=None):
2473 def basefilter(repo, visibilityexceptions=None):
2421 return basefilterrevs
2474 return basefilterrevs
2422
2475
2423 def targetfilter(repo, visibilityexceptions=None):
2476 def targetfilter(repo, visibilityexceptions=None):
2424 return targetfilterrevs
2477 return targetfilterrevs
2425
2478
2426 msg = b'benchmark of branchmap with %d revisions with %d new ones\n'
2479 msg = b'benchmark of branchmap with %d revisions with %d new ones\n'
2427 ui.status(msg % (len(allbaserevs), len(newrevs)))
2480 ui.status(msg % (len(allbaserevs), len(newrevs)))
2428 if targetfilterrevs:
2481 if targetfilterrevs:
2429 msg = b'(%d revisions still filtered)\n'
2482 msg = b'(%d revisions still filtered)\n'
2430 ui.status(msg % len(targetfilterrevs))
2483 ui.status(msg % len(targetfilterrevs))
2431
2484
2432 try:
2485 try:
2433 repoview.filtertable[b'__perf_branchmap_update_base'] = basefilter
2486 repoview.filtertable[b'__perf_branchmap_update_base'] = basefilter
2434 repoview.filtertable[b'__perf_branchmap_update_target'] = targetfilter
2487 repoview.filtertable[b'__perf_branchmap_update_target'] = targetfilter
2435
2488
2436 baserepo = repo.filtered(b'__perf_branchmap_update_base')
2489 baserepo = repo.filtered(b'__perf_branchmap_update_base')
2437 targetrepo = repo.filtered(b'__perf_branchmap_update_target')
2490 targetrepo = repo.filtered(b'__perf_branchmap_update_target')
2438
2491
2439 # try to find an existing branchmap to reuse
2492 # try to find an existing branchmap to reuse
2440 subsettable = getbranchmapsubsettable()
2493 subsettable = getbranchmapsubsettable()
2441 candidatefilter = subsettable.get(None)
2494 candidatefilter = subsettable.get(None)
2442 while candidatefilter is not None:
2495 while candidatefilter is not None:
2443 candidatebm = repo.filtered(candidatefilter).branchmap()
2496 candidatebm = repo.filtered(candidatefilter).branchmap()
2444 if candidatebm.validfor(baserepo):
2497 if candidatebm.validfor(baserepo):
2445 filtered = repoview.filterrevs(repo, candidatefilter)
2498 filtered = repoview.filterrevs(repo, candidatefilter)
2446 missing = [r for r in allbaserevs if r in filtered]
2499 missing = [r for r in allbaserevs if r in filtered]
2447 base = candidatebm.copy()
2500 base = candidatebm.copy()
2448 base.update(baserepo, missing)
2501 base.update(baserepo, missing)
2449 break
2502 break
2450 candidatefilter = subsettable.get(candidatefilter)
2503 candidatefilter = subsettable.get(candidatefilter)
2451 else:
2504 else:
2452 # no suitable subset where found
2505 # no suitable subset where found
2453 base = branchmap.branchcache()
2506 base = branchmap.branchcache()
2454 base.update(baserepo, allbaserevs)
2507 base.update(baserepo, allbaserevs)
2455
2508
2456 def setup():
2509 def setup():
2457 x[0] = base.copy()
2510 x[0] = base.copy()
2458 if clearcaches:
2511 if clearcaches:
2459 unfi._revbranchcache = None
2512 unfi._revbranchcache = None
2460 clearchangelog(repo)
2513 clearchangelog(repo)
2461
2514
2462 def bench():
2515 def bench():
2463 x[0].update(targetrepo, newrevs)
2516 x[0].update(targetrepo, newrevs)
2464
2517
2465 timer(bench, setup=setup)
2518 timer(bench, setup=setup)
2466 fm.end()
2519 fm.end()
2467 finally:
2520 finally:
2468 repoview.filtertable.pop(b'__perf_branchmap_update_base', None)
2521 repoview.filtertable.pop(b'__perf_branchmap_update_base', None)
2469 repoview.filtertable.pop(b'__perf_branchmap_update_target', None)
2522 repoview.filtertable.pop(b'__perf_branchmap_update_target', None)
2470
2523
2471 @command(b'perfbranchmapload', [
2524 @command(b'perfbranchmapload', [
2472 (b'f', b'filter', b'', b'Specify repoview filter'),
2525 (b'f', b'filter', b'', b'Specify repoview filter'),
2473 (b'', b'list', False, b'List brachmap filter caches'),
2526 (b'', b'list', False, b'List brachmap filter caches'),
2474 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
2527 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
2475
2528
2476 ] + formatteropts)
2529 ] + formatteropts)
2477 def perfbranchmapload(ui, repo, filter=b'', list=False, **opts):
2530 def perfbranchmapload(ui, repo, filter=b'', list=False, **opts):
2478 """benchmark reading the branchmap"""
2531 """benchmark reading the branchmap"""
2479 opts = _byteskwargs(opts)
2532 opts = _byteskwargs(opts)
2480 clearrevlogs = opts[b'clear_revlogs']
2533 clearrevlogs = opts[b'clear_revlogs']
2481
2534
2482 if list:
2535 if list:
2483 for name, kind, st in repo.cachevfs.readdir(stat=True):
2536 for name, kind, st in repo.cachevfs.readdir(stat=True):
2484 if name.startswith(b'branch2'):
2537 if name.startswith(b'branch2'):
2485 filtername = name.partition(b'-')[2] or b'unfiltered'
2538 filtername = name.partition(b'-')[2] or b'unfiltered'
2486 ui.status(b'%s - %s\n'
2539 ui.status(b'%s - %s\n'
2487 % (filtername, util.bytecount(st.st_size)))
2540 % (filtername, util.bytecount(st.st_size)))
2488 return
2541 return
2489 if not filter:
2542 if not filter:
2490 filter = None
2543 filter = None
2491 subsettable = getbranchmapsubsettable()
2544 subsettable = getbranchmapsubsettable()
2492 if filter is None:
2545 if filter is None:
2493 repo = repo.unfiltered()
2546 repo = repo.unfiltered()
2494 else:
2547 else:
2495 repo = repoview.repoview(repo, filter)
2548 repo = repoview.repoview(repo, filter)
2496
2549
2497 repo.branchmap() # make sure we have a relevant, up to date branchmap
2550 repo.branchmap() # make sure we have a relevant, up to date branchmap
2498
2551
2499 currentfilter = filter
2552 currentfilter = filter
2500 # try once without timer, the filter may not be cached
2553 # try once without timer, the filter may not be cached
2501 while branchmap.read(repo) is None:
2554 while branchmap.read(repo) is None:
2502 currentfilter = subsettable.get(currentfilter)
2555 currentfilter = subsettable.get(currentfilter)
2503 if currentfilter is None:
2556 if currentfilter is None:
2504 raise error.Abort(b'No branchmap cached for %s repo'
2557 raise error.Abort(b'No branchmap cached for %s repo'
2505 % (filter or b'unfiltered'))
2558 % (filter or b'unfiltered'))
2506 repo = repo.filtered(currentfilter)
2559 repo = repo.filtered(currentfilter)
2507 timer, fm = gettimer(ui, opts)
2560 timer, fm = gettimer(ui, opts)
2508 def setup():
2561 def setup():
2509 if clearrevlogs:
2562 if clearrevlogs:
2510 clearchangelog(repo)
2563 clearchangelog(repo)
2511 def bench():
2564 def bench():
2512 branchmap.read(repo)
2565 branchmap.read(repo)
2513 timer(bench, setup=setup)
2566 timer(bench, setup=setup)
2514 fm.end()
2567 fm.end()
2515
2568
2516 @command(b'perfloadmarkers')
2569 @command(b'perfloadmarkers')
2517 def perfloadmarkers(ui, repo):
2570 def perfloadmarkers(ui, repo):
2518 """benchmark the time to parse the on-disk markers for a repo
2571 """benchmark the time to parse the on-disk markers for a repo
2519
2572
2520 Result is the number of markers in the repo."""
2573 Result is the number of markers in the repo."""
2521 timer, fm = gettimer(ui)
2574 timer, fm = gettimer(ui)
2522 svfs = getsvfs(repo)
2575 svfs = getsvfs(repo)
2523 timer(lambda: len(obsolete.obsstore(svfs)))
2576 timer(lambda: len(obsolete.obsstore(svfs)))
2524 fm.end()
2577 fm.end()
2525
2578
2526 @command(b'perflrucachedict', formatteropts +
2579 @command(b'perflrucachedict', formatteropts +
2527 [(b'', b'costlimit', 0, b'maximum total cost of items in cache'),
2580 [(b'', b'costlimit', 0, b'maximum total cost of items in cache'),
2528 (b'', b'mincost', 0, b'smallest cost of items in cache'),
2581 (b'', b'mincost', 0, b'smallest cost of items in cache'),
2529 (b'', b'maxcost', 100, b'maximum cost of items in cache'),
2582 (b'', b'maxcost', 100, b'maximum cost of items in cache'),
2530 (b'', b'size', 4, b'size of cache'),
2583 (b'', b'size', 4, b'size of cache'),
2531 (b'', b'gets', 10000, b'number of key lookups'),
2584 (b'', b'gets', 10000, b'number of key lookups'),
2532 (b'', b'sets', 10000, b'number of key sets'),
2585 (b'', b'sets', 10000, b'number of key sets'),
2533 (b'', b'mixed', 10000, b'number of mixed mode operations'),
2586 (b'', b'mixed', 10000, b'number of mixed mode operations'),
2534 (b'', b'mixedgetfreq', 50, b'frequency of get vs set ops in mixed mode')],
2587 (b'', b'mixedgetfreq', 50, b'frequency of get vs set ops in mixed mode')],
2535 norepo=True)
2588 norepo=True)
2536 def perflrucache(ui, mincost=0, maxcost=100, costlimit=0, size=4,
2589 def perflrucache(ui, mincost=0, maxcost=100, costlimit=0, size=4,
2537 gets=10000, sets=10000, mixed=10000, mixedgetfreq=50, **opts):
2590 gets=10000, sets=10000, mixed=10000, mixedgetfreq=50, **opts):
2538 opts = _byteskwargs(opts)
2591 opts = _byteskwargs(opts)
2539
2592
2540 def doinit():
2593 def doinit():
2541 for i in _xrange(10000):
2594 for i in _xrange(10000):
2542 util.lrucachedict(size)
2595 util.lrucachedict(size)
2543
2596
2544 costrange = list(range(mincost, maxcost + 1))
2597 costrange = list(range(mincost, maxcost + 1))
2545
2598
2546 values = []
2599 values = []
2547 for i in _xrange(size):
2600 for i in _xrange(size):
2548 values.append(random.randint(0, _maxint))
2601 values.append(random.randint(0, _maxint))
2549
2602
2550 # Get mode fills the cache and tests raw lookup performance with no
2603 # Get mode fills the cache and tests raw lookup performance with no
2551 # eviction.
2604 # eviction.
2552 getseq = []
2605 getseq = []
2553 for i in _xrange(gets):
2606 for i in _xrange(gets):
2554 getseq.append(random.choice(values))
2607 getseq.append(random.choice(values))
2555
2608
2556 def dogets():
2609 def dogets():
2557 d = util.lrucachedict(size)
2610 d = util.lrucachedict(size)
2558 for v in values:
2611 for v in values:
2559 d[v] = v
2612 d[v] = v
2560 for key in getseq:
2613 for key in getseq:
2561 value = d[key]
2614 value = d[key]
2562 value # silence pyflakes warning
2615 value # silence pyflakes warning
2563
2616
2564 def dogetscost():
2617 def dogetscost():
2565 d = util.lrucachedict(size, maxcost=costlimit)
2618 d = util.lrucachedict(size, maxcost=costlimit)
2566 for i, v in enumerate(values):
2619 for i, v in enumerate(values):
2567 d.insert(v, v, cost=costs[i])
2620 d.insert(v, v, cost=costs[i])
2568 for key in getseq:
2621 for key in getseq:
2569 try:
2622 try:
2570 value = d[key]
2623 value = d[key]
2571 value # silence pyflakes warning
2624 value # silence pyflakes warning
2572 except KeyError:
2625 except KeyError:
2573 pass
2626 pass
2574
2627
2575 # Set mode tests insertion speed with cache eviction.
2628 # Set mode tests insertion speed with cache eviction.
2576 setseq = []
2629 setseq = []
2577 costs = []
2630 costs = []
2578 for i in _xrange(sets):
2631 for i in _xrange(sets):
2579 setseq.append(random.randint(0, _maxint))
2632 setseq.append(random.randint(0, _maxint))
2580 costs.append(random.choice(costrange))
2633 costs.append(random.choice(costrange))
2581
2634
2582 def doinserts():
2635 def doinserts():
2583 d = util.lrucachedict(size)
2636 d = util.lrucachedict(size)
2584 for v in setseq:
2637 for v in setseq:
2585 d.insert(v, v)
2638 d.insert(v, v)
2586
2639
2587 def doinsertscost():
2640 def doinsertscost():
2588 d = util.lrucachedict(size, maxcost=costlimit)
2641 d = util.lrucachedict(size, maxcost=costlimit)
2589 for i, v in enumerate(setseq):
2642 for i, v in enumerate(setseq):
2590 d.insert(v, v, cost=costs[i])
2643 d.insert(v, v, cost=costs[i])
2591
2644
2592 def dosets():
2645 def dosets():
2593 d = util.lrucachedict(size)
2646 d = util.lrucachedict(size)
2594 for v in setseq:
2647 for v in setseq:
2595 d[v] = v
2648 d[v] = v
2596
2649
2597 # Mixed mode randomly performs gets and sets with eviction.
2650 # Mixed mode randomly performs gets and sets with eviction.
2598 mixedops = []
2651 mixedops = []
2599 for i in _xrange(mixed):
2652 for i in _xrange(mixed):
2600 r = random.randint(0, 100)
2653 r = random.randint(0, 100)
2601 if r < mixedgetfreq:
2654 if r < mixedgetfreq:
2602 op = 0
2655 op = 0
2603 else:
2656 else:
2604 op = 1
2657 op = 1
2605
2658
2606 mixedops.append((op,
2659 mixedops.append((op,
2607 random.randint(0, size * 2),
2660 random.randint(0, size * 2),
2608 random.choice(costrange)))
2661 random.choice(costrange)))
2609
2662
2610 def domixed():
2663 def domixed():
2611 d = util.lrucachedict(size)
2664 d = util.lrucachedict(size)
2612
2665
2613 for op, v, cost in mixedops:
2666 for op, v, cost in mixedops:
2614 if op == 0:
2667 if op == 0:
2615 try:
2668 try:
2616 d[v]
2669 d[v]
2617 except KeyError:
2670 except KeyError:
2618 pass
2671 pass
2619 else:
2672 else:
2620 d[v] = v
2673 d[v] = v
2621
2674
2622 def domixedcost():
2675 def domixedcost():
2623 d = util.lrucachedict(size, maxcost=costlimit)
2676 d = util.lrucachedict(size, maxcost=costlimit)
2624
2677
2625 for op, v, cost in mixedops:
2678 for op, v, cost in mixedops:
2626 if op == 0:
2679 if op == 0:
2627 try:
2680 try:
2628 d[v]
2681 d[v]
2629 except KeyError:
2682 except KeyError:
2630 pass
2683 pass
2631 else:
2684 else:
2632 d.insert(v, v, cost=cost)
2685 d.insert(v, v, cost=cost)
2633
2686
2634 benches = [
2687 benches = [
2635 (doinit, b'init'),
2688 (doinit, b'init'),
2636 ]
2689 ]
2637
2690
2638 if costlimit:
2691 if costlimit:
2639 benches.extend([
2692 benches.extend([
2640 (dogetscost, b'gets w/ cost limit'),
2693 (dogetscost, b'gets w/ cost limit'),
2641 (doinsertscost, b'inserts w/ cost limit'),
2694 (doinsertscost, b'inserts w/ cost limit'),
2642 (domixedcost, b'mixed w/ cost limit'),
2695 (domixedcost, b'mixed w/ cost limit'),
2643 ])
2696 ])
2644 else:
2697 else:
2645 benches.extend([
2698 benches.extend([
2646 (dogets, b'gets'),
2699 (dogets, b'gets'),
2647 (doinserts, b'inserts'),
2700 (doinserts, b'inserts'),
2648 (dosets, b'sets'),
2701 (dosets, b'sets'),
2649 (domixed, b'mixed')
2702 (domixed, b'mixed')
2650 ])
2703 ])
2651
2704
2652 for fn, title in benches:
2705 for fn, title in benches:
2653 timer, fm = gettimer(ui, opts)
2706 timer, fm = gettimer(ui, opts)
2654 timer(fn, title=title)
2707 timer(fn, title=title)
2655 fm.end()
2708 fm.end()
2656
2709
2657 @command(b'perfwrite', formatteropts)
2710 @command(b'perfwrite', formatteropts)
2658 def perfwrite(ui, repo, **opts):
2711 def perfwrite(ui, repo, **opts):
2659 """microbenchmark ui.write
2712 """microbenchmark ui.write
2660 """
2713 """
2661 opts = _byteskwargs(opts)
2714 opts = _byteskwargs(opts)
2662
2715
2663 timer, fm = gettimer(ui, opts)
2716 timer, fm = gettimer(ui, opts)
2664 def write():
2717 def write():
2665 for i in range(100000):
2718 for i in range(100000):
2666 ui.write((b'Testing write performance\n'))
2719 ui.write((b'Testing write performance\n'))
2667 timer(write)
2720 timer(write)
2668 fm.end()
2721 fm.end()
2669
2722
2670 def uisetup(ui):
2723 def uisetup(ui):
2671 if (util.safehasattr(cmdutil, b'openrevlog') and
2724 if (util.safehasattr(cmdutil, b'openrevlog') and
2672 not util.safehasattr(commands, b'debugrevlogopts')):
2725 not util.safehasattr(commands, b'debugrevlogopts')):
2673 # for "historical portability":
2726 # for "historical portability":
2674 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
2727 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
2675 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
2728 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
2676 # openrevlog() should cause failure, because it has been
2729 # openrevlog() should cause failure, because it has been
2677 # available since 3.5 (or 49c583ca48c4).
2730 # available since 3.5 (or 49c583ca48c4).
2678 def openrevlog(orig, repo, cmd, file_, opts):
2731 def openrevlog(orig, repo, cmd, file_, opts):
2679 if opts.get(b'dir') and not util.safehasattr(repo, b'dirlog'):
2732 if opts.get(b'dir') and not util.safehasattr(repo, b'dirlog'):
2680 raise error.Abort(b"This version doesn't support --dir option",
2733 raise error.Abort(b"This version doesn't support --dir option",
2681 hint=b"use 3.5 or later")
2734 hint=b"use 3.5 or later")
2682 return orig(repo, cmd, file_, opts)
2735 return orig(repo, cmd, file_, opts)
2683 extensions.wrapfunction(cmdutil, b'openrevlog', openrevlog)
2736 extensions.wrapfunction(cmdutil, b'openrevlog', openrevlog)
2684
2737
2685 @command(b'perfprogress', formatteropts + [
2738 @command(b'perfprogress', formatteropts + [
2686 (b'', b'topic', b'topic', b'topic for progress messages'),
2739 (b'', b'topic', b'topic', b'topic for progress messages'),
2687 (b'c', b'total', 1000000, b'total value we are progressing to'),
2740 (b'c', b'total', 1000000, b'total value we are progressing to'),
2688 ], norepo=True)
2741 ], norepo=True)
2689 def perfprogress(ui, topic=None, total=None, **opts):
2742 def perfprogress(ui, topic=None, total=None, **opts):
2690 """printing of progress bars"""
2743 """printing of progress bars"""
2691 opts = _byteskwargs(opts)
2744 opts = _byteskwargs(opts)
2692
2745
2693 timer, fm = gettimer(ui, opts)
2746 timer, fm = gettimer(ui, opts)
2694
2747
2695 def doprogress():
2748 def doprogress():
2696 with ui.makeprogress(topic, total=total) as progress:
2749 with ui.makeprogress(topic, total=total) as progress:
2697 for i in pycompat.xrange(total):
2750 for i in pycompat.xrange(total):
2698 progress.increment()
2751 progress.increment()
2699
2752
2700 timer(doprogress)
2753 timer(doprogress)
2701 fm.end()
2754 fm.end()
@@ -1,300 +1,302 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 perfbranchmapload
58 perfbranchmapload
59 benchmark reading the branchmap
59 benchmark reading the branchmap
60 perfbranchmapupdate
60 perfbranchmapupdate
61 benchmark branchmap update from for <base> revs to <target>
61 benchmark branchmap update from for <base> revs to <target>
62 revs
62 revs
63 perfbundleread
63 perfbundleread
64 Benchmark reading of bundle files.
64 Benchmark reading of bundle files.
65 perfcca (no help text available)
65 perfcca (no help text available)
66 perfchangegroupchangelog
66 perfchangegroupchangelog
67 Benchmark producing a changelog group for a changegroup.
67 Benchmark producing a changelog group for a changegroup.
68 perfchangeset
68 perfchangeset
69 (no help text available)
69 (no help text available)
70 perfctxfiles (no help text available)
70 perfctxfiles (no help text available)
71 perfdiffwd Profile diff of working directory changes
71 perfdiffwd Profile diff of working directory changes
72 perfdirfoldmap
72 perfdirfoldmap
73 (no help text available)
73 (no help text available)
74 perfdirs (no help text available)
74 perfdirs (no help text available)
75 perfdirstate (no help text available)
75 perfdirstate (no help text available)
76 perfdirstatedirs
76 perfdirstatedirs
77 (no help text available)
77 (no help text available)
78 perfdirstatefoldmap
78 perfdirstatefoldmap
79 (no help text available)
79 (no help text available)
80 perfdirstatewrite
80 perfdirstatewrite
81 (no help text available)
81 (no help text available)
82 perfdiscovery
82 perfdiscovery
83 benchmark discovery between local repo and the peer at given
83 benchmark discovery between local repo and the peer at given
84 path
84 path
85 perffncacheencode
85 perffncacheencode
86 (no help text available)
86 (no help text available)
87 perffncacheload
87 perffncacheload
88 (no help text available)
88 (no help text available)
89 perffncachewrite
89 perffncachewrite
90 (no help text available)
90 (no help text available)
91 perfheads benchmark the computation of a changelog heads
91 perfheads benchmark the computation of a changelog heads
92 perfhelper-pathcopies
92 perfhelper-pathcopies
93 find statistic about potential parameters for the
93 find statistic about potential parameters for the
94 'perftracecopies'
94 'perftracecopies'
95 perfignore benchmark operation related to computing ignore
95 perfignore benchmark operation related to computing ignore
96 perfindex benchmark index creation time followed by a lookup
96 perfindex benchmark index creation time followed by a lookup
97 perflinelogedits
97 perflinelogedits
98 (no help text available)
98 (no help text available)
99 perfloadmarkers
99 perfloadmarkers
100 benchmark the time to parse the on-disk markers for a repo
100 benchmark the time to parse the on-disk markers for a repo
101 perflog (no help text available)
101 perflog (no help text available)
102 perflookup (no help text available)
102 perflookup (no help text available)
103 perflrucachedict
103 perflrucachedict
104 (no help text available)
104 (no help text available)
105 perfmanifest benchmark the time to read a manifest from disk and return a
105 perfmanifest benchmark the time to read a manifest from disk and return a
106 usable
106 usable
107 perfmergecalculate
107 perfmergecalculate
108 (no help text available)
108 (no help text available)
109 perfmoonwalk benchmark walking the changelog backwards
109 perfmoonwalk benchmark walking the changelog backwards
110 perfnodelookup
110 perfnodelookup
111 (no help text available)
111 (no help text available)
112 perfnodemap benchmark the time necessary to look up revision from a cold
113 nodemap
112 perfparents (no help text available)
114 perfparents (no help text available)
113 perfpathcopies
115 perfpathcopies
114 benchmark the copy tracing logic
116 benchmark the copy tracing logic
115 perfphases benchmark phasesets computation
117 perfphases benchmark phasesets computation
116 perfphasesremote
118 perfphasesremote
117 benchmark time needed to analyse phases of the remote server
119 benchmark time needed to analyse phases of the remote server
118 perfprogress printing of progress bars
120 perfprogress printing of progress bars
119 perfrawfiles (no help text available)
121 perfrawfiles (no help text available)
120 perfrevlogchunks
122 perfrevlogchunks
121 Benchmark operations on revlog chunks.
123 Benchmark operations on revlog chunks.
122 perfrevlogindex
124 perfrevlogindex
123 Benchmark operations against a revlog index.
125 Benchmark operations against a revlog index.
124 perfrevlogrevision
126 perfrevlogrevision
125 Benchmark obtaining a revlog revision.
127 Benchmark obtaining a revlog revision.
126 perfrevlogrevisions
128 perfrevlogrevisions
127 Benchmark reading a series of revisions from a revlog.
129 Benchmark reading a series of revisions from a revlog.
128 perfrevlogwrite
130 perfrevlogwrite
129 Benchmark writing a series of revisions to a revlog.
131 Benchmark writing a series of revisions to a revlog.
130 perfrevrange (no help text available)
132 perfrevrange (no help text available)
131 perfrevset benchmark the execution time of a revset
133 perfrevset benchmark the execution time of a revset
132 perfstartup (no help text available)
134 perfstartup (no help text available)
133 perfstatus (no help text available)
135 perfstatus (no help text available)
134 perftags (no help text available)
136 perftags (no help text available)
135 perftemplating
137 perftemplating
136 test the rendering time of a given template
138 test the rendering time of a given template
137 perfunidiff benchmark a unified diff between revisions
139 perfunidiff benchmark a unified diff between revisions
138 perfvolatilesets
140 perfvolatilesets
139 benchmark the computation of various volatile set
141 benchmark the computation of various volatile set
140 perfwalk (no help text available)
142 perfwalk (no help text available)
141 perfwrite microbenchmark ui.write
143 perfwrite microbenchmark ui.write
142
144
143 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
145 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
144 $ hg perfaddremove
146 $ hg perfaddremove
145 $ hg perfancestors
147 $ hg perfancestors
146 $ hg perfancestorset 2
148 $ hg perfancestorset 2
147 $ hg perfannotate a
149 $ hg perfannotate a
148 $ hg perfbdiff -c 1
150 $ hg perfbdiff -c 1
149 $ hg perfbdiff --alldata 1
151 $ hg perfbdiff --alldata 1
150 $ hg perfunidiff -c 1
152 $ hg perfunidiff -c 1
151 $ hg perfunidiff --alldata 1
153 $ hg perfunidiff --alldata 1
152 $ hg perfbookmarks
154 $ hg perfbookmarks
153 $ hg perfbranchmap
155 $ hg perfbranchmap
154 $ hg perfbranchmapload
156 $ hg perfbranchmapload
155 $ hg perfbranchmapupdate --base "not tip" --target "tip"
157 $ hg perfbranchmapupdate --base "not tip" --target "tip"
156 benchmark of branchmap with 3 revisions with 1 new ones
158 benchmark of branchmap with 3 revisions with 1 new ones
157 $ hg perfcca
159 $ hg perfcca
158 $ hg perfchangegroupchangelog
160 $ hg perfchangegroupchangelog
159 $ hg perfchangegroupchangelog --cgversion 01
161 $ hg perfchangegroupchangelog --cgversion 01
160 $ hg perfchangeset 2
162 $ hg perfchangeset 2
161 $ hg perfctxfiles 2
163 $ hg perfctxfiles 2
162 $ hg perfdiffwd
164 $ hg perfdiffwd
163 $ hg perfdirfoldmap
165 $ hg perfdirfoldmap
164 $ hg perfdirs
166 $ hg perfdirs
165 $ hg perfdirstate
167 $ hg perfdirstate
166 $ hg perfdirstatedirs
168 $ hg perfdirstatedirs
167 $ hg perfdirstatefoldmap
169 $ hg perfdirstatefoldmap
168 $ hg perfdirstatewrite
170 $ hg perfdirstatewrite
169 #if repofncache
171 #if repofncache
170 $ hg perffncacheencode
172 $ hg perffncacheencode
171 $ hg perffncacheload
173 $ hg perffncacheload
172 $ hg debugrebuildfncache
174 $ hg debugrebuildfncache
173 fncache already up to date
175 fncache already up to date
174 $ hg perffncachewrite
176 $ hg perffncachewrite
175 $ hg debugrebuildfncache
177 $ hg debugrebuildfncache
176 fncache already up to date
178 fncache already up to date
177 #endif
179 #endif
178 $ hg perfheads
180 $ hg perfheads
179 $ hg perfignore
181 $ hg perfignore
180 $ hg perfindex
182 $ hg perfindex
181 $ hg perflinelogedits -n 1
183 $ hg perflinelogedits -n 1
182 $ hg perfloadmarkers
184 $ hg perfloadmarkers
183 $ hg perflog
185 $ hg perflog
184 $ hg perflookup 2
186 $ hg perflookup 2
185 $ hg perflrucache
187 $ hg perflrucache
186 $ hg perfmanifest 2
188 $ hg perfmanifest 2
187 $ hg perfmanifest -m 44fe2c8352bb3a478ffd7d8350bbc721920134d1
189 $ hg perfmanifest -m 44fe2c8352bb3a478ffd7d8350bbc721920134d1
188 $ hg perfmanifest -m 44fe2c8352bb
190 $ hg perfmanifest -m 44fe2c8352bb
189 abort: manifest revision must be integer or full node
191 abort: manifest revision must be integer or full node
190 [255]
192 [255]
191 $ hg perfmergecalculate -r 3
193 $ hg perfmergecalculate -r 3
192 $ hg perfmoonwalk
194 $ hg perfmoonwalk
193 $ hg perfnodelookup 2
195 $ hg perfnodelookup 2
194 $ hg perfpathcopies 1 2
196 $ hg perfpathcopies 1 2
195 $ hg perfprogress --total 1000
197 $ hg perfprogress --total 1000
196 $ hg perfrawfiles 2
198 $ hg perfrawfiles 2
197 $ hg perfrevlogindex -c
199 $ hg perfrevlogindex -c
198 #if reporevlogstore
200 #if reporevlogstore
199 $ hg perfrevlogrevisions .hg/store/data/a.i
201 $ hg perfrevlogrevisions .hg/store/data/a.i
200 #endif
202 #endif
201 $ hg perfrevlogrevision -m 0
203 $ hg perfrevlogrevision -m 0
202 $ hg perfrevlogchunks -c
204 $ hg perfrevlogchunks -c
203 $ hg perfrevrange
205 $ hg perfrevrange
204 $ hg perfrevset 'all()'
206 $ hg perfrevset 'all()'
205 $ hg perfstartup
207 $ hg perfstartup
206 $ hg perfstatus
208 $ hg perfstatus
207 $ hg perftags
209 $ hg perftags
208 $ hg perftemplating
210 $ hg perftemplating
209 $ hg perfvolatilesets
211 $ hg perfvolatilesets
210 $ hg perfwalk
212 $ hg perfwalk
211 $ hg perfparents
213 $ hg perfparents
212 $ hg perfdiscovery -q .
214 $ hg perfdiscovery -q .
213
215
214 test actual output
216 test actual output
215 ------------------
217 ------------------
216
218
217 normal output:
219 normal output:
218
220
219 $ hg perfheads --config perf.stub=no
221 $ hg perfheads --config perf.stub=no
220 ! wall * comb * user * sys * (best of *) (glob)
222 ! wall * comb * user * sys * (best of *) (glob)
221
223
222 detailed output:
224 detailed output:
223
225
224 $ hg perfheads --config perf.all-timing=yes --config perf.stub=no
226 $ hg perfheads --config perf.all-timing=yes --config perf.stub=no
225 ! wall * comb * user * sys * (best of *) (glob)
227 ! wall * comb * user * sys * (best of *) (glob)
226 ! wall * comb * user * sys * (max of *) (glob)
228 ! wall * comb * user * sys * (max of *) (glob)
227 ! wall * comb * user * sys * (avg of *) (glob)
229 ! wall * comb * user * sys * (avg of *) (glob)
228 ! wall * comb * user * sys * (median of *) (glob)
230 ! wall * comb * user * sys * (median of *) (glob)
229
231
230 test json output
232 test json output
231 ----------------
233 ----------------
232
234
233 normal output:
235 normal output:
234
236
235 $ hg perfheads --template json --config perf.stub=no
237 $ hg perfheads --template json --config perf.stub=no
236 [
238 [
237 {
239 {
238 "comb": *, (glob)
240 "comb": *, (glob)
239 "count": *, (glob)
241 "count": *, (glob)
240 "sys": *, (glob)
242 "sys": *, (glob)
241 "user": *, (glob)
243 "user": *, (glob)
242 "wall": * (glob)
244 "wall": * (glob)
243 }
245 }
244 ]
246 ]
245
247
246 detailed output:
248 detailed output:
247
249
248 $ hg perfheads --template json --config perf.all-timing=yes --config perf.stub=no
250 $ hg perfheads --template json --config perf.all-timing=yes --config perf.stub=no
249 [
251 [
250 {
252 {
251 "avg.comb": *, (glob)
253 "avg.comb": *, (glob)
252 "avg.count": *, (glob)
254 "avg.count": *, (glob)
253 "avg.sys": *, (glob)
255 "avg.sys": *, (glob)
254 "avg.user": *, (glob)
256 "avg.user": *, (glob)
255 "avg.wall": *, (glob)
257 "avg.wall": *, (glob)
256 "comb": *, (glob)
258 "comb": *, (glob)
257 "count": *, (glob)
259 "count": *, (glob)
258 "max.comb": *, (glob)
260 "max.comb": *, (glob)
259 "max.count": *, (glob)
261 "max.count": *, (glob)
260 "max.sys": *, (glob)
262 "max.sys": *, (glob)
261 "max.user": *, (glob)
263 "max.user": *, (glob)
262 "max.wall": *, (glob)
264 "max.wall": *, (glob)
263 "median.comb": *, (glob)
265 "median.comb": *, (glob)
264 "median.count": *, (glob)
266 "median.count": *, (glob)
265 "median.sys": *, (glob)
267 "median.sys": *, (glob)
266 "median.user": *, (glob)
268 "median.user": *, (glob)
267 "median.wall": *, (glob)
269 "median.wall": *, (glob)
268 "sys": *, (glob)
270 "sys": *, (glob)
269 "user": *, (glob)
271 "user": *, (glob)
270 "wall": * (glob)
272 "wall": * (glob)
271 }
273 }
272 ]
274 ]
273
275
274 Check perf.py for historical portability
276 Check perf.py for historical portability
275 ----------------------------------------
277 ----------------------------------------
276
278
277 $ cd "$TESTDIR/.."
279 $ cd "$TESTDIR/.."
278
280
279 $ (testrepohg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
281 $ (testrepohg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
280 > testrepohg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
282 > testrepohg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
281 > "$TESTDIR"/check-perf-code.py contrib/perf.py
283 > "$TESTDIR"/check-perf-code.py contrib/perf.py
282 contrib/perf.py:\d+: (re)
284 contrib/perf.py:\d+: (re)
283 > from mercurial import (
285 > from mercurial import (
284 import newer module separately in try clause for early Mercurial
286 import newer module separately in try clause for early Mercurial
285 contrib/perf.py:\d+: (re)
287 contrib/perf.py:\d+: (re)
286 > from mercurial import (
288 > from mercurial import (
287 import newer module separately in try clause for early Mercurial
289 import newer module separately in try clause for early Mercurial
288 contrib/perf.py:\d+: (re)
290 contrib/perf.py:\d+: (re)
289 > origindexpath = orig.opener.join(orig.indexfile)
291 > origindexpath = orig.opener.join(orig.indexfile)
290 use getvfs()/getsvfs() for early Mercurial
292 use getvfs()/getsvfs() for early Mercurial
291 contrib/perf.py:\d+: (re)
293 contrib/perf.py:\d+: (re)
292 > origdatapath = orig.opener.join(orig.datafile)
294 > origdatapath = orig.opener.join(orig.datafile)
293 use getvfs()/getsvfs() for early Mercurial
295 use getvfs()/getsvfs() for early Mercurial
294 contrib/perf.py:\d+: (re)
296 contrib/perf.py:\d+: (re)
295 > vfs = vfsmod.vfs(tmpdir)
297 > vfs = vfsmod.vfs(tmpdir)
296 use getvfs()/getsvfs() for early Mercurial
298 use getvfs()/getsvfs() for early Mercurial
297 contrib/perf.py:\d+: (re)
299 contrib/perf.py:\d+: (re)
298 > vfs.options = getattr(orig.opener, 'options', None)
300 > vfs.options = getattr(orig.opener, 'options', None)
299 use getvfs()/getsvfs() for early Mercurial
301 use getvfs()/getsvfs() for early Mercurial
300 [1]
302 [1]
General Comments 0
You need to be logged in to leave comments. Login now