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