##// END OF EJS Templates
perf: add a command to benchmark linelog edits...
Jun Wu -
r39005:1601afbb default
parent child Browse files
Show More
@@ -1,1833 +1,1865 b''
1 # perf.py - performance test routines
1 # perf.py - performance test routines
2 '''helper extension to measure performance'''
2 '''helper extension to measure performance'''
3
3
4 # "historical portability" policy of perf.py:
4 # "historical portability" policy of perf.py:
5 #
5 #
6 # We have to do:
6 # We have to do:
7 # - make perf.py "loadable" with as wide Mercurial version as possible
7 # - make perf.py "loadable" with as wide Mercurial version as possible
8 # This doesn't mean that perf commands work correctly with that Mercurial.
8 # This doesn't mean that perf commands work correctly with that Mercurial.
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
10 # - make historical perf command work correctly with as wide Mercurial
10 # - make historical perf command work correctly with as wide Mercurial
11 # version as possible
11 # version as possible
12 #
12 #
13 # We have to do, if possible with reasonable cost:
13 # We have to do, if possible with reasonable cost:
14 # - make recent perf command for historical feature work correctly
14 # - make recent perf command for historical feature work correctly
15 # with early Mercurial
15 # with early Mercurial
16 #
16 #
17 # We don't have to do:
17 # We don't have to do:
18 # - make perf command for recent feature work correctly with early
18 # - make perf command for recent feature work correctly with early
19 # Mercurial
19 # Mercurial
20
20
21 from __future__ import absolute_import
21 from __future__ import absolute_import
22 import functools
22 import functools
23 import gc
23 import gc
24 import os
24 import os
25 import random
25 import random
26 import struct
26 import struct
27 import sys
27 import sys
28 import threading
28 import threading
29 import time
29 import time
30 from mercurial import (
30 from mercurial import (
31 changegroup,
31 changegroup,
32 cmdutil,
32 cmdutil,
33 commands,
33 commands,
34 copies,
34 copies,
35 error,
35 error,
36 extensions,
36 extensions,
37 mdiff,
37 mdiff,
38 merge,
38 merge,
39 revlog,
39 revlog,
40 util,
40 util,
41 )
41 )
42
42
43 # for "historical portability":
43 # for "historical portability":
44 # try to import modules separately (in dict order), and ignore
44 # try to import modules separately (in dict order), and ignore
45 # failure, because these aren't available with early Mercurial
45 # failure, because these aren't available with early Mercurial
46 try:
46 try:
47 from mercurial import branchmap # since 2.5 (or bcee63733aad)
47 from mercurial import branchmap # since 2.5 (or bcee63733aad)
48 except ImportError:
48 except ImportError:
49 pass
49 pass
50 try:
50 try:
51 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
51 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
52 except ImportError:
52 except ImportError:
53 pass
53 pass
54 try:
54 try:
55 from mercurial import registrar # since 3.7 (or 37d50250b696)
55 from mercurial import registrar # since 3.7 (or 37d50250b696)
56 dir(registrar) # forcibly load it
56 dir(registrar) # forcibly load it
57 except ImportError:
57 except ImportError:
58 registrar = None
58 registrar = None
59 try:
59 try:
60 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
60 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
61 except ImportError:
61 except ImportError:
62 pass
62 pass
63 try:
63 try:
64 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
64 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
65 except ImportError:
65 except ImportError:
66 pass
66 pass
67 try:
67 try:
68 from mercurial import pycompat
68 from mercurial import pycompat
69 getargspec = pycompat.getargspec # added to module after 4.5
69 getargspec = pycompat.getargspec # added to module after 4.5
70 except (ImportError, AttributeError):
70 except (ImportError, AttributeError):
71 import inspect
71 import inspect
72 getargspec = inspect.getargspec
72 getargspec = inspect.getargspec
73
73
74 try:
74 try:
75 # 4.7+
75 # 4.7+
76 queue = pycompat.queue.Queue
76 queue = pycompat.queue.Queue
77 except (AttributeError, ImportError):
77 except (AttributeError, ImportError):
78 # <4.7.
78 # <4.7.
79 try:
79 try:
80 queue = pycompat.queue
80 queue = pycompat.queue
81 except (AttributeError, ImportError):
81 except (AttributeError, ImportError):
82 queue = util.queue
82 queue = util.queue
83
83
84 try:
84 try:
85 from mercurial import logcmdutil
85 from mercurial import logcmdutil
86 makelogtemplater = logcmdutil.maketemplater
86 makelogtemplater = logcmdutil.maketemplater
87 except (AttributeError, ImportError):
87 except (AttributeError, ImportError):
88 try:
88 try:
89 makelogtemplater = cmdutil.makelogtemplater
89 makelogtemplater = cmdutil.makelogtemplater
90 except (AttributeError, ImportError):
90 except (AttributeError, ImportError):
91 makelogtemplater = None
91 makelogtemplater = None
92
92
93 # for "historical portability":
93 # for "historical portability":
94 # define util.safehasattr forcibly, because util.safehasattr has been
94 # define util.safehasattr forcibly, because util.safehasattr has been
95 # available since 1.9.3 (or 94b200a11cf7)
95 # available since 1.9.3 (or 94b200a11cf7)
96 _undefined = object()
96 _undefined = object()
97 def safehasattr(thing, attr):
97 def safehasattr(thing, attr):
98 return getattr(thing, attr, _undefined) is not _undefined
98 return getattr(thing, attr, _undefined) is not _undefined
99 setattr(util, 'safehasattr', safehasattr)
99 setattr(util, 'safehasattr', safehasattr)
100
100
101 # for "historical portability":
101 # for "historical portability":
102 # define util.timer forcibly, because util.timer has been available
102 # define util.timer forcibly, because util.timer has been available
103 # since ae5d60bb70c9
103 # since ae5d60bb70c9
104 if safehasattr(time, 'perf_counter'):
104 if safehasattr(time, 'perf_counter'):
105 util.timer = time.perf_counter
105 util.timer = time.perf_counter
106 elif os.name == 'nt':
106 elif os.name == '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('perfmanifest',[
794 @command('perfmanifest',[
795 ('m', 'manifest-rev', False, 'Look up a manifest node revision'),
795 ('m', 'manifest-rev', False, 'Look up a manifest node revision'),
796 ('', 'clear-disk', False, 'clear on-disk caches too'),
796 ('', 'clear-disk', False, 'clear on-disk caches too'),
797 ], 'REV|NODE')
797 ], 'REV|NODE')
798 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
798 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
799 """benchmark the time to read a manifest from disk and return a usable
799 """benchmark the time to read a manifest from disk and return a usable
800 dict-like object
800 dict-like object
801
801
802 Manifest caches are cleared before retrieval."""
802 Manifest caches are cleared before retrieval."""
803 timer, fm = gettimer(ui, opts)
803 timer, fm = gettimer(ui, opts)
804 if not manifest_rev:
804 if not manifest_rev:
805 ctx = scmutil.revsingle(repo, rev, rev)
805 ctx = scmutil.revsingle(repo, rev, rev)
806 t = ctx.manifestnode()
806 t = ctx.manifestnode()
807 else:
807 else:
808 t = repo.manifestlog._revlog.lookup(rev)
808 t = repo.manifestlog._revlog.lookup(rev)
809 def d():
809 def d():
810 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
810 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
811 repo.manifestlog[t].read()
811 repo.manifestlog[t].read()
812 timer(d)
812 timer(d)
813 fm.end()
813 fm.end()
814
814
815 @command('perfchangeset', formatteropts)
815 @command('perfchangeset', formatteropts)
816 def perfchangeset(ui, repo, rev, **opts):
816 def perfchangeset(ui, repo, rev, **opts):
817 timer, fm = gettimer(ui, opts)
817 timer, fm = gettimer(ui, opts)
818 n = scmutil.revsingle(repo, rev).node()
818 n = scmutil.revsingle(repo, rev).node()
819 def d():
819 def d():
820 repo.changelog.read(n)
820 repo.changelog.read(n)
821 #repo.changelog._cache = None
821 #repo.changelog._cache = None
822 timer(d)
822 timer(d)
823 fm.end()
823 fm.end()
824
824
825 @command('perfindex', formatteropts)
825 @command('perfindex', formatteropts)
826 def perfindex(ui, repo, **opts):
826 def perfindex(ui, repo, **opts):
827 import mercurial.revlog
827 import mercurial.revlog
828 timer, fm = gettimer(ui, opts)
828 timer, fm = gettimer(ui, opts)
829 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
829 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
830 n = repo["tip"].node()
830 n = repo["tip"].node()
831 svfs = getsvfs(repo)
831 svfs = getsvfs(repo)
832 def d():
832 def d():
833 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
833 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
834 cl.rev(n)
834 cl.rev(n)
835 timer(d)
835 timer(d)
836 fm.end()
836 fm.end()
837
837
838 @command('perfstartup', formatteropts)
838 @command('perfstartup', formatteropts)
839 def perfstartup(ui, repo, **opts):
839 def perfstartup(ui, repo, **opts):
840 timer, fm = gettimer(ui, opts)
840 timer, fm = gettimer(ui, opts)
841 cmd = sys.argv[0]
841 cmd = sys.argv[0]
842 def d():
842 def d():
843 if os.name != 'nt':
843 if os.name != 'nt':
844 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
844 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
845 else:
845 else:
846 os.environ['HGRCPATH'] = ' '
846 os.environ['HGRCPATH'] = ' '
847 os.system("%s version -q > NUL" % cmd)
847 os.system("%s version -q > NUL" % cmd)
848 timer(d)
848 timer(d)
849 fm.end()
849 fm.end()
850
850
851 @command('perfparents', formatteropts)
851 @command('perfparents', formatteropts)
852 def perfparents(ui, repo, **opts):
852 def perfparents(ui, repo, **opts):
853 timer, fm = gettimer(ui, opts)
853 timer, fm = gettimer(ui, opts)
854 # control the number of commits perfparents iterates over
854 # control the number of commits perfparents iterates over
855 # experimental config: perf.parentscount
855 # experimental config: perf.parentscount
856 count = getint(ui, "perf", "parentscount", 1000)
856 count = getint(ui, "perf", "parentscount", 1000)
857 if len(repo.changelog) < count:
857 if len(repo.changelog) < count:
858 raise error.Abort("repo needs %d commits for this test" % count)
858 raise error.Abort("repo needs %d commits for this test" % count)
859 repo = repo.unfiltered()
859 repo = repo.unfiltered()
860 nl = [repo.changelog.node(i) for i in xrange(count)]
860 nl = [repo.changelog.node(i) for i in xrange(count)]
861 def d():
861 def d():
862 for n in nl:
862 for n in nl:
863 repo.changelog.parents(n)
863 repo.changelog.parents(n)
864 timer(d)
864 timer(d)
865 fm.end()
865 fm.end()
866
866
867 @command('perfctxfiles', formatteropts)
867 @command('perfctxfiles', formatteropts)
868 def perfctxfiles(ui, repo, x, **opts):
868 def perfctxfiles(ui, repo, x, **opts):
869 x = int(x)
869 x = int(x)
870 timer, fm = gettimer(ui, opts)
870 timer, fm = gettimer(ui, opts)
871 def d():
871 def d():
872 len(repo[x].files())
872 len(repo[x].files())
873 timer(d)
873 timer(d)
874 fm.end()
874 fm.end()
875
875
876 @command('perfrawfiles', formatteropts)
876 @command('perfrawfiles', formatteropts)
877 def perfrawfiles(ui, repo, x, **opts):
877 def perfrawfiles(ui, repo, x, **opts):
878 x = int(x)
878 x = int(x)
879 timer, fm = gettimer(ui, opts)
879 timer, fm = gettimer(ui, opts)
880 cl = repo.changelog
880 cl = repo.changelog
881 def d():
881 def d():
882 len(cl.read(x)[3])
882 len(cl.read(x)[3])
883 timer(d)
883 timer(d)
884 fm.end()
884 fm.end()
885
885
886 @command('perflookup', formatteropts)
886 @command('perflookup', formatteropts)
887 def perflookup(ui, repo, rev, **opts):
887 def perflookup(ui, repo, rev, **opts):
888 timer, fm = gettimer(ui, opts)
888 timer, fm = gettimer(ui, opts)
889 timer(lambda: len(repo.lookup(rev)))
889 timer(lambda: len(repo.lookup(rev)))
890 fm.end()
890 fm.end()
891
891
892 @command('perflinelogedits',
893 [('n', 'edits', 10000, 'number of edits'),
894 ('', 'max-hunk-lines', 10, 'max lines in a hunk'),
895 ], norepo=True)
896 def perflinelogedits(ui, **opts):
897 from mercurial import linelog
898
899 edits = opts['edits']
900 maxhunklines = opts['max_hunk_lines']
901
902 maxb1 = 100000
903 random.seed(0)
904 randint = random.randint
905 currentlines = 0
906 arglist = []
907 for rev in xrange(edits):
908 a1 = randint(0, currentlines)
909 a2 = randint(a1, min(currentlines, a1 + maxhunklines))
910 b1 = randint(0, maxb1)
911 b2 = randint(b1, b1 + maxhunklines)
912 currentlines += (b2 - b1) - (a2 - a1)
913 arglist.append((rev, a1, a2, b1, b2))
914
915 def d():
916 ll = linelog.linelog()
917 for args in arglist:
918 ll.replacelines(*args)
919
920 timer, fm = gettimer(ui, opts)
921 timer(d)
922 fm.end()
923
892 @command('perfrevrange', formatteropts)
924 @command('perfrevrange', formatteropts)
893 def perfrevrange(ui, repo, *specs, **opts):
925 def perfrevrange(ui, repo, *specs, **opts):
894 timer, fm = gettimer(ui, opts)
926 timer, fm = gettimer(ui, opts)
895 revrange = scmutil.revrange
927 revrange = scmutil.revrange
896 timer(lambda: len(revrange(repo, specs)))
928 timer(lambda: len(revrange(repo, specs)))
897 fm.end()
929 fm.end()
898
930
899 @command('perfnodelookup', formatteropts)
931 @command('perfnodelookup', formatteropts)
900 def perfnodelookup(ui, repo, rev, **opts):
932 def perfnodelookup(ui, repo, rev, **opts):
901 timer, fm = gettimer(ui, opts)
933 timer, fm = gettimer(ui, opts)
902 import mercurial.revlog
934 import mercurial.revlog
903 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
935 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
904 n = scmutil.revsingle(repo, rev).node()
936 n = scmutil.revsingle(repo, rev).node()
905 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
937 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
906 def d():
938 def d():
907 cl.rev(n)
939 cl.rev(n)
908 clearcaches(cl)
940 clearcaches(cl)
909 timer(d)
941 timer(d)
910 fm.end()
942 fm.end()
911
943
912 @command('perflog',
944 @command('perflog',
913 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
945 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
914 def perflog(ui, repo, rev=None, **opts):
946 def perflog(ui, repo, rev=None, **opts):
915 if rev is None:
947 if rev is None:
916 rev=[]
948 rev=[]
917 timer, fm = gettimer(ui, opts)
949 timer, fm = gettimer(ui, opts)
918 ui.pushbuffer()
950 ui.pushbuffer()
919 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
951 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
920 copies=opts.get('rename')))
952 copies=opts.get('rename')))
921 ui.popbuffer()
953 ui.popbuffer()
922 fm.end()
954 fm.end()
923
955
924 @command('perfmoonwalk', formatteropts)
956 @command('perfmoonwalk', formatteropts)
925 def perfmoonwalk(ui, repo, **opts):
957 def perfmoonwalk(ui, repo, **opts):
926 """benchmark walking the changelog backwards
958 """benchmark walking the changelog backwards
927
959
928 This also loads the changelog data for each revision in the changelog.
960 This also loads the changelog data for each revision in the changelog.
929 """
961 """
930 timer, fm = gettimer(ui, opts)
962 timer, fm = gettimer(ui, opts)
931 def moonwalk():
963 def moonwalk():
932 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
964 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
933 ctx = repo[i]
965 ctx = repo[i]
934 ctx.branch() # read changelog data (in addition to the index)
966 ctx.branch() # read changelog data (in addition to the index)
935 timer(moonwalk)
967 timer(moonwalk)
936 fm.end()
968 fm.end()
937
969
938 @command('perftemplating',
970 @command('perftemplating',
939 [('r', 'rev', [], 'revisions to run the template on'),
971 [('r', 'rev', [], 'revisions to run the template on'),
940 ] + formatteropts)
972 ] + formatteropts)
941 def perftemplating(ui, repo, testedtemplate=None, **opts):
973 def perftemplating(ui, repo, testedtemplate=None, **opts):
942 """test the rendering time of a given template"""
974 """test the rendering time of a given template"""
943 if makelogtemplater is None:
975 if makelogtemplater is None:
944 raise error.Abort(("perftemplating not available with this Mercurial"),
976 raise error.Abort(("perftemplating not available with this Mercurial"),
945 hint="use 4.3 or later")
977 hint="use 4.3 or later")
946
978
947 nullui = ui.copy()
979 nullui = ui.copy()
948 nullui.fout = open(os.devnull, 'wb')
980 nullui.fout = open(os.devnull, 'wb')
949 nullui.disablepager()
981 nullui.disablepager()
950 revs = opts.get('rev')
982 revs = opts.get('rev')
951 if not revs:
983 if not revs:
952 revs = ['all()']
984 revs = ['all()']
953 revs = list(scmutil.revrange(repo, revs))
985 revs = list(scmutil.revrange(repo, revs))
954
986
955 defaulttemplate = ('{date|shortdate} [{rev}:{node|short}]'
987 defaulttemplate = ('{date|shortdate} [{rev}:{node|short}]'
956 ' {author|person}: {desc|firstline}\n')
988 ' {author|person}: {desc|firstline}\n')
957 if testedtemplate is None:
989 if testedtemplate is None:
958 testedtemplate = defaulttemplate
990 testedtemplate = defaulttemplate
959 displayer = makelogtemplater(nullui, repo, testedtemplate)
991 displayer = makelogtemplater(nullui, repo, testedtemplate)
960 def format():
992 def format():
961 for r in revs:
993 for r in revs:
962 ctx = repo[r]
994 ctx = repo[r]
963 displayer.show(ctx)
995 displayer.show(ctx)
964 displayer.flush(ctx)
996 displayer.flush(ctx)
965
997
966 timer, fm = gettimer(ui, opts)
998 timer, fm = gettimer(ui, opts)
967 timer(format)
999 timer(format)
968 fm.end()
1000 fm.end()
969
1001
970 @command('perfcca', formatteropts)
1002 @command('perfcca', formatteropts)
971 def perfcca(ui, repo, **opts):
1003 def perfcca(ui, repo, **opts):
972 timer, fm = gettimer(ui, opts)
1004 timer, fm = gettimer(ui, opts)
973 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
1005 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
974 fm.end()
1006 fm.end()
975
1007
976 @command('perffncacheload', formatteropts)
1008 @command('perffncacheload', formatteropts)
977 def perffncacheload(ui, repo, **opts):
1009 def perffncacheload(ui, repo, **opts):
978 timer, fm = gettimer(ui, opts)
1010 timer, fm = gettimer(ui, opts)
979 s = repo.store
1011 s = repo.store
980 def d():
1012 def d():
981 s.fncache._load()
1013 s.fncache._load()
982 timer(d)
1014 timer(d)
983 fm.end()
1015 fm.end()
984
1016
985 @command('perffncachewrite', formatteropts)
1017 @command('perffncachewrite', formatteropts)
986 def perffncachewrite(ui, repo, **opts):
1018 def perffncachewrite(ui, repo, **opts):
987 timer, fm = gettimer(ui, opts)
1019 timer, fm = gettimer(ui, opts)
988 s = repo.store
1020 s = repo.store
989 lock = repo.lock()
1021 lock = repo.lock()
990 s.fncache._load()
1022 s.fncache._load()
991 tr = repo.transaction('perffncachewrite')
1023 tr = repo.transaction('perffncachewrite')
992 tr.addbackup('fncache')
1024 tr.addbackup('fncache')
993 def d():
1025 def d():
994 s.fncache._dirty = True
1026 s.fncache._dirty = True
995 s.fncache.write(tr)
1027 s.fncache.write(tr)
996 timer(d)
1028 timer(d)
997 tr.close()
1029 tr.close()
998 lock.release()
1030 lock.release()
999 fm.end()
1031 fm.end()
1000
1032
1001 @command('perffncacheencode', formatteropts)
1033 @command('perffncacheencode', formatteropts)
1002 def perffncacheencode(ui, repo, **opts):
1034 def perffncacheencode(ui, repo, **opts):
1003 timer, fm = gettimer(ui, opts)
1035 timer, fm = gettimer(ui, opts)
1004 s = repo.store
1036 s = repo.store
1005 s.fncache._load()
1037 s.fncache._load()
1006 def d():
1038 def d():
1007 for p in s.fncache.entries:
1039 for p in s.fncache.entries:
1008 s.encode(p)
1040 s.encode(p)
1009 timer(d)
1041 timer(d)
1010 fm.end()
1042 fm.end()
1011
1043
1012 def _bdiffworker(q, blocks, xdiff, ready, done):
1044 def _bdiffworker(q, blocks, xdiff, ready, done):
1013 while not done.is_set():
1045 while not done.is_set():
1014 pair = q.get()
1046 pair = q.get()
1015 while pair is not None:
1047 while pair is not None:
1016 if xdiff:
1048 if xdiff:
1017 mdiff.bdiff.xdiffblocks(*pair)
1049 mdiff.bdiff.xdiffblocks(*pair)
1018 elif blocks:
1050 elif blocks:
1019 mdiff.bdiff.blocks(*pair)
1051 mdiff.bdiff.blocks(*pair)
1020 else:
1052 else:
1021 mdiff.textdiff(*pair)
1053 mdiff.textdiff(*pair)
1022 q.task_done()
1054 q.task_done()
1023 pair = q.get()
1055 pair = q.get()
1024 q.task_done() # for the None one
1056 q.task_done() # for the None one
1025 with ready:
1057 with ready:
1026 ready.wait()
1058 ready.wait()
1027
1059
1028 @command('perfbdiff', revlogopts + formatteropts + [
1060 @command('perfbdiff', revlogopts + formatteropts + [
1029 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1061 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1030 ('', 'alldata', False, 'test bdiffs for all associated revisions'),
1062 ('', 'alldata', False, 'test bdiffs for all associated revisions'),
1031 ('', 'threads', 0, 'number of thread to use (disable with 0)'),
1063 ('', 'threads', 0, 'number of thread to use (disable with 0)'),
1032 ('', 'blocks', False, 'test computing diffs into blocks'),
1064 ('', 'blocks', False, 'test computing diffs into blocks'),
1033 ('', 'xdiff', False, 'use xdiff algorithm'),
1065 ('', 'xdiff', False, 'use xdiff algorithm'),
1034 ],
1066 ],
1035
1067
1036 '-c|-m|FILE REV')
1068 '-c|-m|FILE REV')
1037 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1069 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1038 """benchmark a bdiff between revisions
1070 """benchmark a bdiff between revisions
1039
1071
1040 By default, benchmark a bdiff between its delta parent and itself.
1072 By default, benchmark a bdiff between its delta parent and itself.
1041
1073
1042 With ``--count``, benchmark bdiffs between delta parents and self for N
1074 With ``--count``, benchmark bdiffs between delta parents and self for N
1043 revisions starting at the specified revision.
1075 revisions starting at the specified revision.
1044
1076
1045 With ``--alldata``, assume the requested revision is a changeset and
1077 With ``--alldata``, assume the requested revision is a changeset and
1046 measure bdiffs for all changes related to that changeset (manifest
1078 measure bdiffs for all changes related to that changeset (manifest
1047 and filelogs).
1079 and filelogs).
1048 """
1080 """
1049 opts = pycompat.byteskwargs(opts)
1081 opts = pycompat.byteskwargs(opts)
1050
1082
1051 if opts['xdiff'] and not opts['blocks']:
1083 if opts['xdiff'] and not opts['blocks']:
1052 raise error.CommandError('perfbdiff', '--xdiff requires --blocks')
1084 raise error.CommandError('perfbdiff', '--xdiff requires --blocks')
1053
1085
1054 if opts['alldata']:
1086 if opts['alldata']:
1055 opts['changelog'] = True
1087 opts['changelog'] = True
1056
1088
1057 if opts.get('changelog') or opts.get('manifest'):
1089 if opts.get('changelog') or opts.get('manifest'):
1058 file_, rev = None, file_
1090 file_, rev = None, file_
1059 elif rev is None:
1091 elif rev is None:
1060 raise error.CommandError('perfbdiff', 'invalid arguments')
1092 raise error.CommandError('perfbdiff', 'invalid arguments')
1061
1093
1062 blocks = opts['blocks']
1094 blocks = opts['blocks']
1063 xdiff = opts['xdiff']
1095 xdiff = opts['xdiff']
1064 textpairs = []
1096 textpairs = []
1065
1097
1066 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
1098 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
1067
1099
1068 startrev = r.rev(r.lookup(rev))
1100 startrev = r.rev(r.lookup(rev))
1069 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1101 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1070 if opts['alldata']:
1102 if opts['alldata']:
1071 # Load revisions associated with changeset.
1103 # Load revisions associated with changeset.
1072 ctx = repo[rev]
1104 ctx = repo[rev]
1073 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1105 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1074 for pctx in ctx.parents():
1106 for pctx in ctx.parents():
1075 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1107 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1076 textpairs.append((pman, mtext))
1108 textpairs.append((pman, mtext))
1077
1109
1078 # Load filelog revisions by iterating manifest delta.
1110 # Load filelog revisions by iterating manifest delta.
1079 man = ctx.manifest()
1111 man = ctx.manifest()
1080 pman = ctx.p1().manifest()
1112 pman = ctx.p1().manifest()
1081 for filename, change in pman.diff(man).items():
1113 for filename, change in pman.diff(man).items():
1082 fctx = repo.file(filename)
1114 fctx = repo.file(filename)
1083 f1 = fctx.revision(change[0][0] or -1)
1115 f1 = fctx.revision(change[0][0] or -1)
1084 f2 = fctx.revision(change[1][0] or -1)
1116 f2 = fctx.revision(change[1][0] or -1)
1085 textpairs.append((f1, f2))
1117 textpairs.append((f1, f2))
1086 else:
1118 else:
1087 dp = r.deltaparent(rev)
1119 dp = r.deltaparent(rev)
1088 textpairs.append((r.revision(dp), r.revision(rev)))
1120 textpairs.append((r.revision(dp), r.revision(rev)))
1089
1121
1090 withthreads = threads > 0
1122 withthreads = threads > 0
1091 if not withthreads:
1123 if not withthreads:
1092 def d():
1124 def d():
1093 for pair in textpairs:
1125 for pair in textpairs:
1094 if xdiff:
1126 if xdiff:
1095 mdiff.bdiff.xdiffblocks(*pair)
1127 mdiff.bdiff.xdiffblocks(*pair)
1096 elif blocks:
1128 elif blocks:
1097 mdiff.bdiff.blocks(*pair)
1129 mdiff.bdiff.blocks(*pair)
1098 else:
1130 else:
1099 mdiff.textdiff(*pair)
1131 mdiff.textdiff(*pair)
1100 else:
1132 else:
1101 q = queue()
1133 q = queue()
1102 for i in xrange(threads):
1134 for i in xrange(threads):
1103 q.put(None)
1135 q.put(None)
1104 ready = threading.Condition()
1136 ready = threading.Condition()
1105 done = threading.Event()
1137 done = threading.Event()
1106 for i in xrange(threads):
1138 for i in xrange(threads):
1107 threading.Thread(target=_bdiffworker,
1139 threading.Thread(target=_bdiffworker,
1108 args=(q, blocks, xdiff, ready, done)).start()
1140 args=(q, blocks, xdiff, ready, done)).start()
1109 q.join()
1141 q.join()
1110 def d():
1142 def d():
1111 for pair in textpairs:
1143 for pair in textpairs:
1112 q.put(pair)
1144 q.put(pair)
1113 for i in xrange(threads):
1145 for i in xrange(threads):
1114 q.put(None)
1146 q.put(None)
1115 with ready:
1147 with ready:
1116 ready.notify_all()
1148 ready.notify_all()
1117 q.join()
1149 q.join()
1118 timer, fm = gettimer(ui, opts)
1150 timer, fm = gettimer(ui, opts)
1119 timer(d)
1151 timer(d)
1120 fm.end()
1152 fm.end()
1121
1153
1122 if withthreads:
1154 if withthreads:
1123 done.set()
1155 done.set()
1124 for i in xrange(threads):
1156 for i in xrange(threads):
1125 q.put(None)
1157 q.put(None)
1126 with ready:
1158 with ready:
1127 ready.notify_all()
1159 ready.notify_all()
1128
1160
1129 @command('perfunidiff', revlogopts + formatteropts + [
1161 @command('perfunidiff', revlogopts + formatteropts + [
1130 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1162 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
1131 ('', 'alldata', False, 'test unidiffs for all associated revisions'),
1163 ('', 'alldata', False, 'test unidiffs for all associated revisions'),
1132 ], '-c|-m|FILE REV')
1164 ], '-c|-m|FILE REV')
1133 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1165 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1134 """benchmark a unified diff between revisions
1166 """benchmark a unified diff between revisions
1135
1167
1136 This doesn't include any copy tracing - it's just a unified diff
1168 This doesn't include any copy tracing - it's just a unified diff
1137 of the texts.
1169 of the texts.
1138
1170
1139 By default, benchmark a diff between its delta parent and itself.
1171 By default, benchmark a diff between its delta parent and itself.
1140
1172
1141 With ``--count``, benchmark diffs between delta parents and self for N
1173 With ``--count``, benchmark diffs between delta parents and self for N
1142 revisions starting at the specified revision.
1174 revisions starting at the specified revision.
1143
1175
1144 With ``--alldata``, assume the requested revision is a changeset and
1176 With ``--alldata``, assume the requested revision is a changeset and
1145 measure diffs for all changes related to that changeset (manifest
1177 measure diffs for all changes related to that changeset (manifest
1146 and filelogs).
1178 and filelogs).
1147 """
1179 """
1148 if opts['alldata']:
1180 if opts['alldata']:
1149 opts['changelog'] = True
1181 opts['changelog'] = True
1150
1182
1151 if opts.get('changelog') or opts.get('manifest'):
1183 if opts.get('changelog') or opts.get('manifest'):
1152 file_, rev = None, file_
1184 file_, rev = None, file_
1153 elif rev is None:
1185 elif rev is None:
1154 raise error.CommandError('perfunidiff', 'invalid arguments')
1186 raise error.CommandError('perfunidiff', 'invalid arguments')
1155
1187
1156 textpairs = []
1188 textpairs = []
1157
1189
1158 r = cmdutil.openrevlog(repo, 'perfunidiff', file_, opts)
1190 r = cmdutil.openrevlog(repo, 'perfunidiff', file_, opts)
1159
1191
1160 startrev = r.rev(r.lookup(rev))
1192 startrev = r.rev(r.lookup(rev))
1161 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1193 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1162 if opts['alldata']:
1194 if opts['alldata']:
1163 # Load revisions associated with changeset.
1195 # Load revisions associated with changeset.
1164 ctx = repo[rev]
1196 ctx = repo[rev]
1165 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1197 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
1166 for pctx in ctx.parents():
1198 for pctx in ctx.parents():
1167 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1199 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
1168 textpairs.append((pman, mtext))
1200 textpairs.append((pman, mtext))
1169
1201
1170 # Load filelog revisions by iterating manifest delta.
1202 # Load filelog revisions by iterating manifest delta.
1171 man = ctx.manifest()
1203 man = ctx.manifest()
1172 pman = ctx.p1().manifest()
1204 pman = ctx.p1().manifest()
1173 for filename, change in pman.diff(man).items():
1205 for filename, change in pman.diff(man).items():
1174 fctx = repo.file(filename)
1206 fctx = repo.file(filename)
1175 f1 = fctx.revision(change[0][0] or -1)
1207 f1 = fctx.revision(change[0][0] or -1)
1176 f2 = fctx.revision(change[1][0] or -1)
1208 f2 = fctx.revision(change[1][0] or -1)
1177 textpairs.append((f1, f2))
1209 textpairs.append((f1, f2))
1178 else:
1210 else:
1179 dp = r.deltaparent(rev)
1211 dp = r.deltaparent(rev)
1180 textpairs.append((r.revision(dp), r.revision(rev)))
1212 textpairs.append((r.revision(dp), r.revision(rev)))
1181
1213
1182 def d():
1214 def d():
1183 for left, right in textpairs:
1215 for left, right in textpairs:
1184 # The date strings don't matter, so we pass empty strings.
1216 # The date strings don't matter, so we pass empty strings.
1185 headerlines, hunks = mdiff.unidiff(
1217 headerlines, hunks = mdiff.unidiff(
1186 left, '', right, '', 'left', 'right', binary=False)
1218 left, '', right, '', 'left', 'right', binary=False)
1187 # consume iterators in roughly the way patch.py does
1219 # consume iterators in roughly the way patch.py does
1188 b'\n'.join(headerlines)
1220 b'\n'.join(headerlines)
1189 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1221 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1190 timer, fm = gettimer(ui, opts)
1222 timer, fm = gettimer(ui, opts)
1191 timer(d)
1223 timer(d)
1192 fm.end()
1224 fm.end()
1193
1225
1194 @command('perfdiffwd', formatteropts)
1226 @command('perfdiffwd', formatteropts)
1195 def perfdiffwd(ui, repo, **opts):
1227 def perfdiffwd(ui, repo, **opts):
1196 """Profile diff of working directory changes"""
1228 """Profile diff of working directory changes"""
1197 timer, fm = gettimer(ui, opts)
1229 timer, fm = gettimer(ui, opts)
1198 options = {
1230 options = {
1199 'w': 'ignore_all_space',
1231 'w': 'ignore_all_space',
1200 'b': 'ignore_space_change',
1232 'b': 'ignore_space_change',
1201 'B': 'ignore_blank_lines',
1233 'B': 'ignore_blank_lines',
1202 }
1234 }
1203
1235
1204 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1236 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1205 opts = dict((options[c], '1') for c in diffopt)
1237 opts = dict((options[c], '1') for c in diffopt)
1206 def d():
1238 def d():
1207 ui.pushbuffer()
1239 ui.pushbuffer()
1208 commands.diff(ui, repo, **opts)
1240 commands.diff(ui, repo, **opts)
1209 ui.popbuffer()
1241 ui.popbuffer()
1210 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
1242 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
1211 timer(d, title)
1243 timer(d, title)
1212 fm.end()
1244 fm.end()
1213
1245
1214 @command('perfrevlogindex', revlogopts + formatteropts,
1246 @command('perfrevlogindex', revlogopts + formatteropts,
1215 '-c|-m|FILE')
1247 '-c|-m|FILE')
1216 def perfrevlogindex(ui, repo, file_=None, **opts):
1248 def perfrevlogindex(ui, repo, file_=None, **opts):
1217 """Benchmark operations against a revlog index.
1249 """Benchmark operations against a revlog index.
1218
1250
1219 This tests constructing a revlog instance, reading index data,
1251 This tests constructing a revlog instance, reading index data,
1220 parsing index data, and performing various operations related to
1252 parsing index data, and performing various operations related to
1221 index data.
1253 index data.
1222 """
1254 """
1223
1255
1224 rl = cmdutil.openrevlog(repo, 'perfrevlogindex', file_, opts)
1256 rl = cmdutil.openrevlog(repo, 'perfrevlogindex', file_, opts)
1225
1257
1226 opener = getattr(rl, 'opener') # trick linter
1258 opener = getattr(rl, 'opener') # trick linter
1227 indexfile = rl.indexfile
1259 indexfile = rl.indexfile
1228 data = opener.read(indexfile)
1260 data = opener.read(indexfile)
1229
1261
1230 header = struct.unpack('>I', data[0:4])[0]
1262 header = struct.unpack('>I', data[0:4])[0]
1231 version = header & 0xFFFF
1263 version = header & 0xFFFF
1232 if version == 1:
1264 if version == 1:
1233 revlogio = revlog.revlogio()
1265 revlogio = revlog.revlogio()
1234 inline = header & (1 << 16)
1266 inline = header & (1 << 16)
1235 else:
1267 else:
1236 raise error.Abort(('unsupported revlog version: %d') % version)
1268 raise error.Abort(('unsupported revlog version: %d') % version)
1237
1269
1238 rllen = len(rl)
1270 rllen = len(rl)
1239
1271
1240 node0 = rl.node(0)
1272 node0 = rl.node(0)
1241 node25 = rl.node(rllen // 4)
1273 node25 = rl.node(rllen // 4)
1242 node50 = rl.node(rllen // 2)
1274 node50 = rl.node(rllen // 2)
1243 node75 = rl.node(rllen // 4 * 3)
1275 node75 = rl.node(rllen // 4 * 3)
1244 node100 = rl.node(rllen - 1)
1276 node100 = rl.node(rllen - 1)
1245
1277
1246 allrevs = range(rllen)
1278 allrevs = range(rllen)
1247 allrevsrev = list(reversed(allrevs))
1279 allrevsrev = list(reversed(allrevs))
1248 allnodes = [rl.node(rev) for rev in range(rllen)]
1280 allnodes = [rl.node(rev) for rev in range(rllen)]
1249 allnodesrev = list(reversed(allnodes))
1281 allnodesrev = list(reversed(allnodes))
1250
1282
1251 def constructor():
1283 def constructor():
1252 revlog.revlog(opener, indexfile)
1284 revlog.revlog(opener, indexfile)
1253
1285
1254 def read():
1286 def read():
1255 with opener(indexfile) as fh:
1287 with opener(indexfile) as fh:
1256 fh.read()
1288 fh.read()
1257
1289
1258 def parseindex():
1290 def parseindex():
1259 revlogio.parseindex(data, inline)
1291 revlogio.parseindex(data, inline)
1260
1292
1261 def getentry(revornode):
1293 def getentry(revornode):
1262 index = revlogio.parseindex(data, inline)[0]
1294 index = revlogio.parseindex(data, inline)[0]
1263 index[revornode]
1295 index[revornode]
1264
1296
1265 def getentries(revs, count=1):
1297 def getentries(revs, count=1):
1266 index = revlogio.parseindex(data, inline)[0]
1298 index = revlogio.parseindex(data, inline)[0]
1267
1299
1268 for i in range(count):
1300 for i in range(count):
1269 for rev in revs:
1301 for rev in revs:
1270 index[rev]
1302 index[rev]
1271
1303
1272 def resolvenode(node):
1304 def resolvenode(node):
1273 nodemap = revlogio.parseindex(data, inline)[1]
1305 nodemap = revlogio.parseindex(data, inline)[1]
1274 # This only works for the C code.
1306 # This only works for the C code.
1275 if nodemap is None:
1307 if nodemap is None:
1276 return
1308 return
1277
1309
1278 try:
1310 try:
1279 nodemap[node]
1311 nodemap[node]
1280 except error.RevlogError:
1312 except error.RevlogError:
1281 pass
1313 pass
1282
1314
1283 def resolvenodes(nodes, count=1):
1315 def resolvenodes(nodes, count=1):
1284 nodemap = revlogio.parseindex(data, inline)[1]
1316 nodemap = revlogio.parseindex(data, inline)[1]
1285 if nodemap is None:
1317 if nodemap is None:
1286 return
1318 return
1287
1319
1288 for i in range(count):
1320 for i in range(count):
1289 for node in nodes:
1321 for node in nodes:
1290 try:
1322 try:
1291 nodemap[node]
1323 nodemap[node]
1292 except error.RevlogError:
1324 except error.RevlogError:
1293 pass
1325 pass
1294
1326
1295 benches = [
1327 benches = [
1296 (constructor, 'revlog constructor'),
1328 (constructor, 'revlog constructor'),
1297 (read, 'read'),
1329 (read, 'read'),
1298 (parseindex, 'create index object'),
1330 (parseindex, 'create index object'),
1299 (lambda: getentry(0), 'retrieve index entry for rev 0'),
1331 (lambda: getentry(0), 'retrieve index entry for rev 0'),
1300 (lambda: resolvenode('a' * 20), 'look up missing node'),
1332 (lambda: resolvenode('a' * 20), 'look up missing node'),
1301 (lambda: resolvenode(node0), 'look up node at rev 0'),
1333 (lambda: resolvenode(node0), 'look up node at rev 0'),
1302 (lambda: resolvenode(node25), 'look up node at 1/4 len'),
1334 (lambda: resolvenode(node25), 'look up node at 1/4 len'),
1303 (lambda: resolvenode(node50), 'look up node at 1/2 len'),
1335 (lambda: resolvenode(node50), 'look up node at 1/2 len'),
1304 (lambda: resolvenode(node75), 'look up node at 3/4 len'),
1336 (lambda: resolvenode(node75), 'look up node at 3/4 len'),
1305 (lambda: resolvenode(node100), 'look up node at tip'),
1337 (lambda: resolvenode(node100), 'look up node at tip'),
1306 # 2x variation is to measure caching impact.
1338 # 2x variation is to measure caching impact.
1307 (lambda: resolvenodes(allnodes),
1339 (lambda: resolvenodes(allnodes),
1308 'look up all nodes (forward)'),
1340 'look up all nodes (forward)'),
1309 (lambda: resolvenodes(allnodes, 2),
1341 (lambda: resolvenodes(allnodes, 2),
1310 'look up all nodes 2x (forward)'),
1342 'look up all nodes 2x (forward)'),
1311 (lambda: resolvenodes(allnodesrev),
1343 (lambda: resolvenodes(allnodesrev),
1312 'look up all nodes (reverse)'),
1344 'look up all nodes (reverse)'),
1313 (lambda: resolvenodes(allnodesrev, 2),
1345 (lambda: resolvenodes(allnodesrev, 2),
1314 'look up all nodes 2x (reverse)'),
1346 'look up all nodes 2x (reverse)'),
1315 (lambda: getentries(allrevs),
1347 (lambda: getentries(allrevs),
1316 'retrieve all index entries (forward)'),
1348 'retrieve all index entries (forward)'),
1317 (lambda: getentries(allrevs, 2),
1349 (lambda: getentries(allrevs, 2),
1318 'retrieve all index entries 2x (forward)'),
1350 'retrieve all index entries 2x (forward)'),
1319 (lambda: getentries(allrevsrev),
1351 (lambda: getentries(allrevsrev),
1320 'retrieve all index entries (reverse)'),
1352 'retrieve all index entries (reverse)'),
1321 (lambda: getentries(allrevsrev, 2),
1353 (lambda: getentries(allrevsrev, 2),
1322 'retrieve all index entries 2x (reverse)'),
1354 'retrieve all index entries 2x (reverse)'),
1323 ]
1355 ]
1324
1356
1325 for fn, title in benches:
1357 for fn, title in benches:
1326 timer, fm = gettimer(ui, opts)
1358 timer, fm = gettimer(ui, opts)
1327 timer(fn, title=title)
1359 timer(fn, title=title)
1328 fm.end()
1360 fm.end()
1329
1361
1330 @command('perfrevlogrevisions', revlogopts + formatteropts +
1362 @command('perfrevlogrevisions', revlogopts + formatteropts +
1331 [('d', 'dist', 100, 'distance between the revisions'),
1363 [('d', 'dist', 100, 'distance between the revisions'),
1332 ('s', 'startrev', 0, 'revision to start reading at'),
1364 ('s', 'startrev', 0, 'revision to start reading at'),
1333 ('', 'reverse', False, 'read in reverse')],
1365 ('', 'reverse', False, 'read in reverse')],
1334 '-c|-m|FILE')
1366 '-c|-m|FILE')
1335 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1367 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1336 **opts):
1368 **opts):
1337 """Benchmark reading a series of revisions from a revlog.
1369 """Benchmark reading a series of revisions from a revlog.
1338
1370
1339 By default, we read every ``-d/--dist`` revision from 0 to tip of
1371 By default, we read every ``-d/--dist`` revision from 0 to tip of
1340 the specified revlog.
1372 the specified revlog.
1341
1373
1342 The start revision can be defined via ``-s/--startrev``.
1374 The start revision can be defined via ``-s/--startrev``.
1343 """
1375 """
1344 rl = cmdutil.openrevlog(repo, 'perfrevlogrevisions', file_, opts)
1376 rl = cmdutil.openrevlog(repo, 'perfrevlogrevisions', file_, opts)
1345 rllen = getlen(ui)(rl)
1377 rllen = getlen(ui)(rl)
1346
1378
1347 def d():
1379 def d():
1348 rl.clearcaches()
1380 rl.clearcaches()
1349
1381
1350 beginrev = startrev
1382 beginrev = startrev
1351 endrev = rllen
1383 endrev = rllen
1352 dist = opts['dist']
1384 dist = opts['dist']
1353
1385
1354 if reverse:
1386 if reverse:
1355 beginrev, endrev = endrev, beginrev
1387 beginrev, endrev = endrev, beginrev
1356 dist = -1 * dist
1388 dist = -1 * dist
1357
1389
1358 for x in xrange(beginrev, endrev, dist):
1390 for x in xrange(beginrev, endrev, dist):
1359 # Old revisions don't support passing int.
1391 # Old revisions don't support passing int.
1360 n = rl.node(x)
1392 n = rl.node(x)
1361 rl.revision(n)
1393 rl.revision(n)
1362
1394
1363 timer, fm = gettimer(ui, opts)
1395 timer, fm = gettimer(ui, opts)
1364 timer(d)
1396 timer(d)
1365 fm.end()
1397 fm.end()
1366
1398
1367 @command('perfrevlogchunks', revlogopts + formatteropts +
1399 @command('perfrevlogchunks', revlogopts + formatteropts +
1368 [('e', 'engines', '', 'compression engines to use'),
1400 [('e', 'engines', '', 'compression engines to use'),
1369 ('s', 'startrev', 0, 'revision to start at')],
1401 ('s', 'startrev', 0, 'revision to start at')],
1370 '-c|-m|FILE')
1402 '-c|-m|FILE')
1371 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1403 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1372 """Benchmark operations on revlog chunks.
1404 """Benchmark operations on revlog chunks.
1373
1405
1374 Logically, each revlog is a collection of fulltext revisions. However,
1406 Logically, each revlog is a collection of fulltext revisions. However,
1375 stored within each revlog are "chunks" of possibly compressed data. This
1407 stored within each revlog are "chunks" of possibly compressed data. This
1376 data needs to be read and decompressed or compressed and written.
1408 data needs to be read and decompressed or compressed and written.
1377
1409
1378 This command measures the time it takes to read+decompress and recompress
1410 This command measures the time it takes to read+decompress and recompress
1379 chunks in a revlog. It effectively isolates I/O and compression performance.
1411 chunks in a revlog. It effectively isolates I/O and compression performance.
1380 For measurements of higher-level operations like resolving revisions,
1412 For measurements of higher-level operations like resolving revisions,
1381 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1413 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1382 """
1414 """
1383 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
1415 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
1384
1416
1385 # _chunkraw was renamed to _getsegmentforrevs.
1417 # _chunkraw was renamed to _getsegmentforrevs.
1386 try:
1418 try:
1387 segmentforrevs = rl._getsegmentforrevs
1419 segmentforrevs = rl._getsegmentforrevs
1388 except AttributeError:
1420 except AttributeError:
1389 segmentforrevs = rl._chunkraw
1421 segmentforrevs = rl._chunkraw
1390
1422
1391 # Verify engines argument.
1423 # Verify engines argument.
1392 if engines:
1424 if engines:
1393 engines = set(e.strip() for e in engines.split(','))
1425 engines = set(e.strip() for e in engines.split(','))
1394 for engine in engines:
1426 for engine in engines:
1395 try:
1427 try:
1396 util.compressionengines[engine]
1428 util.compressionengines[engine]
1397 except KeyError:
1429 except KeyError:
1398 raise error.Abort('unknown compression engine: %s' % engine)
1430 raise error.Abort('unknown compression engine: %s' % engine)
1399 else:
1431 else:
1400 engines = []
1432 engines = []
1401 for e in util.compengines:
1433 for e in util.compengines:
1402 engine = util.compengines[e]
1434 engine = util.compengines[e]
1403 try:
1435 try:
1404 if engine.available():
1436 if engine.available():
1405 engine.revlogcompressor().compress('dummy')
1437 engine.revlogcompressor().compress('dummy')
1406 engines.append(e)
1438 engines.append(e)
1407 except NotImplementedError:
1439 except NotImplementedError:
1408 pass
1440 pass
1409
1441
1410 revs = list(rl.revs(startrev, len(rl) - 1))
1442 revs = list(rl.revs(startrev, len(rl) - 1))
1411
1443
1412 def rlfh(rl):
1444 def rlfh(rl):
1413 if rl._inline:
1445 if rl._inline:
1414 return getsvfs(repo)(rl.indexfile)
1446 return getsvfs(repo)(rl.indexfile)
1415 else:
1447 else:
1416 return getsvfs(repo)(rl.datafile)
1448 return getsvfs(repo)(rl.datafile)
1417
1449
1418 def doread():
1450 def doread():
1419 rl.clearcaches()
1451 rl.clearcaches()
1420 for rev in revs:
1452 for rev in revs:
1421 segmentforrevs(rev, rev)
1453 segmentforrevs(rev, rev)
1422
1454
1423 def doreadcachedfh():
1455 def doreadcachedfh():
1424 rl.clearcaches()
1456 rl.clearcaches()
1425 fh = rlfh(rl)
1457 fh = rlfh(rl)
1426 for rev in revs:
1458 for rev in revs:
1427 segmentforrevs(rev, rev, df=fh)
1459 segmentforrevs(rev, rev, df=fh)
1428
1460
1429 def doreadbatch():
1461 def doreadbatch():
1430 rl.clearcaches()
1462 rl.clearcaches()
1431 segmentforrevs(revs[0], revs[-1])
1463 segmentforrevs(revs[0], revs[-1])
1432
1464
1433 def doreadbatchcachedfh():
1465 def doreadbatchcachedfh():
1434 rl.clearcaches()
1466 rl.clearcaches()
1435 fh = rlfh(rl)
1467 fh = rlfh(rl)
1436 segmentforrevs(revs[0], revs[-1], df=fh)
1468 segmentforrevs(revs[0], revs[-1], df=fh)
1437
1469
1438 def dochunk():
1470 def dochunk():
1439 rl.clearcaches()
1471 rl.clearcaches()
1440 fh = rlfh(rl)
1472 fh = rlfh(rl)
1441 for rev in revs:
1473 for rev in revs:
1442 rl._chunk(rev, df=fh)
1474 rl._chunk(rev, df=fh)
1443
1475
1444 chunks = [None]
1476 chunks = [None]
1445
1477
1446 def dochunkbatch():
1478 def dochunkbatch():
1447 rl.clearcaches()
1479 rl.clearcaches()
1448 fh = rlfh(rl)
1480 fh = rlfh(rl)
1449 # Save chunks as a side-effect.
1481 # Save chunks as a side-effect.
1450 chunks[0] = rl._chunks(revs, df=fh)
1482 chunks[0] = rl._chunks(revs, df=fh)
1451
1483
1452 def docompress(compressor):
1484 def docompress(compressor):
1453 rl.clearcaches()
1485 rl.clearcaches()
1454
1486
1455 try:
1487 try:
1456 # Swap in the requested compression engine.
1488 # Swap in the requested compression engine.
1457 oldcompressor = rl._compressor
1489 oldcompressor = rl._compressor
1458 rl._compressor = compressor
1490 rl._compressor = compressor
1459 for chunk in chunks[0]:
1491 for chunk in chunks[0]:
1460 rl.compress(chunk)
1492 rl.compress(chunk)
1461 finally:
1493 finally:
1462 rl._compressor = oldcompressor
1494 rl._compressor = oldcompressor
1463
1495
1464 benches = [
1496 benches = [
1465 (lambda: doread(), 'read'),
1497 (lambda: doread(), 'read'),
1466 (lambda: doreadcachedfh(), 'read w/ reused fd'),
1498 (lambda: doreadcachedfh(), 'read w/ reused fd'),
1467 (lambda: doreadbatch(), 'read batch'),
1499 (lambda: doreadbatch(), 'read batch'),
1468 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
1500 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
1469 (lambda: dochunk(), 'chunk'),
1501 (lambda: dochunk(), 'chunk'),
1470 (lambda: dochunkbatch(), 'chunk batch'),
1502 (lambda: dochunkbatch(), 'chunk batch'),
1471 ]
1503 ]
1472
1504
1473 for engine in sorted(engines):
1505 for engine in sorted(engines):
1474 compressor = util.compengines[engine].revlogcompressor()
1506 compressor = util.compengines[engine].revlogcompressor()
1475 benches.append((functools.partial(docompress, compressor),
1507 benches.append((functools.partial(docompress, compressor),
1476 'compress w/ %s' % engine))
1508 'compress w/ %s' % engine))
1477
1509
1478 for fn, title in benches:
1510 for fn, title in benches:
1479 timer, fm = gettimer(ui, opts)
1511 timer, fm = gettimer(ui, opts)
1480 timer(fn, title=title)
1512 timer(fn, title=title)
1481 fm.end()
1513 fm.end()
1482
1514
1483 @command('perfrevlogrevision', revlogopts + formatteropts +
1515 @command('perfrevlogrevision', revlogopts + formatteropts +
1484 [('', 'cache', False, 'use caches instead of clearing')],
1516 [('', 'cache', False, 'use caches instead of clearing')],
1485 '-c|-m|FILE REV')
1517 '-c|-m|FILE REV')
1486 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1518 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1487 """Benchmark obtaining a revlog revision.
1519 """Benchmark obtaining a revlog revision.
1488
1520
1489 Obtaining a revlog revision consists of roughly the following steps:
1521 Obtaining a revlog revision consists of roughly the following steps:
1490
1522
1491 1. Compute the delta chain
1523 1. Compute the delta chain
1492 2. Obtain the raw chunks for that delta chain
1524 2. Obtain the raw chunks for that delta chain
1493 3. Decompress each raw chunk
1525 3. Decompress each raw chunk
1494 4. Apply binary patches to obtain fulltext
1526 4. Apply binary patches to obtain fulltext
1495 5. Verify hash of fulltext
1527 5. Verify hash of fulltext
1496
1528
1497 This command measures the time spent in each of these phases.
1529 This command measures the time spent in each of these phases.
1498 """
1530 """
1499 if opts.get('changelog') or opts.get('manifest'):
1531 if opts.get('changelog') or opts.get('manifest'):
1500 file_, rev = None, file_
1532 file_, rev = None, file_
1501 elif rev is None:
1533 elif rev is None:
1502 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1534 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1503
1535
1504 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1536 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1505
1537
1506 # _chunkraw was renamed to _getsegmentforrevs.
1538 # _chunkraw was renamed to _getsegmentforrevs.
1507 try:
1539 try:
1508 segmentforrevs = r._getsegmentforrevs
1540 segmentforrevs = r._getsegmentforrevs
1509 except AttributeError:
1541 except AttributeError:
1510 segmentforrevs = r._chunkraw
1542 segmentforrevs = r._chunkraw
1511
1543
1512 node = r.lookup(rev)
1544 node = r.lookup(rev)
1513 rev = r.rev(node)
1545 rev = r.rev(node)
1514
1546
1515 def getrawchunks(data, chain):
1547 def getrawchunks(data, chain):
1516 start = r.start
1548 start = r.start
1517 length = r.length
1549 length = r.length
1518 inline = r._inline
1550 inline = r._inline
1519 iosize = r._io.size
1551 iosize = r._io.size
1520 buffer = util.buffer
1552 buffer = util.buffer
1521 offset = start(chain[0])
1553 offset = start(chain[0])
1522
1554
1523 chunks = []
1555 chunks = []
1524 ladd = chunks.append
1556 ladd = chunks.append
1525
1557
1526 for rev in chain:
1558 for rev in chain:
1527 chunkstart = start(rev)
1559 chunkstart = start(rev)
1528 if inline:
1560 if inline:
1529 chunkstart += (rev + 1) * iosize
1561 chunkstart += (rev + 1) * iosize
1530 chunklength = length(rev)
1562 chunklength = length(rev)
1531 ladd(buffer(data, chunkstart - offset, chunklength))
1563 ladd(buffer(data, chunkstart - offset, chunklength))
1532
1564
1533 return chunks
1565 return chunks
1534
1566
1535 def dodeltachain(rev):
1567 def dodeltachain(rev):
1536 if not cache:
1568 if not cache:
1537 r.clearcaches()
1569 r.clearcaches()
1538 r._deltachain(rev)
1570 r._deltachain(rev)
1539
1571
1540 def doread(chain):
1572 def doread(chain):
1541 if not cache:
1573 if not cache:
1542 r.clearcaches()
1574 r.clearcaches()
1543 segmentforrevs(chain[0], chain[-1])
1575 segmentforrevs(chain[0], chain[-1])
1544
1576
1545 def dorawchunks(data, chain):
1577 def dorawchunks(data, chain):
1546 if not cache:
1578 if not cache:
1547 r.clearcaches()
1579 r.clearcaches()
1548 getrawchunks(data, chain)
1580 getrawchunks(data, chain)
1549
1581
1550 def dodecompress(chunks):
1582 def dodecompress(chunks):
1551 decomp = r.decompress
1583 decomp = r.decompress
1552 for chunk in chunks:
1584 for chunk in chunks:
1553 decomp(chunk)
1585 decomp(chunk)
1554
1586
1555 def dopatch(text, bins):
1587 def dopatch(text, bins):
1556 if not cache:
1588 if not cache:
1557 r.clearcaches()
1589 r.clearcaches()
1558 mdiff.patches(text, bins)
1590 mdiff.patches(text, bins)
1559
1591
1560 def dohash(text):
1592 def dohash(text):
1561 if not cache:
1593 if not cache:
1562 r.clearcaches()
1594 r.clearcaches()
1563 r.checkhash(text, node, rev=rev)
1595 r.checkhash(text, node, rev=rev)
1564
1596
1565 def dorevision():
1597 def dorevision():
1566 if not cache:
1598 if not cache:
1567 r.clearcaches()
1599 r.clearcaches()
1568 r.revision(node)
1600 r.revision(node)
1569
1601
1570 chain = r._deltachain(rev)[0]
1602 chain = r._deltachain(rev)[0]
1571 data = segmentforrevs(chain[0], chain[-1])[1]
1603 data = segmentforrevs(chain[0], chain[-1])[1]
1572 rawchunks = getrawchunks(data, chain)
1604 rawchunks = getrawchunks(data, chain)
1573 bins = r._chunks(chain)
1605 bins = r._chunks(chain)
1574 text = str(bins[0])
1606 text = str(bins[0])
1575 bins = bins[1:]
1607 bins = bins[1:]
1576 text = mdiff.patches(text, bins)
1608 text = mdiff.patches(text, bins)
1577
1609
1578 benches = [
1610 benches = [
1579 (lambda: dorevision(), 'full'),
1611 (lambda: dorevision(), 'full'),
1580 (lambda: dodeltachain(rev), 'deltachain'),
1612 (lambda: dodeltachain(rev), 'deltachain'),
1581 (lambda: doread(chain), 'read'),
1613 (lambda: doread(chain), 'read'),
1582 (lambda: dorawchunks(data, chain), 'rawchunks'),
1614 (lambda: dorawchunks(data, chain), 'rawchunks'),
1583 (lambda: dodecompress(rawchunks), 'decompress'),
1615 (lambda: dodecompress(rawchunks), 'decompress'),
1584 (lambda: dopatch(text, bins), 'patch'),
1616 (lambda: dopatch(text, bins), 'patch'),
1585 (lambda: dohash(text), 'hash'),
1617 (lambda: dohash(text), 'hash'),
1586 ]
1618 ]
1587
1619
1588 for fn, title in benches:
1620 for fn, title in benches:
1589 timer, fm = gettimer(ui, opts)
1621 timer, fm = gettimer(ui, opts)
1590 timer(fn, title=title)
1622 timer(fn, title=title)
1591 fm.end()
1623 fm.end()
1592
1624
1593 @command('perfrevset',
1625 @command('perfrevset',
1594 [('C', 'clear', False, 'clear volatile cache between each call.'),
1626 [('C', 'clear', False, 'clear volatile cache between each call.'),
1595 ('', 'contexts', False, 'obtain changectx for each revision')]
1627 ('', 'contexts', False, 'obtain changectx for each revision')]
1596 + formatteropts, "REVSET")
1628 + formatteropts, "REVSET")
1597 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1629 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1598 """benchmark the execution time of a revset
1630 """benchmark the execution time of a revset
1599
1631
1600 Use the --clean option if need to evaluate the impact of build volatile
1632 Use the --clean option if need to evaluate the impact of build volatile
1601 revisions set cache on the revset execution. Volatile cache hold filtered
1633 revisions set cache on the revset execution. Volatile cache hold filtered
1602 and obsolete related cache."""
1634 and obsolete related cache."""
1603 timer, fm = gettimer(ui, opts)
1635 timer, fm = gettimer(ui, opts)
1604 def d():
1636 def d():
1605 if clear:
1637 if clear:
1606 repo.invalidatevolatilesets()
1638 repo.invalidatevolatilesets()
1607 if contexts:
1639 if contexts:
1608 for ctx in repo.set(expr): pass
1640 for ctx in repo.set(expr): pass
1609 else:
1641 else:
1610 for r in repo.revs(expr): pass
1642 for r in repo.revs(expr): pass
1611 timer(d)
1643 timer(d)
1612 fm.end()
1644 fm.end()
1613
1645
1614 @command('perfvolatilesets',
1646 @command('perfvolatilesets',
1615 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1647 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1616 ] + formatteropts)
1648 ] + formatteropts)
1617 def perfvolatilesets(ui, repo, *names, **opts):
1649 def perfvolatilesets(ui, repo, *names, **opts):
1618 """benchmark the computation of various volatile set
1650 """benchmark the computation of various volatile set
1619
1651
1620 Volatile set computes element related to filtering and obsolescence."""
1652 Volatile set computes element related to filtering and obsolescence."""
1621 timer, fm = gettimer(ui, opts)
1653 timer, fm = gettimer(ui, opts)
1622 repo = repo.unfiltered()
1654 repo = repo.unfiltered()
1623
1655
1624 def getobs(name):
1656 def getobs(name):
1625 def d():
1657 def d():
1626 repo.invalidatevolatilesets()
1658 repo.invalidatevolatilesets()
1627 if opts['clear_obsstore']:
1659 if opts['clear_obsstore']:
1628 clearfilecache(repo, 'obsstore')
1660 clearfilecache(repo, 'obsstore')
1629 obsolete.getrevs(repo, name)
1661 obsolete.getrevs(repo, name)
1630 return d
1662 return d
1631
1663
1632 allobs = sorted(obsolete.cachefuncs)
1664 allobs = sorted(obsolete.cachefuncs)
1633 if names:
1665 if names:
1634 allobs = [n for n in allobs if n in names]
1666 allobs = [n for n in allobs if n in names]
1635
1667
1636 for name in allobs:
1668 for name in allobs:
1637 timer(getobs(name), title=name)
1669 timer(getobs(name), title=name)
1638
1670
1639 def getfiltered(name):
1671 def getfiltered(name):
1640 def d():
1672 def d():
1641 repo.invalidatevolatilesets()
1673 repo.invalidatevolatilesets()
1642 if opts['clear_obsstore']:
1674 if opts['clear_obsstore']:
1643 clearfilecache(repo, 'obsstore')
1675 clearfilecache(repo, 'obsstore')
1644 repoview.filterrevs(repo, name)
1676 repoview.filterrevs(repo, name)
1645 return d
1677 return d
1646
1678
1647 allfilter = sorted(repoview.filtertable)
1679 allfilter = sorted(repoview.filtertable)
1648 if names:
1680 if names:
1649 allfilter = [n for n in allfilter if n in names]
1681 allfilter = [n for n in allfilter if n in names]
1650
1682
1651 for name in allfilter:
1683 for name in allfilter:
1652 timer(getfiltered(name), title=name)
1684 timer(getfiltered(name), title=name)
1653 fm.end()
1685 fm.end()
1654
1686
1655 @command('perfbranchmap',
1687 @command('perfbranchmap',
1656 [('f', 'full', False,
1688 [('f', 'full', False,
1657 'Includes build time of subset'),
1689 'Includes build time of subset'),
1658 ('', 'clear-revbranch', False,
1690 ('', 'clear-revbranch', False,
1659 'purge the revbranch cache between computation'),
1691 'purge the revbranch cache between computation'),
1660 ] + formatteropts)
1692 ] + formatteropts)
1661 def perfbranchmap(ui, repo, *filternames, **opts):
1693 def perfbranchmap(ui, repo, *filternames, **opts):
1662 """benchmark the update of a branchmap
1694 """benchmark the update of a branchmap
1663
1695
1664 This benchmarks the full repo.branchmap() call with read and write disabled
1696 This benchmarks the full repo.branchmap() call with read and write disabled
1665 """
1697 """
1666 full = opts.get("full", False)
1698 full = opts.get("full", False)
1667 clear_revbranch = opts.get("clear_revbranch", False)
1699 clear_revbranch = opts.get("clear_revbranch", False)
1668 timer, fm = gettimer(ui, opts)
1700 timer, fm = gettimer(ui, opts)
1669 def getbranchmap(filtername):
1701 def getbranchmap(filtername):
1670 """generate a benchmark function for the filtername"""
1702 """generate a benchmark function for the filtername"""
1671 if filtername is None:
1703 if filtername is None:
1672 view = repo
1704 view = repo
1673 else:
1705 else:
1674 view = repo.filtered(filtername)
1706 view = repo.filtered(filtername)
1675 def d():
1707 def d():
1676 if clear_revbranch:
1708 if clear_revbranch:
1677 repo.revbranchcache()._clear()
1709 repo.revbranchcache()._clear()
1678 if full:
1710 if full:
1679 view._branchcaches.clear()
1711 view._branchcaches.clear()
1680 else:
1712 else:
1681 view._branchcaches.pop(filtername, None)
1713 view._branchcaches.pop(filtername, None)
1682 view.branchmap()
1714 view.branchmap()
1683 return d
1715 return d
1684 # add filter in smaller subset to bigger subset
1716 # add filter in smaller subset to bigger subset
1685 possiblefilters = set(repoview.filtertable)
1717 possiblefilters = set(repoview.filtertable)
1686 if filternames:
1718 if filternames:
1687 possiblefilters &= set(filternames)
1719 possiblefilters &= set(filternames)
1688 subsettable = getbranchmapsubsettable()
1720 subsettable = getbranchmapsubsettable()
1689 allfilters = []
1721 allfilters = []
1690 while possiblefilters:
1722 while possiblefilters:
1691 for name in possiblefilters:
1723 for name in possiblefilters:
1692 subset = subsettable.get(name)
1724 subset = subsettable.get(name)
1693 if subset not in possiblefilters:
1725 if subset not in possiblefilters:
1694 break
1726 break
1695 else:
1727 else:
1696 assert False, 'subset cycle %s!' % possiblefilters
1728 assert False, 'subset cycle %s!' % possiblefilters
1697 allfilters.append(name)
1729 allfilters.append(name)
1698 possiblefilters.remove(name)
1730 possiblefilters.remove(name)
1699
1731
1700 # warm the cache
1732 # warm the cache
1701 if not full:
1733 if not full:
1702 for name in allfilters:
1734 for name in allfilters:
1703 repo.filtered(name).branchmap()
1735 repo.filtered(name).branchmap()
1704 if not filternames or 'unfiltered' in filternames:
1736 if not filternames or 'unfiltered' in filternames:
1705 # add unfiltered
1737 # add unfiltered
1706 allfilters.append(None)
1738 allfilters.append(None)
1707
1739
1708 branchcacheread = safeattrsetter(branchmap, 'read')
1740 branchcacheread = safeattrsetter(branchmap, 'read')
1709 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1741 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1710 branchcacheread.set(lambda repo: None)
1742 branchcacheread.set(lambda repo: None)
1711 branchcachewrite.set(lambda bc, repo: None)
1743 branchcachewrite.set(lambda bc, repo: None)
1712 try:
1744 try:
1713 for name in allfilters:
1745 for name in allfilters:
1714 printname = name
1746 printname = name
1715 if name is None:
1747 if name is None:
1716 printname = 'unfiltered'
1748 printname = 'unfiltered'
1717 timer(getbranchmap(name), title=str(printname))
1749 timer(getbranchmap(name), title=str(printname))
1718 finally:
1750 finally:
1719 branchcacheread.restore()
1751 branchcacheread.restore()
1720 branchcachewrite.restore()
1752 branchcachewrite.restore()
1721 fm.end()
1753 fm.end()
1722
1754
1723 @command('perfloadmarkers')
1755 @command('perfloadmarkers')
1724 def perfloadmarkers(ui, repo):
1756 def perfloadmarkers(ui, repo):
1725 """benchmark the time to parse the on-disk markers for a repo
1757 """benchmark the time to parse the on-disk markers for a repo
1726
1758
1727 Result is the number of markers in the repo."""
1759 Result is the number of markers in the repo."""
1728 timer, fm = gettimer(ui)
1760 timer, fm = gettimer(ui)
1729 svfs = getsvfs(repo)
1761 svfs = getsvfs(repo)
1730 timer(lambda: len(obsolete.obsstore(svfs)))
1762 timer(lambda: len(obsolete.obsstore(svfs)))
1731 fm.end()
1763 fm.end()
1732
1764
1733 @command('perflrucachedict', formatteropts +
1765 @command('perflrucachedict', formatteropts +
1734 [('', 'size', 4, 'size of cache'),
1766 [('', 'size', 4, 'size of cache'),
1735 ('', 'gets', 10000, 'number of key lookups'),
1767 ('', 'gets', 10000, 'number of key lookups'),
1736 ('', 'sets', 10000, 'number of key sets'),
1768 ('', 'sets', 10000, 'number of key sets'),
1737 ('', 'mixed', 10000, 'number of mixed mode operations'),
1769 ('', 'mixed', 10000, 'number of mixed mode operations'),
1738 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1770 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1739 norepo=True)
1771 norepo=True)
1740 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1772 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1741 mixedgetfreq=50, **opts):
1773 mixedgetfreq=50, **opts):
1742 def doinit():
1774 def doinit():
1743 for i in xrange(10000):
1775 for i in xrange(10000):
1744 util.lrucachedict(size)
1776 util.lrucachedict(size)
1745
1777
1746 values = []
1778 values = []
1747 for i in xrange(size):
1779 for i in xrange(size):
1748 values.append(random.randint(0, sys.maxint))
1780 values.append(random.randint(0, sys.maxint))
1749
1781
1750 # Get mode fills the cache and tests raw lookup performance with no
1782 # Get mode fills the cache and tests raw lookup performance with no
1751 # eviction.
1783 # eviction.
1752 getseq = []
1784 getseq = []
1753 for i in xrange(gets):
1785 for i in xrange(gets):
1754 getseq.append(random.choice(values))
1786 getseq.append(random.choice(values))
1755
1787
1756 def dogets():
1788 def dogets():
1757 d = util.lrucachedict(size)
1789 d = util.lrucachedict(size)
1758 for v in values:
1790 for v in values:
1759 d[v] = v
1791 d[v] = v
1760 for key in getseq:
1792 for key in getseq:
1761 value = d[key]
1793 value = d[key]
1762 value # silence pyflakes warning
1794 value # silence pyflakes warning
1763
1795
1764 # Set mode tests insertion speed with cache eviction.
1796 # Set mode tests insertion speed with cache eviction.
1765 setseq = []
1797 setseq = []
1766 for i in xrange(sets):
1798 for i in xrange(sets):
1767 setseq.append(random.randint(0, sys.maxint))
1799 setseq.append(random.randint(0, sys.maxint))
1768
1800
1769 def dosets():
1801 def dosets():
1770 d = util.lrucachedict(size)
1802 d = util.lrucachedict(size)
1771 for v in setseq:
1803 for v in setseq:
1772 d[v] = v
1804 d[v] = v
1773
1805
1774 # Mixed mode randomly performs gets and sets with eviction.
1806 # Mixed mode randomly performs gets and sets with eviction.
1775 mixedops = []
1807 mixedops = []
1776 for i in xrange(mixed):
1808 for i in xrange(mixed):
1777 r = random.randint(0, 100)
1809 r = random.randint(0, 100)
1778 if r < mixedgetfreq:
1810 if r < mixedgetfreq:
1779 op = 0
1811 op = 0
1780 else:
1812 else:
1781 op = 1
1813 op = 1
1782
1814
1783 mixedops.append((op, random.randint(0, size * 2)))
1815 mixedops.append((op, random.randint(0, size * 2)))
1784
1816
1785 def domixed():
1817 def domixed():
1786 d = util.lrucachedict(size)
1818 d = util.lrucachedict(size)
1787
1819
1788 for op, v in mixedops:
1820 for op, v in mixedops:
1789 if op == 0:
1821 if op == 0:
1790 try:
1822 try:
1791 d[v]
1823 d[v]
1792 except KeyError:
1824 except KeyError:
1793 pass
1825 pass
1794 else:
1826 else:
1795 d[v] = v
1827 d[v] = v
1796
1828
1797 benches = [
1829 benches = [
1798 (doinit, 'init'),
1830 (doinit, 'init'),
1799 (dogets, 'gets'),
1831 (dogets, 'gets'),
1800 (dosets, 'sets'),
1832 (dosets, 'sets'),
1801 (domixed, 'mixed')
1833 (domixed, 'mixed')
1802 ]
1834 ]
1803
1835
1804 for fn, title in benches:
1836 for fn, title in benches:
1805 timer, fm = gettimer(ui, opts)
1837 timer, fm = gettimer(ui, opts)
1806 timer(fn, title=title)
1838 timer(fn, title=title)
1807 fm.end()
1839 fm.end()
1808
1840
1809 @command('perfwrite', formatteropts)
1841 @command('perfwrite', formatteropts)
1810 def perfwrite(ui, repo, **opts):
1842 def perfwrite(ui, repo, **opts):
1811 """microbenchmark ui.write
1843 """microbenchmark ui.write
1812 """
1844 """
1813 timer, fm = gettimer(ui, opts)
1845 timer, fm = gettimer(ui, opts)
1814 def write():
1846 def write():
1815 for i in range(100000):
1847 for i in range(100000):
1816 ui.write(('Testing write performance\n'))
1848 ui.write(('Testing write performance\n'))
1817 timer(write)
1849 timer(write)
1818 fm.end()
1850 fm.end()
1819
1851
1820 def uisetup(ui):
1852 def uisetup(ui):
1821 if (util.safehasattr(cmdutil, 'openrevlog') and
1853 if (util.safehasattr(cmdutil, 'openrevlog') and
1822 not util.safehasattr(commands, 'debugrevlogopts')):
1854 not util.safehasattr(commands, 'debugrevlogopts')):
1823 # for "historical portability":
1855 # for "historical portability":
1824 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1856 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1825 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1857 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1826 # openrevlog() should cause failure, because it has been
1858 # openrevlog() should cause failure, because it has been
1827 # available since 3.5 (or 49c583ca48c4).
1859 # available since 3.5 (or 49c583ca48c4).
1828 def openrevlog(orig, repo, cmd, file_, opts):
1860 def openrevlog(orig, repo, cmd, file_, opts):
1829 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1861 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1830 raise error.Abort("This version doesn't support --dir option",
1862 raise error.Abort("This version doesn't support --dir option",
1831 hint="use 3.5 or later")
1863 hint="use 3.5 or later")
1832 return orig(repo, cmd, file_, opts)
1864 return orig(repo, cmd, file_, opts)
1833 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1865 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
@@ -1,210 +1,213 b''
1 #require test-repo
1 #require test-repo
2
2
3 Set vars:
3 Set vars:
4
4
5 $ . "$TESTDIR/helpers-testrepo.sh"
5 $ . "$TESTDIR/helpers-testrepo.sh"
6 $ CONTRIBDIR="$TESTDIR/../contrib"
6 $ CONTRIBDIR="$TESTDIR/../contrib"
7
7
8 Prepare repo:
8 Prepare repo:
9
9
10 $ hg init
10 $ hg init
11
11
12 $ echo this is file a > a
12 $ echo this is file a > a
13 $ hg add a
13 $ hg add a
14 $ hg commit -m first
14 $ hg commit -m first
15
15
16 $ echo adding to file a >> a
16 $ echo adding to file a >> a
17 $ hg commit -m second
17 $ hg commit -m second
18
18
19 $ echo adding more to file a >> a
19 $ echo adding more to file a >> a
20 $ hg commit -m third
20 $ hg commit -m third
21
21
22 $ hg up -r 0
22 $ hg up -r 0
23 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 $ echo merge-this >> a
24 $ echo merge-this >> a
25 $ hg commit -m merge-able
25 $ hg commit -m merge-able
26 created new head
26 created new head
27
27
28 $ hg up -r 2
28 $ hg up -r 2
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30
30
31 perfstatus
31 perfstatus
32
32
33 $ cat >> $HGRCPATH << EOF
33 $ cat >> $HGRCPATH << EOF
34 > [extensions]
34 > [extensions]
35 > perfstatusext=$CONTRIBDIR/perf.py
35 > perfstatusext=$CONTRIBDIR/perf.py
36 > [perf]
36 > [perf]
37 > presleep=0
37 > presleep=0
38 > stub=on
38 > stub=on
39 > parentscount=1
39 > parentscount=1
40 > EOF
40 > EOF
41 $ hg help perfstatusext
41 $ hg help perfstatusext
42 perfstatusext extension - helper extension to measure performance
42 perfstatusext extension - helper extension to measure performance
43
43
44 list of commands:
44 list of commands:
45
45
46 perfaddremove
46 perfaddremove
47 (no help text available)
47 (no help text available)
48 perfancestors
48 perfancestors
49 (no help text available)
49 (no help text available)
50 perfancestorset
50 perfancestorset
51 (no help text available)
51 (no help text available)
52 perfannotate (no help text available)
52 perfannotate (no help text available)
53 perfbdiff benchmark a bdiff between revisions
53 perfbdiff benchmark a bdiff between revisions
54 perfbookmarks
54 perfbookmarks
55 benchmark parsing bookmarks from disk to memory
55 benchmark parsing bookmarks from disk to memory
56 perfbranchmap
56 perfbranchmap
57 benchmark the update of a branchmap
57 benchmark the update of a branchmap
58 perfbundleread
58 perfbundleread
59 Benchmark reading of bundle files.
59 Benchmark reading of bundle files.
60 perfcca (no help text available)
60 perfcca (no help text available)
61 perfchangegroupchangelog
61 perfchangegroupchangelog
62 Benchmark producing a changelog group for a changegroup.
62 Benchmark producing a changelog group for a changegroup.
63 perfchangeset
63 perfchangeset
64 (no help text available)
64 (no help text available)
65 perfctxfiles (no help text available)
65 perfctxfiles (no help text available)
66 perfdiffwd Profile diff of working directory changes
66 perfdiffwd Profile diff of working directory changes
67 perfdirfoldmap
67 perfdirfoldmap
68 (no help text available)
68 (no help text available)
69 perfdirs (no help text available)
69 perfdirs (no help text available)
70 perfdirstate (no help text available)
70 perfdirstate (no help text available)
71 perfdirstatedirs
71 perfdirstatedirs
72 (no help text available)
72 (no help text available)
73 perfdirstatefoldmap
73 perfdirstatefoldmap
74 (no help text available)
74 (no help text available)
75 perfdirstatewrite
75 perfdirstatewrite
76 (no help text available)
76 (no help text available)
77 perffncacheencode
77 perffncacheencode
78 (no help text available)
78 (no help text available)
79 perffncacheload
79 perffncacheload
80 (no help text available)
80 (no help text available)
81 perffncachewrite
81 perffncachewrite
82 (no help text available)
82 (no help text available)
83 perfheads (no help text available)
83 perfheads (no help text available)
84 perfindex (no help text available)
84 perfindex (no help text available)
85 perflinelogedits
86 (no help text available)
85 perfloadmarkers
87 perfloadmarkers
86 benchmark the time to parse the on-disk markers for a repo
88 benchmark the time to parse the on-disk markers for a repo
87 perflog (no help text available)
89 perflog (no help text available)
88 perflookup (no help text available)
90 perflookup (no help text available)
89 perflrucachedict
91 perflrucachedict
90 (no help text available)
92 (no help text available)
91 perfmanifest benchmark the time to read a manifest from disk and return a
93 perfmanifest benchmark the time to read a manifest from disk and return a
92 usable
94 usable
93 perfmergecalculate
95 perfmergecalculate
94 (no help text available)
96 (no help text available)
95 perfmoonwalk benchmark walking the changelog backwards
97 perfmoonwalk benchmark walking the changelog backwards
96 perfnodelookup
98 perfnodelookup
97 (no help text available)
99 (no help text available)
98 perfparents (no help text available)
100 perfparents (no help text available)
99 perfpathcopies
101 perfpathcopies
100 (no help text available)
102 (no help text available)
101 perfphases benchmark phasesets computation
103 perfphases benchmark phasesets computation
102 perfrawfiles (no help text available)
104 perfrawfiles (no help text available)
103 perfrevlogchunks
105 perfrevlogchunks
104 Benchmark operations on revlog chunks.
106 Benchmark operations on revlog chunks.
105 perfrevlogindex
107 perfrevlogindex
106 Benchmark operations against a revlog index.
108 Benchmark operations against a revlog index.
107 perfrevlogrevision
109 perfrevlogrevision
108 Benchmark obtaining a revlog revision.
110 Benchmark obtaining a revlog revision.
109 perfrevlogrevisions
111 perfrevlogrevisions
110 Benchmark reading a series of revisions from a revlog.
112 Benchmark reading a series of revisions from a revlog.
111 perfrevrange (no help text available)
113 perfrevrange (no help text available)
112 perfrevset benchmark the execution time of a revset
114 perfrevset benchmark the execution time of a revset
113 perfstartup (no help text available)
115 perfstartup (no help text available)
114 perfstatus (no help text available)
116 perfstatus (no help text available)
115 perftags (no help text available)
117 perftags (no help text available)
116 perftemplating
118 perftemplating
117 test the rendering time of a given template
119 test the rendering time of a given template
118 perfunidiff benchmark a unified diff between revisions
120 perfunidiff benchmark a unified diff between revisions
119 perfvolatilesets
121 perfvolatilesets
120 benchmark the computation of various volatile set
122 benchmark the computation of various volatile set
121 perfwalk (no help text available)
123 perfwalk (no help text available)
122 perfwrite microbenchmark ui.write
124 perfwrite microbenchmark ui.write
123
125
124 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
126 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
125 $ hg perfaddremove
127 $ hg perfaddremove
126 $ hg perfancestors
128 $ hg perfancestors
127 $ hg perfancestorset 2
129 $ hg perfancestorset 2
128 $ hg perfannotate a
130 $ hg perfannotate a
129 $ hg perfbdiff -c 1
131 $ hg perfbdiff -c 1
130 $ hg perfbdiff --alldata 1
132 $ hg perfbdiff --alldata 1
131 $ hg perfunidiff -c 1
133 $ hg perfunidiff -c 1
132 $ hg perfunidiff --alldata 1
134 $ hg perfunidiff --alldata 1
133 $ hg perfbookmarks
135 $ hg perfbookmarks
134 $ hg perfbranchmap
136 $ hg perfbranchmap
135 $ hg perfcca
137 $ hg perfcca
136 $ hg perfchangegroupchangelog
138 $ hg perfchangegroupchangelog
137 $ hg perfchangeset 2
139 $ hg perfchangeset 2
138 $ hg perfctxfiles 2
140 $ hg perfctxfiles 2
139 $ hg perfdiffwd
141 $ hg perfdiffwd
140 $ hg perfdirfoldmap
142 $ hg perfdirfoldmap
141 $ hg perfdirs
143 $ hg perfdirs
142 $ hg perfdirstate
144 $ hg perfdirstate
143 $ hg perfdirstatedirs
145 $ hg perfdirstatedirs
144 $ hg perfdirstatefoldmap
146 $ hg perfdirstatefoldmap
145 $ hg perfdirstatewrite
147 $ hg perfdirstatewrite
146 #if repofncache
148 #if repofncache
147 $ hg perffncacheencode
149 $ hg perffncacheencode
148 $ hg perffncacheload
150 $ hg perffncacheload
149 $ hg debugrebuildfncache
151 $ hg debugrebuildfncache
150 fncache already up to date
152 fncache already up to date
151 $ hg perffncachewrite
153 $ hg perffncachewrite
152 $ hg debugrebuildfncache
154 $ hg debugrebuildfncache
153 fncache already up to date
155 fncache already up to date
154 #endif
156 #endif
155 $ hg perfheads
157 $ hg perfheads
156 $ hg perfindex
158 $ hg perfindex
159 $ hg perflinelogedits -n 1
157 $ hg perfloadmarkers
160 $ hg perfloadmarkers
158 $ hg perflog
161 $ hg perflog
159 $ hg perflookup 2
162 $ hg perflookup 2
160 $ hg perflrucache
163 $ hg perflrucache
161 $ hg perfmanifest 2
164 $ hg perfmanifest 2
162 $ hg perfmergecalculate -r 3
165 $ hg perfmergecalculate -r 3
163 $ hg perfmoonwalk
166 $ hg perfmoonwalk
164 $ hg perfnodelookup 2
167 $ hg perfnodelookup 2
165 $ hg perfpathcopies 1 2
168 $ hg perfpathcopies 1 2
166 $ hg perfrawfiles 2
169 $ hg perfrawfiles 2
167 $ hg perfrevlogindex -c
170 $ hg perfrevlogindex -c
168 #if reporevlogstore
171 #if reporevlogstore
169 $ hg perfrevlogrevisions .hg/store/data/a.i
172 $ hg perfrevlogrevisions .hg/store/data/a.i
170 #endif
173 #endif
171 $ hg perfrevlogrevision -m 0
174 $ hg perfrevlogrevision -m 0
172 $ hg perfrevlogchunks -c
175 $ hg perfrevlogchunks -c
173 $ hg perfrevrange
176 $ hg perfrevrange
174 $ hg perfrevset 'all()'
177 $ hg perfrevset 'all()'
175 $ hg perfstartup
178 $ hg perfstartup
176 $ hg perfstatus
179 $ hg perfstatus
177 $ hg perftags
180 $ hg perftags
178 $ hg perftemplating
181 $ hg perftemplating
179 $ hg perfvolatilesets
182 $ hg perfvolatilesets
180 $ hg perfwalk
183 $ hg perfwalk
181 $ hg perfparents
184 $ hg perfparents
182
185
183 test actual output
186 test actual output
184 ------------------
187 ------------------
185
188
186 normal output:
189 normal output:
187
190
188 $ hg perfheads --config perf.stub=no
191 $ hg perfheads --config perf.stub=no
189 ! wall * comb * user * sys * (best of *) (glob)
192 ! wall * comb * user * sys * (best of *) (glob)
190
193
191 detailed output:
194 detailed output:
192
195
193 $ hg perfheads --config perf.all-timing=yes --config perf.stub=no
196 $ hg perfheads --config perf.all-timing=yes --config perf.stub=no
194 ! wall * comb * user * sys * (best of *) (glob)
197 ! wall * comb * user * sys * (best of *) (glob)
195 ! wall * comb * user * sys * (max of *) (glob)
198 ! wall * comb * user * sys * (max of *) (glob)
196 ! wall * comb * user * sys * (avg of *) (glob)
199 ! wall * comb * user * sys * (avg of *) (glob)
197 ! wall * comb * user * sys * (median of *) (glob)
200 ! wall * comb * user * sys * (median of *) (glob)
198
201
199 Check perf.py for historical portability
202 Check perf.py for historical portability
200 ----------------------------------------
203 ----------------------------------------
201
204
202 $ cd "$TESTDIR/.."
205 $ cd "$TESTDIR/.."
203
206
204 $ (testrepohg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
207 $ (testrepohg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
205 > testrepohg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
208 > testrepohg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
206 > "$TESTDIR"/check-perf-code.py contrib/perf.py
209 > "$TESTDIR"/check-perf-code.py contrib/perf.py
207 contrib/perf.py:\d+: (re)
210 contrib/perf.py:\d+: (re)
208 > from mercurial import (
211 > from mercurial import (
209 import newer module separately in try clause for early Mercurial
212 import newer module separately in try clause for early Mercurial
210 [1]
213 [1]
General Comments 0
You need to be logged in to leave comments. Login now