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