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