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