##// END OF EJS Templates
perf: fix typo in perfphasesremote()
Yuya Nishihara -
r39229:cee9043c stable
parent child Browse files
Show More
@@ -1,1884 +1,1884
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 == '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 ('c', 'changelog', False, ('open changelog')),
127 ('m', 'manifest', False, ('open manifest')),
127 ('m', 'manifest', False, ('open manifest')),
128 ('', 'dir', False, ('open directory manifest')),
128 ('', 'dir', False, ('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("^").split("|")
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 '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 += ' %s' % ' '.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 += ' %s' % ' '.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('perf', 'presleep',
173 default=mercurial.configitems.dynamicdefault,
173 default=mercurial.configitems.dynamicdefault,
174 )
174 )
175 configitem('perf', 'stub',
175 configitem('perf', 'stub',
176 default=mercurial.configitems.dynamicdefault,
176 default=mercurial.configitems.dynamicdefault,
177 )
177 )
178 configitem('perf', 'parentscount',
178 configitem('perf', 'parentscount',
179 default=mercurial.configitems.dynamicdefault,
179 default=mercurial.configitems.dynamicdefault,
180 )
180 )
181 configitem('perf', 'all-timing',
181 configitem('perf', '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("perf", "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, "perf", "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, '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('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, '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("perf", "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("perf", "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('title', '! %s\n', title)
284 if r:
284 if r:
285 fm.write('result', '! result: %s\n', r)
285 fm.write('result', '! result: %s\n', r)
286 def display(role, entry):
286 def display(role, entry):
287 prefix = ''
287 prefix = ''
288 if role != 'best':
288 if role != 'best':
289 prefix = '%s.' % role
289 prefix = '%s.' % role
290 fm.plain('!')
290 fm.plain('!')
291 fm.write(prefix + 'wall', ' wall %f', entry[0])
291 fm.write(prefix + 'wall', ' wall %f', entry[0])
292 fm.write(prefix + 'comb', ' comb %f', entry[1] + entry[2])
292 fm.write(prefix + 'comb', ' comb %f', entry[1] + entry[2])
293 fm.write(prefix + 'user', ' user %f', entry[1])
293 fm.write(prefix + 'user', ' user %f', entry[1])
294 fm.write(prefix + 'sys', ' sys %f', entry[2])
294 fm.write(prefix + 'sys', ' sys %f', entry[2])
295 fm.write(prefix + 'count', ' (%s of %d)', role, count)
295 fm.write(prefix + 'count', ' (%s of %d)', role, count)
296 fm.plain('\n')
296 fm.plain('\n')
297 results.sort()
297 results.sort()
298 min_val = results[0]
298 min_val = results[0]
299 display('best', min_val)
299 display('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('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('avg', avg)
305 median = results[len(results) // 2]
305 median = results[len(results) // 2]
306 display('median', median)
306 display('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(("%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(("missing attribute %s of %s might break assumption"
341 " of performance measurement") % (name, obj))
341 " 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(("perfbranchmap not available with this Mercurial"),
368 hint="use 2.5 or later")
368 hint="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, '_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 '_tagscache' in vars(repo):
403 del repo.__dict__['_tagscache']
403 del repo.__dict__['_tagscache']
404 return clearcache
404 return clearcache
405
405
406 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
406 repotags = safeattrsetter(repo, '_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, '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(("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('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('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['.'][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('perfstatus',
447 [('u', 'unknown', False,
447 [('u', 'unknown', False,
448 'ask status to look for unknown files')] + formatteropts)
448 '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['unknown']))))
455 fm.end()
455 fm.end()
456
456
457 @command('perfaddremove', formatteropts)
457 @command('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['dry_run'] = True
465 timer(lambda: scmutil.addremove(repo, matcher, "", opts))
465 timer(lambda: scmutil.addremove(repo, matcher, "", 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, 'clearcaches'):
473 cl.clearcaches()
473 cl.clearcaches()
474 elif util.safehasattr(cl, '_nodecache'):
474 elif util.safehasattr(cl, '_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('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('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('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('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('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, '_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('perfbundleread', formatteropts, '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, '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, '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, '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, '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), 'read(8k)'),
614 (makestdioread(16384), 'read(16k)'),
614 (makestdioread(16384), 'read(16k)'),
615 (makestdioread(32768), 'read(32k)'),
615 (makestdioread(32768), 'read(32k)'),
616 (makestdioread(131072), 'read(128k)'),
616 (makestdioread(131072), 'read(128k)'),
617 ]
617 ]
618
618
619 with open(bundlepath, 'rb') as fh:
619 with open(bundlepath, '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), 'cg1 deltaiter()'),
625 (makebench(iterchunks), 'cg1 getchunks()'),
625 (makebench(iterchunks), 'cg1 getchunks()'),
626 (makereadnbytes(8192), 'cg1 read(8k)'),
626 (makereadnbytes(8192), 'cg1 read(8k)'),
627 (makereadnbytes(16384), 'cg1 read(16k)'),
627 (makereadnbytes(16384), 'cg1 read(16k)'),
628 (makereadnbytes(32768), 'cg1 read(32k)'),
628 (makereadnbytes(32768), 'cg1 read(32k)'),
629 (makereadnbytes(131072), 'cg1 read(128k)'),
629 (makereadnbytes(131072), '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), 'bundle2 forwardchunks()'),
634 (makebench(iterparts), 'bundle2 iterparts()'),
634 (makebench(iterparts), 'bundle2 iterparts()'),
635 (makebench(iterpartsseekable), 'bundle2 iterparts() seekable'),
635 (makebench(iterpartsseekable), 'bundle2 iterparts() seekable'),
636 (makebench(seek), 'bundle2 part seek()'),
636 (makebench(seek), 'bundle2 part seek()'),
637 (makepartreadnbytes(8192), 'bundle2 part read(8k)'),
637 (makepartreadnbytes(8192), 'bundle2 part read(8k)'),
638 (makepartreadnbytes(16384), 'bundle2 part read(16k)'),
638 (makepartreadnbytes(16384), 'bundle2 part read(16k)'),
639 (makepartreadnbytes(32768), 'bundle2 part read(32k)'),
639 (makepartreadnbytes(32768), 'bundle2 part read(32k)'),
640 (makepartreadnbytes(131072), 'bundle2 part read(128k)'),
640 (makepartreadnbytes(131072), '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('stream clone bundles not supported')
644 else:
644 else:
645 raise error.Abort('unhandled bundle type: %s' % type(bundle))
645 raise error.Abort('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('perfchangegroupchangelog', formatteropts +
653 [('', 'version', '02', 'changegroup version'),
653 [('', 'version', '02', 'changegroup version'),
654 ('r', 'rev', '', 'revisions to add to changegroup')])
654 ('r', 'rev', '', 'revisions to add to changegroup')])
655 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
655 def perfchangegroupchangelog(ui, repo, version='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 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
666 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
667 bundler = changegroup.getbundler(version, repo)
667 bundler = changegroup.getbundler(version, repo)
668
668
669 def lookup(node):
669 def lookup(node):
670 # The real bundler reads the revision in order to access the
670 # The real bundler reads the revision in order to access the
671 # manifest node and files list. Do that here.
671 # manifest node and files list. Do that here.
672 cl.read(node)
672 cl.read(node)
673 return node
673 return node
674
674
675 def d():
675 def d():
676 for chunk in bundler.group(revs, cl, lookup):
676 for chunk in bundler.group(revs, cl, lookup):
677 pass
677 pass
678
678
679 timer, fm = gettimer(ui, opts)
679 timer, fm = gettimer(ui, opts)
680 timer(d)
680 timer(d)
681 fm.end()
681 fm.end()
682
682
683 @command('perfdirs', formatteropts)
683 @command('perfdirs', formatteropts)
684 def perfdirs(ui, repo, **opts):
684 def perfdirs(ui, repo, **opts):
685 timer, fm = gettimer(ui, opts)
685 timer, fm = gettimer(ui, opts)
686 dirstate = repo.dirstate
686 dirstate = repo.dirstate
687 'a' in dirstate
687 'a' in dirstate
688 def d():
688 def d():
689 dirstate.hasdir('a')
689 dirstate.hasdir('a')
690 del dirstate._map._dirs
690 del dirstate._map._dirs
691 timer(d)
691 timer(d)
692 fm.end()
692 fm.end()
693
693
694 @command('perfdirstate', formatteropts)
694 @command('perfdirstate', formatteropts)
695 def perfdirstate(ui, repo, **opts):
695 def perfdirstate(ui, repo, **opts):
696 timer, fm = gettimer(ui, opts)
696 timer, fm = gettimer(ui, opts)
697 "a" in repo.dirstate
697 "a" in repo.dirstate
698 def d():
698 def d():
699 repo.dirstate.invalidate()
699 repo.dirstate.invalidate()
700 "a" in repo.dirstate
700 "a" in repo.dirstate
701 timer(d)
701 timer(d)
702 fm.end()
702 fm.end()
703
703
704 @command('perfdirstatedirs', formatteropts)
704 @command('perfdirstatedirs', formatteropts)
705 def perfdirstatedirs(ui, repo, **opts):
705 def perfdirstatedirs(ui, repo, **opts):
706 timer, fm = gettimer(ui, opts)
706 timer, fm = gettimer(ui, opts)
707 "a" in repo.dirstate
707 "a" in repo.dirstate
708 def d():
708 def d():
709 repo.dirstate.hasdir("a")
709 repo.dirstate.hasdir("a")
710 del repo.dirstate._map._dirs
710 del repo.dirstate._map._dirs
711 timer(d)
711 timer(d)
712 fm.end()
712 fm.end()
713
713
714 @command('perfdirstatefoldmap', formatteropts)
714 @command('perfdirstatefoldmap', formatteropts)
715 def perfdirstatefoldmap(ui, repo, **opts):
715 def perfdirstatefoldmap(ui, repo, **opts):
716 timer, fm = gettimer(ui, opts)
716 timer, fm = gettimer(ui, opts)
717 dirstate = repo.dirstate
717 dirstate = repo.dirstate
718 'a' in dirstate
718 'a' in dirstate
719 def d():
719 def d():
720 dirstate._map.filefoldmap.get('a')
720 dirstate._map.filefoldmap.get('a')
721 del dirstate._map.filefoldmap
721 del dirstate._map.filefoldmap
722 timer(d)
722 timer(d)
723 fm.end()
723 fm.end()
724
724
725 @command('perfdirfoldmap', formatteropts)
725 @command('perfdirfoldmap', formatteropts)
726 def perfdirfoldmap(ui, repo, **opts):
726 def perfdirfoldmap(ui, repo, **opts):
727 timer, fm = gettimer(ui, opts)
727 timer, fm = gettimer(ui, opts)
728 dirstate = repo.dirstate
728 dirstate = repo.dirstate
729 'a' in dirstate
729 'a' in dirstate
730 def d():
730 def d():
731 dirstate._map.dirfoldmap.get('a')
731 dirstate._map.dirfoldmap.get('a')
732 del dirstate._map.dirfoldmap
732 del dirstate._map.dirfoldmap
733 del dirstate._map._dirs
733 del dirstate._map._dirs
734 timer(d)
734 timer(d)
735 fm.end()
735 fm.end()
736
736
737 @command('perfdirstatewrite', formatteropts)
737 @command('perfdirstatewrite', formatteropts)
738 def perfdirstatewrite(ui, repo, **opts):
738 def perfdirstatewrite(ui, repo, **opts):
739 timer, fm = gettimer(ui, opts)
739 timer, fm = gettimer(ui, opts)
740 ds = repo.dirstate
740 ds = repo.dirstate
741 "a" in ds
741 "a" in ds
742 def d():
742 def d():
743 ds._dirty = True
743 ds._dirty = True
744 ds.write(repo.currenttransaction())
744 ds.write(repo.currenttransaction())
745 timer(d)
745 timer(d)
746 fm.end()
746 fm.end()
747
747
748 @command('perfmergecalculate',
748 @command('perfmergecalculate',
749 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
749 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
750 def perfmergecalculate(ui, repo, rev, **opts):
750 def perfmergecalculate(ui, repo, rev, **opts):
751 timer, fm = gettimer(ui, opts)
751 timer, fm = gettimer(ui, opts)
752 wctx = repo[None]
752 wctx = repo[None]
753 rctx = scmutil.revsingle(repo, rev, rev)
753 rctx = scmutil.revsingle(repo, rev, rev)
754 ancestor = wctx.ancestor(rctx)
754 ancestor = wctx.ancestor(rctx)
755 # we don't want working dir files to be stat'd in the benchmark, so prime
755 # we don't want working dir files to be stat'd in the benchmark, so prime
756 # that cache
756 # that cache
757 wctx.dirty()
757 wctx.dirty()
758 def d():
758 def d():
759 # acceptremote is True because we don't want prompts in the middle of
759 # acceptremote is True because we don't want prompts in the middle of
760 # our benchmark
760 # our benchmark
761 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
761 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
762 acceptremote=True, followcopies=True)
762 acceptremote=True, followcopies=True)
763 timer(d)
763 timer(d)
764 fm.end()
764 fm.end()
765
765
766 @command('perfpathcopies', [], "REV REV")
766 @command('perfpathcopies', [], "REV REV")
767 def perfpathcopies(ui, repo, rev1, rev2, **opts):
767 def perfpathcopies(ui, repo, rev1, rev2, **opts):
768 timer, fm = gettimer(ui, opts)
768 timer, fm = gettimer(ui, opts)
769 ctx1 = scmutil.revsingle(repo, rev1, rev1)
769 ctx1 = scmutil.revsingle(repo, rev1, rev1)
770 ctx2 = scmutil.revsingle(repo, rev2, rev2)
770 ctx2 = scmutil.revsingle(repo, rev2, rev2)
771 def d():
771 def d():
772 copies.pathcopies(ctx1, ctx2)
772 copies.pathcopies(ctx1, ctx2)
773 timer(d)
773 timer(d)
774 fm.end()
774 fm.end()
775
775
776 @command('perfphases',
776 @command('perfphases',
777 [('', 'full', False, 'include file reading time too'),
777 [('', 'full', False, 'include file reading time too'),
778 ], "")
778 ], "")
779 def perfphases(ui, repo, **opts):
779 def perfphases(ui, repo, **opts):
780 """benchmark phasesets computation"""
780 """benchmark phasesets computation"""
781 timer, fm = gettimer(ui, opts)
781 timer, fm = gettimer(ui, opts)
782 _phases = repo._phasecache
782 _phases = repo._phasecache
783 full = opts.get('full')
783 full = opts.get('full')
784 def d():
784 def d():
785 phases = _phases
785 phases = _phases
786 if full:
786 if full:
787 clearfilecache(repo, '_phasecache')
787 clearfilecache(repo, '_phasecache')
788 phases = repo._phasecache
788 phases = repo._phasecache
789 phases.invalidate()
789 phases.invalidate()
790 phases.loadphaserevs(repo)
790 phases.loadphaserevs(repo)
791 timer(d)
791 timer(d)
792 fm.end()
792 fm.end()
793
793
794 @command('perfphasesremote',
794 @command('perfphasesremote',
795 [], "[DEST]")
795 [], "[DEST]")
796 def perfphasesremote(ui, repo, dest=None, **opts):
796 def perfphasesremote(ui, repo, dest=None, **opts):
797 """benchmark time needed to analyse phases of the remote server"""
797 """benchmark time needed to analyse phases of the remote server"""
798 from mercurial.node import (
798 from mercurial.node import (
799 bin,
799 bin,
800 )
800 )
801 from mercurial import (
801 from mercurial import (
802 exchange,
802 exchange,
803 hg,
803 hg,
804 phases,
804 phases,
805 )
805 )
806 timer, fm = gettimer(ui, opts)
806 timer, fm = gettimer(ui, opts)
807
807
808 path = ui.paths.getpath(dest, default=('default-push', 'default'))
808 path = ui.paths.getpath(dest, default=('default-push', 'default'))
809 if not path:
809 if not path:
810 raise error.abort(('default repository not configured!'),
810 raise error.Abort(('default repository not configured!'),
811 hint=("see 'hg help config.paths'"))
811 hint=("see 'hg help config.paths'"))
812 dest = path.pushloc or path.loc
812 dest = path.pushloc or path.loc
813 branches = (path.branch, opts.get('branch') or [])
813 branches = (path.branch, opts.get('branch') or [])
814 ui.status(('analysing phase of %s\n') % util.hidepassword(dest))
814 ui.status(('analysing phase of %s\n') % util.hidepassword(dest))
815 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
815 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
816 other = hg.peer(repo, opts, dest)
816 other = hg.peer(repo, opts, dest)
817
817
818 # easier to perform discovery through the operation
818 # easier to perform discovery through the operation
819 op = exchange.pushoperation(repo, other)
819 op = exchange.pushoperation(repo, other)
820 exchange._pushdiscoverychangeset(op)
820 exchange._pushdiscoverychangeset(op)
821
821
822 remotesubset = op.fallbackheads
822 remotesubset = op.fallbackheads
823
823
824 with other.commandexecutor() as e:
824 with other.commandexecutor() as e:
825 remotephases = e.callcommand('listkeys',
825 remotephases = e.callcommand('listkeys',
826 {'namespace': 'phases'}).result()
826 {'namespace': 'phases'}).result()
827 del other
827 del other
828 publishing = remotephases.get('publishing', False)
828 publishing = remotephases.get('publishing', False)
829 if publishing:
829 if publishing:
830 ui.status(('publishing: yes\n'))
830 ui.status(('publishing: yes\n'))
831 else:
831 else:
832 ui.status(('publishing: no\n'))
832 ui.status(('publishing: no\n'))
833
833
834 nodemap = repo.changelog.nodemap
834 nodemap = repo.changelog.nodemap
835 nonpublishroots = 0
835 nonpublishroots = 0
836 for nhex, phase in remotephases.iteritems():
836 for nhex, phase in remotephases.iteritems():
837 if nhex == 'publishing': # ignore data related to publish option
837 if nhex == 'publishing': # ignore data related to publish option
838 continue
838 continue
839 node = bin(nhex)
839 node = bin(nhex)
840 if node in nodemap and int(phase):
840 if node in nodemap and int(phase):
841 nonpublishroots += 1
841 nonpublishroots += 1
842 ui.status(('number of roots: %d\n') % len(remotephases))
842 ui.status(('number of roots: %d\n') % len(remotephases))
843 ui.status(('number of known non public roots: %d\n') % nonpublishroots)
843 ui.status(('number of known non public roots: %d\n') % nonpublishroots)
844 def d():
844 def d():
845 phases.remotephasessummary(repo,
845 phases.remotephasessummary(repo,
846 remotesubset,
846 remotesubset,
847 remotephases)
847 remotephases)
848 timer(d)
848 timer(d)
849 fm.end()
849 fm.end()
850
850
851 @command('perfmanifest', [], 'REV')
851 @command('perfmanifest', [], 'REV')
852 def perfmanifest(ui, repo, rev, **opts):
852 def perfmanifest(ui, repo, rev, **opts):
853 """benchmark the time to read a manifest from disk and return a usable
853 """benchmark the time to read a manifest from disk and return a usable
854 dict-like object
854 dict-like object
855
855
856 Manifest caches are cleared before retrieval."""
856 Manifest caches are cleared before retrieval."""
857 timer, fm = gettimer(ui, opts)
857 timer, fm = gettimer(ui, opts)
858 ctx = scmutil.revsingle(repo, rev, rev)
858 ctx = scmutil.revsingle(repo, rev, rev)
859 t = ctx.manifestnode()
859 t = ctx.manifestnode()
860 def d():
860 def d():
861 repo.manifestlog.clearcaches()
861 repo.manifestlog.clearcaches()
862 repo.manifestlog[t].read()
862 repo.manifestlog[t].read()
863 timer(d)
863 timer(d)
864 fm.end()
864 fm.end()
865
865
866 @command('perfchangeset', formatteropts)
866 @command('perfchangeset', formatteropts)
867 def perfchangeset(ui, repo, rev, **opts):
867 def perfchangeset(ui, repo, rev, **opts):
868 timer, fm = gettimer(ui, opts)
868 timer, fm = gettimer(ui, opts)
869 n = scmutil.revsingle(repo, rev).node()
869 n = scmutil.revsingle(repo, rev).node()
870 def d():
870 def d():
871 repo.changelog.read(n)
871 repo.changelog.read(n)
872 #repo.changelog._cache = None
872 #repo.changelog._cache = None
873 timer(d)
873 timer(d)
874 fm.end()
874 fm.end()
875
875
876 @command('perfindex', formatteropts)
876 @command('perfindex', formatteropts)
877 def perfindex(ui, repo, **opts):
877 def perfindex(ui, repo, **opts):
878 import mercurial.revlog
878 import mercurial.revlog
879 timer, fm = gettimer(ui, opts)
879 timer, fm = gettimer(ui, opts)
880 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
880 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
881 n = repo["tip"].node()
881 n = repo["tip"].node()
882 svfs = getsvfs(repo)
882 svfs = getsvfs(repo)
883 def d():
883 def d():
884 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
884 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
885 cl.rev(n)
885 cl.rev(n)
886 timer(d)
886 timer(d)
887 fm.end()
887 fm.end()
888
888
889 @command('perfstartup', formatteropts)
889 @command('perfstartup', formatteropts)
890 def perfstartup(ui, repo, **opts):
890 def perfstartup(ui, repo, **opts):
891 timer, fm = gettimer(ui, opts)
891 timer, fm = gettimer(ui, opts)
892 cmd = sys.argv[0]
892 cmd = sys.argv[0]
893 def d():
893 def d():
894 if os.name != 'nt':
894 if os.name != 'nt':
895 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
895 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
896 else:
896 else:
897 os.environ['HGRCPATH'] = ' '
897 os.environ['HGRCPATH'] = ' '
898 os.system("%s version -q > NUL" % cmd)
898 os.system("%s version -q > NUL" % cmd)
899 timer(d)
899 timer(d)
900 fm.end()
900 fm.end()
901
901
902 @command('perfparents', formatteropts)
902 @command('perfparents', formatteropts)
903 def perfparents(ui, repo, **opts):
903 def perfparents(ui, repo, **opts):
904 timer, fm = gettimer(ui, opts)
904 timer, fm = gettimer(ui, opts)
905 # control the number of commits perfparents iterates over
905 # control the number of commits perfparents iterates over
906 # experimental config: perf.parentscount
906 # experimental config: perf.parentscount
907 count = getint(ui, "perf", "parentscount", 1000)
907 count = getint(ui, "perf", "parentscount", 1000)
908 if len(repo.changelog) < count:
908 if len(repo.changelog) < count:
909 raise error.Abort("repo needs %d commits for this test" % count)
909 raise error.Abort("repo needs %d commits for this test" % count)
910 repo = repo.unfiltered()
910 repo = repo.unfiltered()
911 nl = [repo.changelog.node(i) for i in xrange(count)]
911 nl = [repo.changelog.node(i) for i in xrange(count)]
912 def d():
912 def d():
913 for n in nl:
913 for n in nl:
914 repo.changelog.parents(n)
914 repo.changelog.parents(n)
915 timer(d)
915 timer(d)
916 fm.end()
916 fm.end()
917
917
918 @command('perfctxfiles', formatteropts)
918 @command('perfctxfiles', formatteropts)
919 def perfctxfiles(ui, repo, x, **opts):
919 def perfctxfiles(ui, repo, x, **opts):
920 x = int(x)
920 x = int(x)
921 timer, fm = gettimer(ui, opts)
921 timer, fm = gettimer(ui, opts)
922 def d():
922 def d():
923 len(repo[x].files())
923 len(repo[x].files())
924 timer(d)
924 timer(d)
925 fm.end()
925 fm.end()
926
926
927 @command('perfrawfiles', formatteropts)
927 @command('perfrawfiles', formatteropts)
928 def perfrawfiles(ui, repo, x, **opts):
928 def perfrawfiles(ui, repo, x, **opts):
929 x = int(x)
929 x = int(x)
930 timer, fm = gettimer(ui, opts)
930 timer, fm = gettimer(ui, opts)
931 cl = repo.changelog
931 cl = repo.changelog
932 def d():
932 def d():
933 len(cl.read(x)[3])
933 len(cl.read(x)[3])
934 timer(d)
934 timer(d)
935 fm.end()
935 fm.end()
936
936
937 @command('perflookup', formatteropts)
937 @command('perflookup', formatteropts)
938 def perflookup(ui, repo, rev, **opts):
938 def perflookup(ui, repo, rev, **opts):
939 timer, fm = gettimer(ui, opts)
939 timer, fm = gettimer(ui, opts)
940 timer(lambda: len(repo.lookup(rev)))
940 timer(lambda: len(repo.lookup(rev)))
941 fm.end()
941 fm.end()
942
942
943 @command('perfrevrange', formatteropts)
943 @command('perfrevrange', formatteropts)
944 def perfrevrange(ui, repo, *specs, **opts):
944 def perfrevrange(ui, repo, *specs, **opts):
945 timer, fm = gettimer(ui, opts)
945 timer, fm = gettimer(ui, opts)
946 revrange = scmutil.revrange
946 revrange = scmutil.revrange
947 timer(lambda: len(revrange(repo, specs)))
947 timer(lambda: len(revrange(repo, specs)))
948 fm.end()
948 fm.end()
949
949
950 @command('perfnodelookup', formatteropts)
950 @command('perfnodelookup', formatteropts)
951 def perfnodelookup(ui, repo, rev, **opts):
951 def perfnodelookup(ui, repo, rev, **opts):
952 timer, fm = gettimer(ui, opts)
952 timer, fm = gettimer(ui, opts)
953 import mercurial.revlog
953 import mercurial.revlog
954 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
954 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
955 n = scmutil.revsingle(repo, rev).node()
955 n = scmutil.revsingle(repo, rev).node()
956 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
956 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
957 def d():
957 def d():
958 cl.rev(n)
958 cl.rev(n)
959 clearcaches(cl)
959 clearcaches(cl)
960 timer(d)
960 timer(d)
961 fm.end()
961 fm.end()
962
962
963 @command('perflog',
963 @command('perflog',
964 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
964 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
965 def perflog(ui, repo, rev=None, **opts):
965 def perflog(ui, repo, rev=None, **opts):
966 if rev is None:
966 if rev is None:
967 rev=[]
967 rev=[]
968 timer, fm = gettimer(ui, opts)
968 timer, fm = gettimer(ui, opts)
969 ui.pushbuffer()
969 ui.pushbuffer()
970 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
970 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
971 copies=opts.get('rename')))
971 copies=opts.get('rename')))
972 ui.popbuffer()
972 ui.popbuffer()
973 fm.end()
973 fm.end()
974
974
975 @command('perfmoonwalk', formatteropts)
975 @command('perfmoonwalk', formatteropts)
976 def perfmoonwalk(ui, repo, **opts):
976 def perfmoonwalk(ui, repo, **opts):
977 """benchmark walking the changelog backwards
977 """benchmark walking the changelog backwards
978
978
979 This also loads the changelog data for each revision in the changelog.
979 This also loads the changelog data for each revision in the changelog.
980 """
980 """
981 timer, fm = gettimer(ui, opts)
981 timer, fm = gettimer(ui, opts)
982 def moonwalk():
982 def moonwalk():
983 for i in xrange(len(repo), -1, -1):
983 for i in xrange(len(repo), -1, -1):
984 ctx = repo[i]
984 ctx = repo[i]
985 ctx.branch() # read changelog data (in addition to the index)
985 ctx.branch() # read changelog data (in addition to the index)
986 timer(moonwalk)
986 timer(moonwalk)
987 fm.end()
987 fm.end()
988
988
989 @command('perftemplating',
989 @command('perftemplating',
990 [('r', 'rev', [], 'revisions to run the template on'),
990 [('r', 'rev', [], 'revisions to run the template on'),
991 ] + formatteropts)
991 ] + formatteropts)
992 def perftemplating(ui, repo, testedtemplate=None, **opts):
992 def perftemplating(ui, repo, testedtemplate=None, **opts):
993 """test the rendering time of a given template"""
993 """test the rendering time of a given template"""
994 if makelogtemplater is None:
994 if makelogtemplater is None:
995 raise error.Abort(("perftemplating not available with this Mercurial"),
995 raise error.Abort(("perftemplating not available with this Mercurial"),
996 hint="use 4.3 or later")
996 hint="use 4.3 or later")
997
997
998 nullui = ui.copy()
998 nullui = ui.copy()
999 nullui.fout = open(os.devnull, 'wb')
999 nullui.fout = open(os.devnull, 'wb')
1000 nullui.disablepager()
1000 nullui.disablepager()
1001 revs = opts.get('rev')
1001 revs = opts.get('rev')
1002 if not revs:
1002 if not revs:
1003 revs = ['all()']
1003 revs = ['all()']
1004 revs = list(scmutil.revrange(repo, revs))
1004 revs = list(scmutil.revrange(repo, revs))
1005
1005
1006 defaulttemplate = ('{date|shortdate} [{rev}:{node|short}]'
1006 defaulttemplate = ('{date|shortdate} [{rev}:{node|short}]'
1007 ' {author|person}: {desc|firstline}\n')
1007 ' {author|person}: {desc|firstline}\n')
1008 if testedtemplate is None:
1008 if testedtemplate is None:
1009 testedtemplate = defaulttemplate
1009 testedtemplate = defaulttemplate
1010 displayer = makelogtemplater(nullui, repo, testedtemplate)
1010 displayer = makelogtemplater(nullui, repo, testedtemplate)
1011 def format():
1011 def format():
1012 for r in revs:
1012 for r in revs:
1013 ctx = repo[r]
1013 ctx = repo[r]
1014 displayer.show(ctx)
1014 displayer.show(ctx)
1015 displayer.flush(ctx)
1015 displayer.flush(ctx)
1016
1016
1017 timer, fm = gettimer(ui, opts)
1017 timer, fm = gettimer(ui, opts)
1018 timer(format)
1018 timer(format)
1019 fm.end()
1019 fm.end()
1020
1020
1021 @command('perfcca', formatteropts)
1021 @command('perfcca', formatteropts)
1022 def perfcca(ui, repo, **opts):
1022 def perfcca(ui, repo, **opts):
1023 timer, fm = gettimer(ui, opts)
1023 timer, fm = gettimer(ui, opts)
1024 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
1024 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
1025 fm.end()
1025 fm.end()
1026
1026
1027 @command('perffncacheload', formatteropts)
1027 @command('perffncacheload', formatteropts)
1028 def perffncacheload(ui, repo, **opts):
1028 def perffncacheload(ui, repo, **opts):
1029 timer, fm = gettimer(ui, opts)
1029 timer, fm = gettimer(ui, opts)
1030 s = repo.store
1030 s = repo.store
1031 def d():
1031 def d():
1032 s.fncache._load()
1032 s.fncache._load()
1033 timer(d)
1033 timer(d)
1034 fm.end()
1034 fm.end()
1035
1035
1036 @command('perffncachewrite', formatteropts)
1036 @command('perffncachewrite', formatteropts)
1037 def perffncachewrite(ui, repo, **opts):
1037 def perffncachewrite(ui, repo, **opts):
1038 timer, fm = gettimer(ui, opts)
1038 timer, fm = gettimer(ui, opts)
1039 s = repo.store
1039 s = repo.store
1040 lock = repo.lock()
1040 lock = repo.lock()
1041 s.fncache._load()
1041 s.fncache._load()
1042 tr = repo.transaction('perffncachewrite')
1042 tr = repo.transaction('perffncachewrite')
1043 tr.addbackup('fncache')
1043 tr.addbackup('fncache')
1044 def d():
1044 def d():
1045 s.fncache._dirty = True
1045 s.fncache._dirty = True
1046 s.fncache.write(tr)
1046 s.fncache.write(tr)
1047 timer(d)
1047 timer(d)
1048 tr.close()
1048 tr.close()
1049 lock.release()
1049 lock.release()
1050 fm.end()
1050 fm.end()
1051
1051
1052 @command('perffncacheencode', formatteropts)
1052 @command('perffncacheencode', formatteropts)
1053 def perffncacheencode(ui, repo, **opts):
1053 def perffncacheencode(ui, repo, **opts):
1054 timer, fm = gettimer(ui, opts)
1054 timer, fm = gettimer(ui, opts)
1055 s = repo.store
1055 s = repo.store
1056 s.fncache._load()
1056 s.fncache._load()
1057 def d():
1057 def d():
1058 for p in s.fncache.entries:
1058 for p in s.fncache.entries:
1059 s.encode(p)
1059 s.encode(p)
1060 timer(d)
1060 timer(d)
1061 fm.end()
1061 fm.end()
1062
1062
1063 def _bdiffworker(q, blocks, xdiff, ready, done):
1063 def _bdiffworker(q, blocks, xdiff, ready, done):
1064 while not done.is_set():
1064 while not done.is_set():
1065 pair = q.get()
1065 pair = q.get()
1066 while pair is not None:
1066 while pair is not None:
1067 if xdiff:
1067 if xdiff:
1068 mdiff.bdiff.xdiffblocks(*pair)
1068 mdiff.bdiff.xdiffblocks(*pair)
1069 elif blocks:
1069 elif blocks:
1070 mdiff.bdiff.blocks(*pair)
1070 mdiff.bdiff.blocks(*pair)
1071 else:
1071 else:
1072 mdiff.textdiff(*pair)
1072 mdiff.textdiff(*pair)
1073 q.task_done()
1073 q.task_done()
1074 pair = q.get()
1074 pair = q.get()
1075 q.task_done() # for the None one
1075 q.task_done() # for the None one
1076 with ready:
1076 with ready:
1077 ready.wait()
1077 ready.wait()
1078
1078
1079 @command('perfbdiff', revlogopts + formatteropts + [
1079 @command('perfbdiff', revlogopts + formatteropts + [
1080 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1080 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1081 ('', 'alldata', False, 'test bdiffs for all associated revisions'),
1081 ('', 'alldata', False, 'test bdiffs for all associated revisions'),
1082 ('', 'threads', 0, 'number of thread to use (disable with 0)'),
1082 ('', 'threads', 0, 'number of thread to use (disable with 0)'),
1083 ('', 'blocks', False, 'test computing diffs into blocks'),
1083 ('', 'blocks', False, 'test computing diffs into blocks'),
1084 ('', 'xdiff', False, 'use xdiff algorithm'),
1084 ('', 'xdiff', False, 'use xdiff algorithm'),
1085 ],
1085 ],
1086
1086
1087 '-c|-m|FILE REV')
1087 '-c|-m|FILE REV')
1088 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1088 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1089 """benchmark a bdiff between revisions
1089 """benchmark a bdiff between revisions
1090
1090
1091 By default, benchmark a bdiff between its delta parent and itself.
1091 By default, benchmark a bdiff between its delta parent and itself.
1092
1092
1093 With ``--count``, benchmark bdiffs between delta parents and self for N
1093 With ``--count``, benchmark bdiffs between delta parents and self for N
1094 revisions starting at the specified revision.
1094 revisions starting at the specified revision.
1095
1095
1096 With ``--alldata``, assume the requested revision is a changeset and
1096 With ``--alldata``, assume the requested revision is a changeset and
1097 measure bdiffs for all changes related to that changeset (manifest
1097 measure bdiffs for all changes related to that changeset (manifest
1098 and filelogs).
1098 and filelogs).
1099 """
1099 """
1100 opts = pycompat.byteskwargs(opts)
1100 opts = pycompat.byteskwargs(opts)
1101
1101
1102 if opts['xdiff'] and not opts['blocks']:
1102 if opts['xdiff'] and not opts['blocks']:
1103 raise error.CommandError('perfbdiff', '--xdiff requires --blocks')
1103 raise error.CommandError('perfbdiff', '--xdiff requires --blocks')
1104
1104
1105 if opts['alldata']:
1105 if opts['alldata']:
1106 opts['changelog'] = True
1106 opts['changelog'] = True
1107
1107
1108 if opts.get('changelog') or opts.get('manifest'):
1108 if opts.get('changelog') or opts.get('manifest'):
1109 file_, rev = None, file_
1109 file_, rev = None, file_
1110 elif rev is None:
1110 elif rev is None:
1111 raise error.CommandError('perfbdiff', 'invalid arguments')
1111 raise error.CommandError('perfbdiff', 'invalid arguments')
1112
1112
1113 blocks = opts['blocks']
1113 blocks = opts['blocks']
1114 xdiff = opts['xdiff']
1114 xdiff = opts['xdiff']
1115 textpairs = []
1115 textpairs = []
1116
1116
1117 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
1117 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
1118
1118
1119 startrev = r.rev(r.lookup(rev))
1119 startrev = r.rev(r.lookup(rev))
1120 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1120 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1121 if opts['alldata']:
1121 if opts['alldata']:
1122 # Load revisions associated with changeset.
1122 # Load revisions associated with changeset.
1123 ctx = repo[rev]
1123 ctx = repo[rev]
1124 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1124 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1125 for pctx in ctx.parents():
1125 for pctx in ctx.parents():
1126 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1126 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1127 textpairs.append((pman, mtext))
1127 textpairs.append((pman, mtext))
1128
1128
1129 # Load filelog revisions by iterating manifest delta.
1129 # Load filelog revisions by iterating manifest delta.
1130 man = ctx.manifest()
1130 man = ctx.manifest()
1131 pman = ctx.p1().manifest()
1131 pman = ctx.p1().manifest()
1132 for filename, change in pman.diff(man).items():
1132 for filename, change in pman.diff(man).items():
1133 fctx = repo.file(filename)
1133 fctx = repo.file(filename)
1134 f1 = fctx.revision(change[0][0] or -1)
1134 f1 = fctx.revision(change[0][0] or -1)
1135 f2 = fctx.revision(change[1][0] or -1)
1135 f2 = fctx.revision(change[1][0] or -1)
1136 textpairs.append((f1, f2))
1136 textpairs.append((f1, f2))
1137 else:
1137 else:
1138 dp = r.deltaparent(rev)
1138 dp = r.deltaparent(rev)
1139 textpairs.append((r.revision(dp), r.revision(rev)))
1139 textpairs.append((r.revision(dp), r.revision(rev)))
1140
1140
1141 withthreads = threads > 0
1141 withthreads = threads > 0
1142 if not withthreads:
1142 if not withthreads:
1143 def d():
1143 def d():
1144 for pair in textpairs:
1144 for pair in textpairs:
1145 if xdiff:
1145 if xdiff:
1146 mdiff.bdiff.xdiffblocks(*pair)
1146 mdiff.bdiff.xdiffblocks(*pair)
1147 elif blocks:
1147 elif blocks:
1148 mdiff.bdiff.blocks(*pair)
1148 mdiff.bdiff.blocks(*pair)
1149 else:
1149 else:
1150 mdiff.textdiff(*pair)
1150 mdiff.textdiff(*pair)
1151 else:
1151 else:
1152 q = queue()
1152 q = queue()
1153 for i in xrange(threads):
1153 for i in xrange(threads):
1154 q.put(None)
1154 q.put(None)
1155 ready = threading.Condition()
1155 ready = threading.Condition()
1156 done = threading.Event()
1156 done = threading.Event()
1157 for i in xrange(threads):
1157 for i in xrange(threads):
1158 threading.Thread(target=_bdiffworker,
1158 threading.Thread(target=_bdiffworker,
1159 args=(q, blocks, xdiff, ready, done)).start()
1159 args=(q, blocks, xdiff, ready, done)).start()
1160 q.join()
1160 q.join()
1161 def d():
1161 def d():
1162 for pair in textpairs:
1162 for pair in textpairs:
1163 q.put(pair)
1163 q.put(pair)
1164 for i in xrange(threads):
1164 for i in xrange(threads):
1165 q.put(None)
1165 q.put(None)
1166 with ready:
1166 with ready:
1167 ready.notify_all()
1167 ready.notify_all()
1168 q.join()
1168 q.join()
1169 timer, fm = gettimer(ui, opts)
1169 timer, fm = gettimer(ui, opts)
1170 timer(d)
1170 timer(d)
1171 fm.end()
1171 fm.end()
1172
1172
1173 if withthreads:
1173 if withthreads:
1174 done.set()
1174 done.set()
1175 for i in xrange(threads):
1175 for i in xrange(threads):
1176 q.put(None)
1176 q.put(None)
1177 with ready:
1177 with ready:
1178 ready.notify_all()
1178 ready.notify_all()
1179
1179
1180 @command('perfunidiff', revlogopts + formatteropts + [
1180 @command('perfunidiff', revlogopts + formatteropts + [
1181 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1181 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1182 ('', 'alldata', False, 'test unidiffs for all associated revisions'),
1182 ('', 'alldata', False, 'test unidiffs for all associated revisions'),
1183 ], '-c|-m|FILE REV')
1183 ], '-c|-m|FILE REV')
1184 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1184 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1185 """benchmark a unified diff between revisions
1185 """benchmark a unified diff between revisions
1186
1186
1187 This doesn't include any copy tracing - it's just a unified diff
1187 This doesn't include any copy tracing - it's just a unified diff
1188 of the texts.
1188 of the texts.
1189
1189
1190 By default, benchmark a diff between its delta parent and itself.
1190 By default, benchmark a diff between its delta parent and itself.
1191
1191
1192 With ``--count``, benchmark diffs between delta parents and self for N
1192 With ``--count``, benchmark diffs between delta parents and self for N
1193 revisions starting at the specified revision.
1193 revisions starting at the specified revision.
1194
1194
1195 With ``--alldata``, assume the requested revision is a changeset and
1195 With ``--alldata``, assume the requested revision is a changeset and
1196 measure diffs for all changes related to that changeset (manifest
1196 measure diffs for all changes related to that changeset (manifest
1197 and filelogs).
1197 and filelogs).
1198 """
1198 """
1199 if opts['alldata']:
1199 if opts['alldata']:
1200 opts['changelog'] = True
1200 opts['changelog'] = True
1201
1201
1202 if opts.get('changelog') or opts.get('manifest'):
1202 if opts.get('changelog') or opts.get('manifest'):
1203 file_, rev = None, file_
1203 file_, rev = None, file_
1204 elif rev is None:
1204 elif rev is None:
1205 raise error.CommandError('perfunidiff', 'invalid arguments')
1205 raise error.CommandError('perfunidiff', 'invalid arguments')
1206
1206
1207 textpairs = []
1207 textpairs = []
1208
1208
1209 r = cmdutil.openrevlog(repo, 'perfunidiff', file_, opts)
1209 r = cmdutil.openrevlog(repo, 'perfunidiff', file_, opts)
1210
1210
1211 startrev = r.rev(r.lookup(rev))
1211 startrev = r.rev(r.lookup(rev))
1212 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1212 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1213 if opts['alldata']:
1213 if opts['alldata']:
1214 # Load revisions associated with changeset.
1214 # Load revisions associated with changeset.
1215 ctx = repo[rev]
1215 ctx = repo[rev]
1216 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1216 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1217 for pctx in ctx.parents():
1217 for pctx in ctx.parents():
1218 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1218 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1219 textpairs.append((pman, mtext))
1219 textpairs.append((pman, mtext))
1220
1220
1221 # Load filelog revisions by iterating manifest delta.
1221 # Load filelog revisions by iterating manifest delta.
1222 man = ctx.manifest()
1222 man = ctx.manifest()
1223 pman = ctx.p1().manifest()
1223 pman = ctx.p1().manifest()
1224 for filename, change in pman.diff(man).items():
1224 for filename, change in pman.diff(man).items():
1225 fctx = repo.file(filename)
1225 fctx = repo.file(filename)
1226 f1 = fctx.revision(change[0][0] or -1)
1226 f1 = fctx.revision(change[0][0] or -1)
1227 f2 = fctx.revision(change[1][0] or -1)
1227 f2 = fctx.revision(change[1][0] or -1)
1228 textpairs.append((f1, f2))
1228 textpairs.append((f1, f2))
1229 else:
1229 else:
1230 dp = r.deltaparent(rev)
1230 dp = r.deltaparent(rev)
1231 textpairs.append((r.revision(dp), r.revision(rev)))
1231 textpairs.append((r.revision(dp), r.revision(rev)))
1232
1232
1233 def d():
1233 def d():
1234 for left, right in textpairs:
1234 for left, right in textpairs:
1235 # The date strings don't matter, so we pass empty strings.
1235 # The date strings don't matter, so we pass empty strings.
1236 headerlines, hunks = mdiff.unidiff(
1236 headerlines, hunks = mdiff.unidiff(
1237 left, '', right, '', 'left', 'right', binary=False)
1237 left, '', right, '', 'left', 'right', binary=False)
1238 # consume iterators in roughly the way patch.py does
1238 # consume iterators in roughly the way patch.py does
1239 b'\n'.join(headerlines)
1239 b'\n'.join(headerlines)
1240 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1240 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1241 timer, fm = gettimer(ui, opts)
1241 timer, fm = gettimer(ui, opts)
1242 timer(d)
1242 timer(d)
1243 fm.end()
1243 fm.end()
1244
1244
1245 @command('perfdiffwd', formatteropts)
1245 @command('perfdiffwd', formatteropts)
1246 def perfdiffwd(ui, repo, **opts):
1246 def perfdiffwd(ui, repo, **opts):
1247 """Profile diff of working directory changes"""
1247 """Profile diff of working directory changes"""
1248 timer, fm = gettimer(ui, opts)
1248 timer, fm = gettimer(ui, opts)
1249 options = {
1249 options = {
1250 'w': 'ignore_all_space',
1250 'w': 'ignore_all_space',
1251 'b': 'ignore_space_change',
1251 'b': 'ignore_space_change',
1252 'B': 'ignore_blank_lines',
1252 'B': 'ignore_blank_lines',
1253 }
1253 }
1254
1254
1255 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1255 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1256 opts = dict((options[c], '1') for c in diffopt)
1256 opts = dict((options[c], '1') for c in diffopt)
1257 def d():
1257 def d():
1258 ui.pushbuffer()
1258 ui.pushbuffer()
1259 commands.diff(ui, repo, **opts)
1259 commands.diff(ui, repo, **opts)
1260 ui.popbuffer()
1260 ui.popbuffer()
1261 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
1261 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
1262 timer(d, title)
1262 timer(d, title)
1263 fm.end()
1263 fm.end()
1264
1264
1265 @command('perfrevlogindex', revlogopts + formatteropts,
1265 @command('perfrevlogindex', revlogopts + formatteropts,
1266 '-c|-m|FILE')
1266 '-c|-m|FILE')
1267 def perfrevlogindex(ui, repo, file_=None, **opts):
1267 def perfrevlogindex(ui, repo, file_=None, **opts):
1268 """Benchmark operations against a revlog index.
1268 """Benchmark operations against a revlog index.
1269
1269
1270 This tests constructing a revlog instance, reading index data,
1270 This tests constructing a revlog instance, reading index data,
1271 parsing index data, and performing various operations related to
1271 parsing index data, and performing various operations related to
1272 index data.
1272 index data.
1273 """
1273 """
1274
1274
1275 rl = cmdutil.openrevlog(repo, 'perfrevlogindex', file_, opts)
1275 rl = cmdutil.openrevlog(repo, 'perfrevlogindex', file_, opts)
1276
1276
1277 opener = getattr(rl, 'opener') # trick linter
1277 opener = getattr(rl, 'opener') # trick linter
1278 indexfile = rl.indexfile
1278 indexfile = rl.indexfile
1279 data = opener.read(indexfile)
1279 data = opener.read(indexfile)
1280
1280
1281 header = struct.unpack('>I', data[0:4])[0]
1281 header = struct.unpack('>I', data[0:4])[0]
1282 version = header & 0xFFFF
1282 version = header & 0xFFFF
1283 if version == 1:
1283 if version == 1:
1284 revlogio = revlog.revlogio()
1284 revlogio = revlog.revlogio()
1285 inline = header & (1 << 16)
1285 inline = header & (1 << 16)
1286 else:
1286 else:
1287 raise error.Abort(('unsupported revlog version: %d') % version)
1287 raise error.Abort(('unsupported revlog version: %d') % version)
1288
1288
1289 rllen = len(rl)
1289 rllen = len(rl)
1290
1290
1291 node0 = rl.node(0)
1291 node0 = rl.node(0)
1292 node25 = rl.node(rllen // 4)
1292 node25 = rl.node(rllen // 4)
1293 node50 = rl.node(rllen // 2)
1293 node50 = rl.node(rllen // 2)
1294 node75 = rl.node(rllen // 4 * 3)
1294 node75 = rl.node(rllen // 4 * 3)
1295 node100 = rl.node(rllen - 1)
1295 node100 = rl.node(rllen - 1)
1296
1296
1297 allrevs = range(rllen)
1297 allrevs = range(rllen)
1298 allrevsrev = list(reversed(allrevs))
1298 allrevsrev = list(reversed(allrevs))
1299 allnodes = [rl.node(rev) for rev in range(rllen)]
1299 allnodes = [rl.node(rev) for rev in range(rllen)]
1300 allnodesrev = list(reversed(allnodes))
1300 allnodesrev = list(reversed(allnodes))
1301
1301
1302 def constructor():
1302 def constructor():
1303 revlog.revlog(opener, indexfile)
1303 revlog.revlog(opener, indexfile)
1304
1304
1305 def read():
1305 def read():
1306 with opener(indexfile) as fh:
1306 with opener(indexfile) as fh:
1307 fh.read()
1307 fh.read()
1308
1308
1309 def parseindex():
1309 def parseindex():
1310 revlogio.parseindex(data, inline)
1310 revlogio.parseindex(data, inline)
1311
1311
1312 def getentry(revornode):
1312 def getentry(revornode):
1313 index = revlogio.parseindex(data, inline)[0]
1313 index = revlogio.parseindex(data, inline)[0]
1314 index[revornode]
1314 index[revornode]
1315
1315
1316 def getentries(revs, count=1):
1316 def getentries(revs, count=1):
1317 index = revlogio.parseindex(data, inline)[0]
1317 index = revlogio.parseindex(data, inline)[0]
1318
1318
1319 for i in range(count):
1319 for i in range(count):
1320 for rev in revs:
1320 for rev in revs:
1321 index[rev]
1321 index[rev]
1322
1322
1323 def resolvenode(node):
1323 def resolvenode(node):
1324 nodemap = revlogio.parseindex(data, inline)[1]
1324 nodemap = revlogio.parseindex(data, inline)[1]
1325 # This only works for the C code.
1325 # This only works for the C code.
1326 if nodemap is None:
1326 if nodemap is None:
1327 return
1327 return
1328
1328
1329 try:
1329 try:
1330 nodemap[node]
1330 nodemap[node]
1331 except error.RevlogError:
1331 except error.RevlogError:
1332 pass
1332 pass
1333
1333
1334 def resolvenodes(nodes, count=1):
1334 def resolvenodes(nodes, count=1):
1335 nodemap = revlogio.parseindex(data, inline)[1]
1335 nodemap = revlogio.parseindex(data, inline)[1]
1336 if nodemap is None:
1336 if nodemap is None:
1337 return
1337 return
1338
1338
1339 for i in range(count):
1339 for i in range(count):
1340 for node in nodes:
1340 for node in nodes:
1341 try:
1341 try:
1342 nodemap[node]
1342 nodemap[node]
1343 except error.RevlogError:
1343 except error.RevlogError:
1344 pass
1344 pass
1345
1345
1346 benches = [
1346 benches = [
1347 (constructor, 'revlog constructor'),
1347 (constructor, 'revlog constructor'),
1348 (read, 'read'),
1348 (read, 'read'),
1349 (parseindex, 'create index object'),
1349 (parseindex, 'create index object'),
1350 (lambda: getentry(0), 'retrieve index entry for rev 0'),
1350 (lambda: getentry(0), 'retrieve index entry for rev 0'),
1351 (lambda: resolvenode('a' * 20), 'look up missing node'),
1351 (lambda: resolvenode('a' * 20), 'look up missing node'),
1352 (lambda: resolvenode(node0), 'look up node at rev 0'),
1352 (lambda: resolvenode(node0), 'look up node at rev 0'),
1353 (lambda: resolvenode(node25), 'look up node at 1/4 len'),
1353 (lambda: resolvenode(node25), 'look up node at 1/4 len'),
1354 (lambda: resolvenode(node50), 'look up node at 1/2 len'),
1354 (lambda: resolvenode(node50), 'look up node at 1/2 len'),
1355 (lambda: resolvenode(node75), 'look up node at 3/4 len'),
1355 (lambda: resolvenode(node75), 'look up node at 3/4 len'),
1356 (lambda: resolvenode(node100), 'look up node at tip'),
1356 (lambda: resolvenode(node100), 'look up node at tip'),
1357 # 2x variation is to measure caching impact.
1357 # 2x variation is to measure caching impact.
1358 (lambda: resolvenodes(allnodes),
1358 (lambda: resolvenodes(allnodes),
1359 'look up all nodes (forward)'),
1359 'look up all nodes (forward)'),
1360 (lambda: resolvenodes(allnodes, 2),
1360 (lambda: resolvenodes(allnodes, 2),
1361 'look up all nodes 2x (forward)'),
1361 'look up all nodes 2x (forward)'),
1362 (lambda: resolvenodes(allnodesrev),
1362 (lambda: resolvenodes(allnodesrev),
1363 'look up all nodes (reverse)'),
1363 'look up all nodes (reverse)'),
1364 (lambda: resolvenodes(allnodesrev, 2),
1364 (lambda: resolvenodes(allnodesrev, 2),
1365 'look up all nodes 2x (reverse)'),
1365 'look up all nodes 2x (reverse)'),
1366 (lambda: getentries(allrevs),
1366 (lambda: getentries(allrevs),
1367 'retrieve all index entries (forward)'),
1367 'retrieve all index entries (forward)'),
1368 (lambda: getentries(allrevs, 2),
1368 (lambda: getentries(allrevs, 2),
1369 'retrieve all index entries 2x (forward)'),
1369 'retrieve all index entries 2x (forward)'),
1370 (lambda: getentries(allrevsrev),
1370 (lambda: getentries(allrevsrev),
1371 'retrieve all index entries (reverse)'),
1371 'retrieve all index entries (reverse)'),
1372 (lambda: getentries(allrevsrev, 2),
1372 (lambda: getentries(allrevsrev, 2),
1373 'retrieve all index entries 2x (reverse)'),
1373 'retrieve all index entries 2x (reverse)'),
1374 ]
1374 ]
1375
1375
1376 for fn, title in benches:
1376 for fn, title in benches:
1377 timer, fm = gettimer(ui, opts)
1377 timer, fm = gettimer(ui, opts)
1378 timer(fn, title=title)
1378 timer(fn, title=title)
1379 fm.end()
1379 fm.end()
1380
1380
1381 @command('perfrevlogrevisions', revlogopts + formatteropts +
1381 @command('perfrevlogrevisions', revlogopts + formatteropts +
1382 [('d', 'dist', 100, 'distance between the revisions'),
1382 [('d', 'dist', 100, 'distance between the revisions'),
1383 ('s', 'startrev', 0, 'revision to start reading at'),
1383 ('s', 'startrev', 0, 'revision to start reading at'),
1384 ('', 'reverse', False, 'read in reverse')],
1384 ('', 'reverse', False, 'read in reverse')],
1385 '-c|-m|FILE')
1385 '-c|-m|FILE')
1386 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1386 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1387 **opts):
1387 **opts):
1388 """Benchmark reading a series of revisions from a revlog.
1388 """Benchmark reading a series of revisions from a revlog.
1389
1389
1390 By default, we read every ``-d/--dist`` revision from 0 to tip of
1390 By default, we read every ``-d/--dist`` revision from 0 to tip of
1391 the specified revlog.
1391 the specified revlog.
1392
1392
1393 The start revision can be defined via ``-s/--startrev``.
1393 The start revision can be defined via ``-s/--startrev``.
1394 """
1394 """
1395 rl = cmdutil.openrevlog(repo, 'perfrevlogrevisions', file_, opts)
1395 rl = cmdutil.openrevlog(repo, 'perfrevlogrevisions', file_, opts)
1396 rllen = getlen(ui)(rl)
1396 rllen = getlen(ui)(rl)
1397
1397
1398 def d():
1398 def d():
1399 rl.clearcaches()
1399 rl.clearcaches()
1400
1400
1401 beginrev = startrev
1401 beginrev = startrev
1402 endrev = rllen
1402 endrev = rllen
1403 dist = opts['dist']
1403 dist = opts['dist']
1404
1404
1405 if reverse:
1405 if reverse:
1406 beginrev, endrev = endrev, beginrev
1406 beginrev, endrev = endrev, beginrev
1407 dist = -1 * dist
1407 dist = -1 * dist
1408
1408
1409 for x in xrange(beginrev, endrev, dist):
1409 for x in xrange(beginrev, endrev, dist):
1410 # Old revisions don't support passing int.
1410 # Old revisions don't support passing int.
1411 n = rl.node(x)
1411 n = rl.node(x)
1412 rl.revision(n)
1412 rl.revision(n)
1413
1413
1414 timer, fm = gettimer(ui, opts)
1414 timer, fm = gettimer(ui, opts)
1415 timer(d)
1415 timer(d)
1416 fm.end()
1416 fm.end()
1417
1417
1418 @command('perfrevlogchunks', revlogopts + formatteropts +
1418 @command('perfrevlogchunks', revlogopts + formatteropts +
1419 [('e', 'engines', '', 'compression engines to use'),
1419 [('e', 'engines', '', 'compression engines to use'),
1420 ('s', 'startrev', 0, 'revision to start at')],
1420 ('s', 'startrev', 0, 'revision to start at')],
1421 '-c|-m|FILE')
1421 '-c|-m|FILE')
1422 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1422 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1423 """Benchmark operations on revlog chunks.
1423 """Benchmark operations on revlog chunks.
1424
1424
1425 Logically, each revlog is a collection of fulltext revisions. However,
1425 Logically, each revlog is a collection of fulltext revisions. However,
1426 stored within each revlog are "chunks" of possibly compressed data. This
1426 stored within each revlog are "chunks" of possibly compressed data. This
1427 data needs to be read and decompressed or compressed and written.
1427 data needs to be read and decompressed or compressed and written.
1428
1428
1429 This command measures the time it takes to read+decompress and recompress
1429 This command measures the time it takes to read+decompress and recompress
1430 chunks in a revlog. It effectively isolates I/O and compression performance.
1430 chunks in a revlog. It effectively isolates I/O and compression performance.
1431 For measurements of higher-level operations like resolving revisions,
1431 For measurements of higher-level operations like resolving revisions,
1432 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1432 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1433 """
1433 """
1434 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
1434 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
1435
1435
1436 # _chunkraw was renamed to _getsegmentforrevs.
1436 # _chunkraw was renamed to _getsegmentforrevs.
1437 try:
1437 try:
1438 segmentforrevs = rl._getsegmentforrevs
1438 segmentforrevs = rl._getsegmentforrevs
1439 except AttributeError:
1439 except AttributeError:
1440 segmentforrevs = rl._chunkraw
1440 segmentforrevs = rl._chunkraw
1441
1441
1442 # Verify engines argument.
1442 # Verify engines argument.
1443 if engines:
1443 if engines:
1444 engines = set(e.strip() for e in engines.split(','))
1444 engines = set(e.strip() for e in engines.split(','))
1445 for engine in engines:
1445 for engine in engines:
1446 try:
1446 try:
1447 util.compressionengines[engine]
1447 util.compressionengines[engine]
1448 except KeyError:
1448 except KeyError:
1449 raise error.Abort('unknown compression engine: %s' % engine)
1449 raise error.Abort('unknown compression engine: %s' % engine)
1450 else:
1450 else:
1451 engines = []
1451 engines = []
1452 for e in util.compengines:
1452 for e in util.compengines:
1453 engine = util.compengines[e]
1453 engine = util.compengines[e]
1454 try:
1454 try:
1455 if engine.available():
1455 if engine.available():
1456 engine.revlogcompressor().compress('dummy')
1456 engine.revlogcompressor().compress('dummy')
1457 engines.append(e)
1457 engines.append(e)
1458 except NotImplementedError:
1458 except NotImplementedError:
1459 pass
1459 pass
1460
1460
1461 revs = list(rl.revs(startrev, len(rl) - 1))
1461 revs = list(rl.revs(startrev, len(rl) - 1))
1462
1462
1463 def rlfh(rl):
1463 def rlfh(rl):
1464 if rl._inline:
1464 if rl._inline:
1465 return getsvfs(repo)(rl.indexfile)
1465 return getsvfs(repo)(rl.indexfile)
1466 else:
1466 else:
1467 return getsvfs(repo)(rl.datafile)
1467 return getsvfs(repo)(rl.datafile)
1468
1468
1469 def doread():
1469 def doread():
1470 rl.clearcaches()
1470 rl.clearcaches()
1471 for rev in revs:
1471 for rev in revs:
1472 segmentforrevs(rev, rev)
1472 segmentforrevs(rev, rev)
1473
1473
1474 def doreadcachedfh():
1474 def doreadcachedfh():
1475 rl.clearcaches()
1475 rl.clearcaches()
1476 fh = rlfh(rl)
1476 fh = rlfh(rl)
1477 for rev in revs:
1477 for rev in revs:
1478 segmentforrevs(rev, rev, df=fh)
1478 segmentforrevs(rev, rev, df=fh)
1479
1479
1480 def doreadbatch():
1480 def doreadbatch():
1481 rl.clearcaches()
1481 rl.clearcaches()
1482 segmentforrevs(revs[0], revs[-1])
1482 segmentforrevs(revs[0], revs[-1])
1483
1483
1484 def doreadbatchcachedfh():
1484 def doreadbatchcachedfh():
1485 rl.clearcaches()
1485 rl.clearcaches()
1486 fh = rlfh(rl)
1486 fh = rlfh(rl)
1487 segmentforrevs(revs[0], revs[-1], df=fh)
1487 segmentforrevs(revs[0], revs[-1], df=fh)
1488
1488
1489 def dochunk():
1489 def dochunk():
1490 rl.clearcaches()
1490 rl.clearcaches()
1491 fh = rlfh(rl)
1491 fh = rlfh(rl)
1492 for rev in revs:
1492 for rev in revs:
1493 rl._chunk(rev, df=fh)
1493 rl._chunk(rev, df=fh)
1494
1494
1495 chunks = [None]
1495 chunks = [None]
1496
1496
1497 def dochunkbatch():
1497 def dochunkbatch():
1498 rl.clearcaches()
1498 rl.clearcaches()
1499 fh = rlfh(rl)
1499 fh = rlfh(rl)
1500 # Save chunks as a side-effect.
1500 # Save chunks as a side-effect.
1501 chunks[0] = rl._chunks(revs, df=fh)
1501 chunks[0] = rl._chunks(revs, df=fh)
1502
1502
1503 def docompress(compressor):
1503 def docompress(compressor):
1504 rl.clearcaches()
1504 rl.clearcaches()
1505
1505
1506 try:
1506 try:
1507 # Swap in the requested compression engine.
1507 # Swap in the requested compression engine.
1508 oldcompressor = rl._compressor
1508 oldcompressor = rl._compressor
1509 rl._compressor = compressor
1509 rl._compressor = compressor
1510 for chunk in chunks[0]:
1510 for chunk in chunks[0]:
1511 rl.compress(chunk)
1511 rl.compress(chunk)
1512 finally:
1512 finally:
1513 rl._compressor = oldcompressor
1513 rl._compressor = oldcompressor
1514
1514
1515 benches = [
1515 benches = [
1516 (lambda: doread(), 'read'),
1516 (lambda: doread(), 'read'),
1517 (lambda: doreadcachedfh(), 'read w/ reused fd'),
1517 (lambda: doreadcachedfh(), 'read w/ reused fd'),
1518 (lambda: doreadbatch(), 'read batch'),
1518 (lambda: doreadbatch(), 'read batch'),
1519 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
1519 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
1520 (lambda: dochunk(), 'chunk'),
1520 (lambda: dochunk(), 'chunk'),
1521 (lambda: dochunkbatch(), 'chunk batch'),
1521 (lambda: dochunkbatch(), 'chunk batch'),
1522 ]
1522 ]
1523
1523
1524 for engine in sorted(engines):
1524 for engine in sorted(engines):
1525 compressor = util.compengines[engine].revlogcompressor()
1525 compressor = util.compengines[engine].revlogcompressor()
1526 benches.append((functools.partial(docompress, compressor),
1526 benches.append((functools.partial(docompress, compressor),
1527 'compress w/ %s' % engine))
1527 'compress w/ %s' % engine))
1528
1528
1529 for fn, title in benches:
1529 for fn, title in benches:
1530 timer, fm = gettimer(ui, opts)
1530 timer, fm = gettimer(ui, opts)
1531 timer(fn, title=title)
1531 timer(fn, title=title)
1532 fm.end()
1532 fm.end()
1533
1533
1534 @command('perfrevlogrevision', revlogopts + formatteropts +
1534 @command('perfrevlogrevision', revlogopts + formatteropts +
1535 [('', 'cache', False, 'use caches instead of clearing')],
1535 [('', 'cache', False, 'use caches instead of clearing')],
1536 '-c|-m|FILE REV')
1536 '-c|-m|FILE REV')
1537 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1537 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1538 """Benchmark obtaining a revlog revision.
1538 """Benchmark obtaining a revlog revision.
1539
1539
1540 Obtaining a revlog revision consists of roughly the following steps:
1540 Obtaining a revlog revision consists of roughly the following steps:
1541
1541
1542 1. Compute the delta chain
1542 1. Compute the delta chain
1543 2. Obtain the raw chunks for that delta chain
1543 2. Obtain the raw chunks for that delta chain
1544 3. Decompress each raw chunk
1544 3. Decompress each raw chunk
1545 4. Apply binary patches to obtain fulltext
1545 4. Apply binary patches to obtain fulltext
1546 5. Verify hash of fulltext
1546 5. Verify hash of fulltext
1547
1547
1548 This command measures the time spent in each of these phases.
1548 This command measures the time spent in each of these phases.
1549 """
1549 """
1550 if opts.get('changelog') or opts.get('manifest'):
1550 if opts.get('changelog') or opts.get('manifest'):
1551 file_, rev = None, file_
1551 file_, rev = None, file_
1552 elif rev is None:
1552 elif rev is None:
1553 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1553 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1554
1554
1555 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1555 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1556
1556
1557 # _chunkraw was renamed to _getsegmentforrevs.
1557 # _chunkraw was renamed to _getsegmentforrevs.
1558 try:
1558 try:
1559 segmentforrevs = r._getsegmentforrevs
1559 segmentforrevs = r._getsegmentforrevs
1560 except AttributeError:
1560 except AttributeError:
1561 segmentforrevs = r._chunkraw
1561 segmentforrevs = r._chunkraw
1562
1562
1563 node = r.lookup(rev)
1563 node = r.lookup(rev)
1564 rev = r.rev(node)
1564 rev = r.rev(node)
1565
1565
1566 def getrawchunks(data, chain):
1566 def getrawchunks(data, chain):
1567 start = r.start
1567 start = r.start
1568 length = r.length
1568 length = r.length
1569 inline = r._inline
1569 inline = r._inline
1570 iosize = r._io.size
1570 iosize = r._io.size
1571 buffer = util.buffer
1571 buffer = util.buffer
1572 offset = start(chain[0])
1572 offset = start(chain[0])
1573
1573
1574 chunks = []
1574 chunks = []
1575 ladd = chunks.append
1575 ladd = chunks.append
1576
1576
1577 for rev in chain:
1577 for rev in chain:
1578 chunkstart = start(rev)
1578 chunkstart = start(rev)
1579 if inline:
1579 if inline:
1580 chunkstart += (rev + 1) * iosize
1580 chunkstart += (rev + 1) * iosize
1581 chunklength = length(rev)
1581 chunklength = length(rev)
1582 ladd(buffer(data, chunkstart - offset, chunklength))
1582 ladd(buffer(data, chunkstart - offset, chunklength))
1583
1583
1584 return chunks
1584 return chunks
1585
1585
1586 def dodeltachain(rev):
1586 def dodeltachain(rev):
1587 if not cache:
1587 if not cache:
1588 r.clearcaches()
1588 r.clearcaches()
1589 r._deltachain(rev)
1589 r._deltachain(rev)
1590
1590
1591 def doread(chain):
1591 def doread(chain):
1592 if not cache:
1592 if not cache:
1593 r.clearcaches()
1593 r.clearcaches()
1594 segmentforrevs(chain[0], chain[-1])
1594 segmentforrevs(chain[0], chain[-1])
1595
1595
1596 def dorawchunks(data, chain):
1596 def dorawchunks(data, chain):
1597 if not cache:
1597 if not cache:
1598 r.clearcaches()
1598 r.clearcaches()
1599 getrawchunks(data, chain)
1599 getrawchunks(data, chain)
1600
1600
1601 def dodecompress(chunks):
1601 def dodecompress(chunks):
1602 decomp = r.decompress
1602 decomp = r.decompress
1603 for chunk in chunks:
1603 for chunk in chunks:
1604 decomp(chunk)
1604 decomp(chunk)
1605
1605
1606 def dopatch(text, bins):
1606 def dopatch(text, bins):
1607 if not cache:
1607 if not cache:
1608 r.clearcaches()
1608 r.clearcaches()
1609 mdiff.patches(text, bins)
1609 mdiff.patches(text, bins)
1610
1610
1611 def dohash(text):
1611 def dohash(text):
1612 if not cache:
1612 if not cache:
1613 r.clearcaches()
1613 r.clearcaches()
1614 r.checkhash(text, node, rev=rev)
1614 r.checkhash(text, node, rev=rev)
1615
1615
1616 def dorevision():
1616 def dorevision():
1617 if not cache:
1617 if not cache:
1618 r.clearcaches()
1618 r.clearcaches()
1619 r.revision(node)
1619 r.revision(node)
1620
1620
1621 chain = r._deltachain(rev)[0]
1621 chain = r._deltachain(rev)[0]
1622 data = segmentforrevs(chain[0], chain[-1])[1]
1622 data = segmentforrevs(chain[0], chain[-1])[1]
1623 rawchunks = getrawchunks(data, chain)
1623 rawchunks = getrawchunks(data, chain)
1624 bins = r._chunks(chain)
1624 bins = r._chunks(chain)
1625 text = str(bins[0])
1625 text = str(bins[0])
1626 bins = bins[1:]
1626 bins = bins[1:]
1627 text = mdiff.patches(text, bins)
1627 text = mdiff.patches(text, bins)
1628
1628
1629 benches = [
1629 benches = [
1630 (lambda: dorevision(), 'full'),
1630 (lambda: dorevision(), 'full'),
1631 (lambda: dodeltachain(rev), 'deltachain'),
1631 (lambda: dodeltachain(rev), 'deltachain'),
1632 (lambda: doread(chain), 'read'),
1632 (lambda: doread(chain), 'read'),
1633 (lambda: dorawchunks(data, chain), 'rawchunks'),
1633 (lambda: dorawchunks(data, chain), 'rawchunks'),
1634 (lambda: dodecompress(rawchunks), 'decompress'),
1634 (lambda: dodecompress(rawchunks), 'decompress'),
1635 (lambda: dopatch(text, bins), 'patch'),
1635 (lambda: dopatch(text, bins), 'patch'),
1636 (lambda: dohash(text), 'hash'),
1636 (lambda: dohash(text), 'hash'),
1637 ]
1637 ]
1638
1638
1639 for fn, title in benches:
1639 for fn, title in benches:
1640 timer, fm = gettimer(ui, opts)
1640 timer, fm = gettimer(ui, opts)
1641 timer(fn, title=title)
1641 timer(fn, title=title)
1642 fm.end()
1642 fm.end()
1643
1643
1644 @command('perfrevset',
1644 @command('perfrevset',
1645 [('C', 'clear', False, 'clear volatile cache between each call.'),
1645 [('C', 'clear', False, 'clear volatile cache between each call.'),
1646 ('', 'contexts', False, 'obtain changectx for each revision')]
1646 ('', 'contexts', False, 'obtain changectx for each revision')]
1647 + formatteropts, "REVSET")
1647 + formatteropts, "REVSET")
1648 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1648 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1649 """benchmark the execution time of a revset
1649 """benchmark the execution time of a revset
1650
1650
1651 Use the --clean option if need to evaluate the impact of build volatile
1651 Use the --clean option if need to evaluate the impact of build volatile
1652 revisions set cache on the revset execution. Volatile cache hold filtered
1652 revisions set cache on the revset execution. Volatile cache hold filtered
1653 and obsolete related cache."""
1653 and obsolete related cache."""
1654 timer, fm = gettimer(ui, opts)
1654 timer, fm = gettimer(ui, opts)
1655 def d():
1655 def d():
1656 if clear:
1656 if clear:
1657 repo.invalidatevolatilesets()
1657 repo.invalidatevolatilesets()
1658 if contexts:
1658 if contexts:
1659 for ctx in repo.set(expr): pass
1659 for ctx in repo.set(expr): pass
1660 else:
1660 else:
1661 for r in repo.revs(expr): pass
1661 for r in repo.revs(expr): pass
1662 timer(d)
1662 timer(d)
1663 fm.end()
1663 fm.end()
1664
1664
1665 @command('perfvolatilesets',
1665 @command('perfvolatilesets',
1666 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1666 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1667 ] + formatteropts)
1667 ] + formatteropts)
1668 def perfvolatilesets(ui, repo, *names, **opts):
1668 def perfvolatilesets(ui, repo, *names, **opts):
1669 """benchmark the computation of various volatile set
1669 """benchmark the computation of various volatile set
1670
1670
1671 Volatile set computes element related to filtering and obsolescence."""
1671 Volatile set computes element related to filtering and obsolescence."""
1672 timer, fm = gettimer(ui, opts)
1672 timer, fm = gettimer(ui, opts)
1673 repo = repo.unfiltered()
1673 repo = repo.unfiltered()
1674
1674
1675 def getobs(name):
1675 def getobs(name):
1676 def d():
1676 def d():
1677 repo.invalidatevolatilesets()
1677 repo.invalidatevolatilesets()
1678 if opts['clear_obsstore']:
1678 if opts['clear_obsstore']:
1679 clearfilecache(repo, 'obsstore')
1679 clearfilecache(repo, 'obsstore')
1680 obsolete.getrevs(repo, name)
1680 obsolete.getrevs(repo, name)
1681 return d
1681 return d
1682
1682
1683 allobs = sorted(obsolete.cachefuncs)
1683 allobs = sorted(obsolete.cachefuncs)
1684 if names:
1684 if names:
1685 allobs = [n for n in allobs if n in names]
1685 allobs = [n for n in allobs if n in names]
1686
1686
1687 for name in allobs:
1687 for name in allobs:
1688 timer(getobs(name), title=name)
1688 timer(getobs(name), title=name)
1689
1689
1690 def getfiltered(name):
1690 def getfiltered(name):
1691 def d():
1691 def d():
1692 repo.invalidatevolatilesets()
1692 repo.invalidatevolatilesets()
1693 if opts['clear_obsstore']:
1693 if opts['clear_obsstore']:
1694 clearfilecache(repo, 'obsstore')
1694 clearfilecache(repo, 'obsstore')
1695 repoview.filterrevs(repo, name)
1695 repoview.filterrevs(repo, name)
1696 return d
1696 return d
1697
1697
1698 allfilter = sorted(repoview.filtertable)
1698 allfilter = sorted(repoview.filtertable)
1699 if names:
1699 if names:
1700 allfilter = [n for n in allfilter if n in names]
1700 allfilter = [n for n in allfilter if n in names]
1701
1701
1702 for name in allfilter:
1702 for name in allfilter:
1703 timer(getfiltered(name), title=name)
1703 timer(getfiltered(name), title=name)
1704 fm.end()
1704 fm.end()
1705
1705
1706 @command('perfbranchmap',
1706 @command('perfbranchmap',
1707 [('f', 'full', False,
1707 [('f', 'full', False,
1708 'Includes build time of subset'),
1708 'Includes build time of subset'),
1709 ('', 'clear-revbranch', False,
1709 ('', 'clear-revbranch', False,
1710 'purge the revbranch cache between computation'),
1710 'purge the revbranch cache between computation'),
1711 ] + formatteropts)
1711 ] + formatteropts)
1712 def perfbranchmap(ui, repo, *filternames, **opts):
1712 def perfbranchmap(ui, repo, *filternames, **opts):
1713 """benchmark the update of a branchmap
1713 """benchmark the update of a branchmap
1714
1714
1715 This benchmarks the full repo.branchmap() call with read and write disabled
1715 This benchmarks the full repo.branchmap() call with read and write disabled
1716 """
1716 """
1717 full = opts.get("full", False)
1717 full = opts.get("full", False)
1718 clear_revbranch = opts.get("clear_revbranch", False)
1718 clear_revbranch = opts.get("clear_revbranch", False)
1719 timer, fm = gettimer(ui, opts)
1719 timer, fm = gettimer(ui, opts)
1720 def getbranchmap(filtername):
1720 def getbranchmap(filtername):
1721 """generate a benchmark function for the filtername"""
1721 """generate a benchmark function for the filtername"""
1722 if filtername is None:
1722 if filtername is None:
1723 view = repo
1723 view = repo
1724 else:
1724 else:
1725 view = repo.filtered(filtername)
1725 view = repo.filtered(filtername)
1726 def d():
1726 def d():
1727 if clear_revbranch:
1727 if clear_revbranch:
1728 repo.revbranchcache()._clear()
1728 repo.revbranchcache()._clear()
1729 if full:
1729 if full:
1730 view._branchcaches.clear()
1730 view._branchcaches.clear()
1731 else:
1731 else:
1732 view._branchcaches.pop(filtername, None)
1732 view._branchcaches.pop(filtername, None)
1733 view.branchmap()
1733 view.branchmap()
1734 return d
1734 return d
1735 # add filter in smaller subset to bigger subset
1735 # add filter in smaller subset to bigger subset
1736 possiblefilters = set(repoview.filtertable)
1736 possiblefilters = set(repoview.filtertable)
1737 if filternames:
1737 if filternames:
1738 possiblefilters &= set(filternames)
1738 possiblefilters &= set(filternames)
1739 subsettable = getbranchmapsubsettable()
1739 subsettable = getbranchmapsubsettable()
1740 allfilters = []
1740 allfilters = []
1741 while possiblefilters:
1741 while possiblefilters:
1742 for name in possiblefilters:
1742 for name in possiblefilters:
1743 subset = subsettable.get(name)
1743 subset = subsettable.get(name)
1744 if subset not in possiblefilters:
1744 if subset not in possiblefilters:
1745 break
1745 break
1746 else:
1746 else:
1747 assert False, 'subset cycle %s!' % possiblefilters
1747 assert False, 'subset cycle %s!' % possiblefilters
1748 allfilters.append(name)
1748 allfilters.append(name)
1749 possiblefilters.remove(name)
1749 possiblefilters.remove(name)
1750
1750
1751 # warm the cache
1751 # warm the cache
1752 if not full:
1752 if not full:
1753 for name in allfilters:
1753 for name in allfilters:
1754 repo.filtered(name).branchmap()
1754 repo.filtered(name).branchmap()
1755 if not filternames or 'unfiltered' in filternames:
1755 if not filternames or 'unfiltered' in filternames:
1756 # add unfiltered
1756 # add unfiltered
1757 allfilters.append(None)
1757 allfilters.append(None)
1758
1758
1759 branchcacheread = safeattrsetter(branchmap, 'read')
1759 branchcacheread = safeattrsetter(branchmap, 'read')
1760 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1760 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1761 branchcacheread.set(lambda repo: None)
1761 branchcacheread.set(lambda repo: None)
1762 branchcachewrite.set(lambda bc, repo: None)
1762 branchcachewrite.set(lambda bc, repo: None)
1763 try:
1763 try:
1764 for name in allfilters:
1764 for name in allfilters:
1765 printname = name
1765 printname = name
1766 if name is None:
1766 if name is None:
1767 printname = 'unfiltered'
1767 printname = 'unfiltered'
1768 timer(getbranchmap(name), title=str(printname))
1768 timer(getbranchmap(name), title=str(printname))
1769 finally:
1769 finally:
1770 branchcacheread.restore()
1770 branchcacheread.restore()
1771 branchcachewrite.restore()
1771 branchcachewrite.restore()
1772 fm.end()
1772 fm.end()
1773
1773
1774 @command('perfloadmarkers')
1774 @command('perfloadmarkers')
1775 def perfloadmarkers(ui, repo):
1775 def perfloadmarkers(ui, repo):
1776 """benchmark the time to parse the on-disk markers for a repo
1776 """benchmark the time to parse the on-disk markers for a repo
1777
1777
1778 Result is the number of markers in the repo."""
1778 Result is the number of markers in the repo."""
1779 timer, fm = gettimer(ui)
1779 timer, fm = gettimer(ui)
1780 svfs = getsvfs(repo)
1780 svfs = getsvfs(repo)
1781 timer(lambda: len(obsolete.obsstore(svfs)))
1781 timer(lambda: len(obsolete.obsstore(svfs)))
1782 fm.end()
1782 fm.end()
1783
1783
1784 @command('perflrucachedict', formatteropts +
1784 @command('perflrucachedict', formatteropts +
1785 [('', 'size', 4, 'size of cache'),
1785 [('', 'size', 4, 'size of cache'),
1786 ('', 'gets', 10000, 'number of key lookups'),
1786 ('', 'gets', 10000, 'number of key lookups'),
1787 ('', 'sets', 10000, 'number of key sets'),
1787 ('', 'sets', 10000, 'number of key sets'),
1788 ('', 'mixed', 10000, 'number of mixed mode operations'),
1788 ('', 'mixed', 10000, 'number of mixed mode operations'),
1789 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1789 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1790 norepo=True)
1790 norepo=True)
1791 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1791 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1792 mixedgetfreq=50, **opts):
1792 mixedgetfreq=50, **opts):
1793 def doinit():
1793 def doinit():
1794 for i in xrange(10000):
1794 for i in xrange(10000):
1795 util.lrucachedict(size)
1795 util.lrucachedict(size)
1796
1796
1797 values = []
1797 values = []
1798 for i in xrange(size):
1798 for i in xrange(size):
1799 values.append(random.randint(0, sys.maxint))
1799 values.append(random.randint(0, sys.maxint))
1800
1800
1801 # Get mode fills the cache and tests raw lookup performance with no
1801 # Get mode fills the cache and tests raw lookup performance with no
1802 # eviction.
1802 # eviction.
1803 getseq = []
1803 getseq = []
1804 for i in xrange(gets):
1804 for i in xrange(gets):
1805 getseq.append(random.choice(values))
1805 getseq.append(random.choice(values))
1806
1806
1807 def dogets():
1807 def dogets():
1808 d = util.lrucachedict(size)
1808 d = util.lrucachedict(size)
1809 for v in values:
1809 for v in values:
1810 d[v] = v
1810 d[v] = v
1811 for key in getseq:
1811 for key in getseq:
1812 value = d[key]
1812 value = d[key]
1813 value # silence pyflakes warning
1813 value # silence pyflakes warning
1814
1814
1815 # Set mode tests insertion speed with cache eviction.
1815 # Set mode tests insertion speed with cache eviction.
1816 setseq = []
1816 setseq = []
1817 for i in xrange(sets):
1817 for i in xrange(sets):
1818 setseq.append(random.randint(0, sys.maxint))
1818 setseq.append(random.randint(0, sys.maxint))
1819
1819
1820 def dosets():
1820 def dosets():
1821 d = util.lrucachedict(size)
1821 d = util.lrucachedict(size)
1822 for v in setseq:
1822 for v in setseq:
1823 d[v] = v
1823 d[v] = v
1824
1824
1825 # Mixed mode randomly performs gets and sets with eviction.
1825 # Mixed mode randomly performs gets and sets with eviction.
1826 mixedops = []
1826 mixedops = []
1827 for i in xrange(mixed):
1827 for i in xrange(mixed):
1828 r = random.randint(0, 100)
1828 r = random.randint(0, 100)
1829 if r < mixedgetfreq:
1829 if r < mixedgetfreq:
1830 op = 0
1830 op = 0
1831 else:
1831 else:
1832 op = 1
1832 op = 1
1833
1833
1834 mixedops.append((op, random.randint(0, size * 2)))
1834 mixedops.append((op, random.randint(0, size * 2)))
1835
1835
1836 def domixed():
1836 def domixed():
1837 d = util.lrucachedict(size)
1837 d = util.lrucachedict(size)
1838
1838
1839 for op, v in mixedops:
1839 for op, v in mixedops:
1840 if op == 0:
1840 if op == 0:
1841 try:
1841 try:
1842 d[v]
1842 d[v]
1843 except KeyError:
1843 except KeyError:
1844 pass
1844 pass
1845 else:
1845 else:
1846 d[v] = v
1846 d[v] = v
1847
1847
1848 benches = [
1848 benches = [
1849 (doinit, 'init'),
1849 (doinit, 'init'),
1850 (dogets, 'gets'),
1850 (dogets, 'gets'),
1851 (dosets, 'sets'),
1851 (dosets, 'sets'),
1852 (domixed, 'mixed')
1852 (domixed, 'mixed')
1853 ]
1853 ]
1854
1854
1855 for fn, title in benches:
1855 for fn, title in benches:
1856 timer, fm = gettimer(ui, opts)
1856 timer, fm = gettimer(ui, opts)
1857 timer(fn, title=title)
1857 timer(fn, title=title)
1858 fm.end()
1858 fm.end()
1859
1859
1860 @command('perfwrite', formatteropts)
1860 @command('perfwrite', formatteropts)
1861 def perfwrite(ui, repo, **opts):
1861 def perfwrite(ui, repo, **opts):
1862 """microbenchmark ui.write
1862 """microbenchmark ui.write
1863 """
1863 """
1864 timer, fm = gettimer(ui, opts)
1864 timer, fm = gettimer(ui, opts)
1865 def write():
1865 def write():
1866 for i in range(100000):
1866 for i in range(100000):
1867 ui.write(('Testing write performance\n'))
1867 ui.write(('Testing write performance\n'))
1868 timer(write)
1868 timer(write)
1869 fm.end()
1869 fm.end()
1870
1870
1871 def uisetup(ui):
1871 def uisetup(ui):
1872 if (util.safehasattr(cmdutil, 'openrevlog') and
1872 if (util.safehasattr(cmdutil, 'openrevlog') and
1873 not util.safehasattr(commands, 'debugrevlogopts')):
1873 not util.safehasattr(commands, 'debugrevlogopts')):
1874 # for "historical portability":
1874 # for "historical portability":
1875 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1875 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1876 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1876 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1877 # openrevlog() should cause failure, because it has been
1877 # openrevlog() should cause failure, because it has been
1878 # available since 3.5 (or 49c583ca48c4).
1878 # available since 3.5 (or 49c583ca48c4).
1879 def openrevlog(orig, repo, cmd, file_, opts):
1879 def openrevlog(orig, repo, cmd, file_, opts):
1880 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1880 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1881 raise error.Abort("This version doesn't support --dir option",
1881 raise error.Abort("This version doesn't support --dir option",
1882 hint="use 3.5 or later")
1882 hint="use 3.5 or later")
1883 return orig(repo, cmd, file_, opts)
1883 return orig(repo, cmd, file_, opts)
1884 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1884 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now