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