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