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