##// END OF EJS Templates
contrib: add a write microbenchmark to perf.py...
Simon Farnsworth -
r30977:5a9e4dc8 default
parent child Browse files
Show More
@@ -1,1285 +1,1296 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 os
23 import os
24 import random
24 import random
25 import sys
25 import sys
26 import time
26 import time
27 from mercurial import (
27 from mercurial import (
28 bdiff,
28 bdiff,
29 changegroup,
29 changegroup,
30 cmdutil,
30 cmdutil,
31 commands,
31 commands,
32 copies,
32 copies,
33 error,
33 error,
34 extensions,
34 extensions,
35 mdiff,
35 mdiff,
36 merge,
36 merge,
37 util,
37 util,
38 )
38 )
39
39
40 # for "historical portability":
40 # for "historical portability":
41 # try to import modules separately (in dict order), and ignore
41 # try to import modules separately (in dict order), and ignore
42 # failure, because these aren't available with early Mercurial
42 # failure, because these aren't available with early Mercurial
43 try:
43 try:
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
45 except ImportError:
45 except ImportError:
46 pass
46 pass
47 try:
47 try:
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
49 except ImportError:
49 except ImportError:
50 pass
50 pass
51 try:
51 try:
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
53 except ImportError:
53 except ImportError:
54 pass
54 pass
55 try:
55 try:
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
57 except ImportError:
57 except ImportError:
58 pass
58 pass
59
59
60 # for "historical portability":
60 # for "historical portability":
61 # define util.safehasattr forcibly, because util.safehasattr has been
61 # define util.safehasattr forcibly, because util.safehasattr has been
62 # available since 1.9.3 (or 94b200a11cf7)
62 # available since 1.9.3 (or 94b200a11cf7)
63 _undefined = object()
63 _undefined = object()
64 def safehasattr(thing, attr):
64 def safehasattr(thing, attr):
65 return getattr(thing, attr, _undefined) is not _undefined
65 return getattr(thing, attr, _undefined) is not _undefined
66 setattr(util, 'safehasattr', safehasattr)
66 setattr(util, 'safehasattr', safehasattr)
67
67
68 # for "historical portability":
68 # for "historical portability":
69 # use locally defined empty option list, if formatteropts isn't
69 # use locally defined empty option list, if formatteropts isn't
70 # available, because commands.formatteropts has been available since
70 # available, because commands.formatteropts has been available since
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
72 # available since 2.2 (or ae5f92e154d3)
72 # available since 2.2 (or ae5f92e154d3)
73 formatteropts = getattr(commands, "formatteropts", [])
73 formatteropts = getattr(commands, "formatteropts", [])
74
74
75 # for "historical portability":
75 # for "historical portability":
76 # use locally defined option list, if debugrevlogopts isn't available,
76 # use locally defined option list, if debugrevlogopts isn't available,
77 # because commands.debugrevlogopts has been available since 3.7 (or
77 # because commands.debugrevlogopts has been available since 3.7 (or
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
79 # since 1.9 (or a79fea6b3e77).
79 # since 1.9 (or a79fea6b3e77).
80 revlogopts = getattr(commands, "debugrevlogopts", [
80 revlogopts = getattr(commands, "debugrevlogopts", [
81 ('c', 'changelog', False, ('open changelog')),
81 ('c', 'changelog', False, ('open changelog')),
82 ('m', 'manifest', False, ('open manifest')),
82 ('m', 'manifest', False, ('open manifest')),
83 ('', 'dir', False, ('open directory manifest')),
83 ('', 'dir', False, ('open directory manifest')),
84 ])
84 ])
85
85
86 cmdtable = {}
86 cmdtable = {}
87
87
88 # for "historical portability":
88 # for "historical portability":
89 # define parsealiases locally, because cmdutil.parsealiases has been
89 # define parsealiases locally, because cmdutil.parsealiases has been
90 # available since 1.5 (or 6252852b4332)
90 # available since 1.5 (or 6252852b4332)
91 def parsealiases(cmd):
91 def parsealiases(cmd):
92 return cmd.lstrip("^").split("|")
92 return cmd.lstrip("^").split("|")
93
93
94 if safehasattr(cmdutil, 'command'):
94 if safehasattr(cmdutil, 'command'):
95 import inspect
95 import inspect
96 command = cmdutil.command(cmdtable)
96 command = cmdutil.command(cmdtable)
97 if 'norepo' not in inspect.getargspec(command)[0]:
97 if 'norepo' not in inspect.getargspec(command)[0]:
98 # for "historical portability":
98 # for "historical portability":
99 # wrap original cmdutil.command, because "norepo" option has
99 # wrap original cmdutil.command, because "norepo" option has
100 # been available since 3.1 (or 75a96326cecb)
100 # been available since 3.1 (or 75a96326cecb)
101 _command = command
101 _command = command
102 def command(name, options=(), synopsis=None, norepo=False):
102 def command(name, options=(), synopsis=None, norepo=False):
103 if norepo:
103 if norepo:
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
105 return _command(name, list(options), synopsis)
105 return _command(name, list(options), synopsis)
106 else:
106 else:
107 # for "historical portability":
107 # for "historical portability":
108 # define "@command" annotation locally, because cmdutil.command
108 # define "@command" annotation locally, because cmdutil.command
109 # has been available since 1.9 (or 2daa5179e73f)
109 # has been available since 1.9 (or 2daa5179e73f)
110 def command(name, options=(), synopsis=None, norepo=False):
110 def command(name, options=(), synopsis=None, norepo=False):
111 def decorator(func):
111 def decorator(func):
112 if synopsis:
112 if synopsis:
113 cmdtable[name] = func, list(options), synopsis
113 cmdtable[name] = func, list(options), synopsis
114 else:
114 else:
115 cmdtable[name] = func, list(options)
115 cmdtable[name] = func, list(options)
116 if norepo:
116 if norepo:
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
118 return func
118 return func
119 return decorator
119 return decorator
120
120
121 def getlen(ui):
121 def getlen(ui):
122 if ui.configbool("perf", "stub"):
122 if ui.configbool("perf", "stub"):
123 return lambda x: 1
123 return lambda x: 1
124 return len
124 return len
125
125
126 def gettimer(ui, opts=None):
126 def gettimer(ui, opts=None):
127 """return a timer function and formatter: (timer, formatter)
127 """return a timer function and formatter: (timer, formatter)
128
128
129 This function exists to gather the creation of formatter in a single
129 This function exists to gather the creation of formatter in a single
130 place instead of duplicating it in all performance commands."""
130 place instead of duplicating it in all performance commands."""
131
131
132 # enforce an idle period before execution to counteract power management
132 # enforce an idle period before execution to counteract power management
133 # experimental config: perf.presleep
133 # experimental config: perf.presleep
134 time.sleep(getint(ui, "perf", "presleep", 1))
134 time.sleep(getint(ui, "perf", "presleep", 1))
135
135
136 if opts is None:
136 if opts is None:
137 opts = {}
137 opts = {}
138 # redirect all to stderr unless buffer api is in use
138 # redirect all to stderr unless buffer api is in use
139 if not ui._buffers:
139 if not ui._buffers:
140 ui = ui.copy()
140 ui = ui.copy()
141 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
141 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
142 if uifout:
142 if uifout:
143 # for "historical portability":
143 # for "historical portability":
144 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
144 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
145 uifout.set(ui.ferr)
145 uifout.set(ui.ferr)
146
146
147 # get a formatter
147 # get a formatter
148 uiformatter = getattr(ui, 'formatter', None)
148 uiformatter = getattr(ui, 'formatter', None)
149 if uiformatter:
149 if uiformatter:
150 fm = uiformatter('perf', opts)
150 fm = uiformatter('perf', opts)
151 else:
151 else:
152 # for "historical portability":
152 # for "historical portability":
153 # define formatter locally, because ui.formatter has been
153 # define formatter locally, because ui.formatter has been
154 # available since 2.2 (or ae5f92e154d3)
154 # available since 2.2 (or ae5f92e154d3)
155 from mercurial import node
155 from mercurial import node
156 class defaultformatter(object):
156 class defaultformatter(object):
157 """Minimized composition of baseformatter and plainformatter
157 """Minimized composition of baseformatter and plainformatter
158 """
158 """
159 def __init__(self, ui, topic, opts):
159 def __init__(self, ui, topic, opts):
160 self._ui = ui
160 self._ui = ui
161 if ui.debugflag:
161 if ui.debugflag:
162 self.hexfunc = node.hex
162 self.hexfunc = node.hex
163 else:
163 else:
164 self.hexfunc = node.short
164 self.hexfunc = node.short
165 def __nonzero__(self):
165 def __nonzero__(self):
166 return False
166 return False
167 def startitem(self):
167 def startitem(self):
168 pass
168 pass
169 def data(self, **data):
169 def data(self, **data):
170 pass
170 pass
171 def write(self, fields, deftext, *fielddata, **opts):
171 def write(self, fields, deftext, *fielddata, **opts):
172 self._ui.write(deftext % fielddata, **opts)
172 self._ui.write(deftext % fielddata, **opts)
173 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
173 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
174 if cond:
174 if cond:
175 self._ui.write(deftext % fielddata, **opts)
175 self._ui.write(deftext % fielddata, **opts)
176 def plain(self, text, **opts):
176 def plain(self, text, **opts):
177 self._ui.write(text, **opts)
177 self._ui.write(text, **opts)
178 def end(self):
178 def end(self):
179 pass
179 pass
180 fm = defaultformatter(ui, 'perf', opts)
180 fm = defaultformatter(ui, 'perf', opts)
181
181
182 # stub function, runs code only once instead of in a loop
182 # stub function, runs code only once instead of in a loop
183 # experimental config: perf.stub
183 # experimental config: perf.stub
184 if ui.configbool("perf", "stub"):
184 if ui.configbool("perf", "stub"):
185 return functools.partial(stub_timer, fm), fm
185 return functools.partial(stub_timer, fm), fm
186 return functools.partial(_timer, fm), fm
186 return functools.partial(_timer, fm), fm
187
187
188 def stub_timer(fm, func, title=None):
188 def stub_timer(fm, func, title=None):
189 func()
189 func()
190
190
191 def _timer(fm, func, title=None):
191 def _timer(fm, func, title=None):
192 results = []
192 results = []
193 begin = util.timer()
193 begin = util.timer()
194 count = 0
194 count = 0
195 while True:
195 while True:
196 ostart = os.times()
196 ostart = os.times()
197 cstart = util.timer()
197 cstart = util.timer()
198 r = func()
198 r = func()
199 cstop = util.timer()
199 cstop = util.timer()
200 ostop = os.times()
200 ostop = os.times()
201 count += 1
201 count += 1
202 a, b = ostart, ostop
202 a, b = ostart, ostop
203 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
203 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
204 if cstop - begin > 3 and count >= 100:
204 if cstop - begin > 3 and count >= 100:
205 break
205 break
206 if cstop - begin > 10 and count >= 3:
206 if cstop - begin > 10 and count >= 3:
207 break
207 break
208
208
209 fm.startitem()
209 fm.startitem()
210
210
211 if title:
211 if title:
212 fm.write('title', '! %s\n', title)
212 fm.write('title', '! %s\n', title)
213 if r:
213 if r:
214 fm.write('result', '! result: %s\n', r)
214 fm.write('result', '! result: %s\n', r)
215 m = min(results)
215 m = min(results)
216 fm.plain('!')
216 fm.plain('!')
217 fm.write('wall', ' wall %f', m[0])
217 fm.write('wall', ' wall %f', m[0])
218 fm.write('comb', ' comb %f', m[1] + m[2])
218 fm.write('comb', ' comb %f', m[1] + m[2])
219 fm.write('user', ' user %f', m[1])
219 fm.write('user', ' user %f', m[1])
220 fm.write('sys', ' sys %f', m[2])
220 fm.write('sys', ' sys %f', m[2])
221 fm.write('count', ' (best of %d)', count)
221 fm.write('count', ' (best of %d)', count)
222 fm.plain('\n')
222 fm.plain('\n')
223
223
224 # utilities for historical portability
224 # utilities for historical portability
225
225
226 def getint(ui, section, name, default):
226 def getint(ui, section, name, default):
227 # for "historical portability":
227 # for "historical portability":
228 # ui.configint has been available since 1.9 (or fa2b596db182)
228 # ui.configint has been available since 1.9 (or fa2b596db182)
229 v = ui.config(section, name, None)
229 v = ui.config(section, name, None)
230 if v is None:
230 if v is None:
231 return default
231 return default
232 try:
232 try:
233 return int(v)
233 return int(v)
234 except ValueError:
234 except ValueError:
235 raise error.ConfigError(("%s.%s is not an integer ('%s')")
235 raise error.ConfigError(("%s.%s is not an integer ('%s')")
236 % (section, name, v))
236 % (section, name, v))
237
237
238 def safeattrsetter(obj, name, ignoremissing=False):
238 def safeattrsetter(obj, name, ignoremissing=False):
239 """Ensure that 'obj' has 'name' attribute before subsequent setattr
239 """Ensure that 'obj' has 'name' attribute before subsequent setattr
240
240
241 This function is aborted, if 'obj' doesn't have 'name' attribute
241 This function is aborted, if 'obj' doesn't have 'name' attribute
242 at runtime. This avoids overlooking removal of an attribute, which
242 at runtime. This avoids overlooking removal of an attribute, which
243 breaks assumption of performance measurement, in the future.
243 breaks assumption of performance measurement, in the future.
244
244
245 This function returns the object to (1) assign a new value, and
245 This function returns the object to (1) assign a new value, and
246 (2) restore an original value to the attribute.
246 (2) restore an original value to the attribute.
247
247
248 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
248 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
249 abortion, and this function returns None. This is useful to
249 abortion, and this function returns None. This is useful to
250 examine an attribute, which isn't ensured in all Mercurial
250 examine an attribute, which isn't ensured in all Mercurial
251 versions.
251 versions.
252 """
252 """
253 if not util.safehasattr(obj, name):
253 if not util.safehasattr(obj, name):
254 if ignoremissing:
254 if ignoremissing:
255 return None
255 return None
256 raise error.Abort(("missing attribute %s of %s might break assumption"
256 raise error.Abort(("missing attribute %s of %s might break assumption"
257 " of performance measurement") % (name, obj))
257 " of performance measurement") % (name, obj))
258
258
259 origvalue = getattr(obj, name)
259 origvalue = getattr(obj, name)
260 class attrutil(object):
260 class attrutil(object):
261 def set(self, newvalue):
261 def set(self, newvalue):
262 setattr(obj, name, newvalue)
262 setattr(obj, name, newvalue)
263 def restore(self):
263 def restore(self):
264 setattr(obj, name, origvalue)
264 setattr(obj, name, origvalue)
265
265
266 return attrutil()
266 return attrutil()
267
267
268 # utilities to examine each internal API changes
268 # utilities to examine each internal API changes
269
269
270 def getbranchmapsubsettable():
270 def getbranchmapsubsettable():
271 # for "historical portability":
271 # for "historical portability":
272 # subsettable is defined in:
272 # subsettable is defined in:
273 # - branchmap since 2.9 (or 175c6fd8cacc)
273 # - branchmap since 2.9 (or 175c6fd8cacc)
274 # - repoview since 2.5 (or 59a9f18d4587)
274 # - repoview since 2.5 (or 59a9f18d4587)
275 for mod in (branchmap, repoview):
275 for mod in (branchmap, repoview):
276 subsettable = getattr(mod, 'subsettable', None)
276 subsettable = getattr(mod, 'subsettable', None)
277 if subsettable:
277 if subsettable:
278 return subsettable
278 return subsettable
279
279
280 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
280 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
281 # branchmap and repoview modules exist, but subsettable attribute
281 # branchmap and repoview modules exist, but subsettable attribute
282 # doesn't)
282 # doesn't)
283 raise error.Abort(("perfbranchmap not available with this Mercurial"),
283 raise error.Abort(("perfbranchmap not available with this Mercurial"),
284 hint="use 2.5 or later")
284 hint="use 2.5 or later")
285
285
286 def getsvfs(repo):
286 def getsvfs(repo):
287 """Return appropriate object to access files under .hg/store
287 """Return appropriate object to access files under .hg/store
288 """
288 """
289 # for "historical portability":
289 # for "historical portability":
290 # repo.svfs has been available since 2.3 (or 7034365089bf)
290 # repo.svfs has been available since 2.3 (or 7034365089bf)
291 svfs = getattr(repo, 'svfs', None)
291 svfs = getattr(repo, 'svfs', None)
292 if svfs:
292 if svfs:
293 return svfs
293 return svfs
294 else:
294 else:
295 return getattr(repo, 'sopener')
295 return getattr(repo, 'sopener')
296
296
297 def getvfs(repo):
297 def getvfs(repo):
298 """Return appropriate object to access files under .hg
298 """Return appropriate object to access files under .hg
299 """
299 """
300 # for "historical portability":
300 # for "historical portability":
301 # repo.vfs has been available since 2.3 (or 7034365089bf)
301 # repo.vfs has been available since 2.3 (or 7034365089bf)
302 vfs = getattr(repo, 'vfs', None)
302 vfs = getattr(repo, 'vfs', None)
303 if vfs:
303 if vfs:
304 return vfs
304 return vfs
305 else:
305 else:
306 return getattr(repo, 'opener')
306 return getattr(repo, 'opener')
307
307
308 def repocleartagscachefunc(repo):
308 def repocleartagscachefunc(repo):
309 """Return the function to clear tags cache according to repo internal API
309 """Return the function to clear tags cache according to repo internal API
310 """
310 """
311 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
311 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
312 # in this case, setattr(repo, '_tagscache', None) or so isn't
312 # in this case, setattr(repo, '_tagscache', None) or so isn't
313 # correct way to clear tags cache, because existing code paths
313 # correct way to clear tags cache, because existing code paths
314 # expect _tagscache to be a structured object.
314 # expect _tagscache to be a structured object.
315 def clearcache():
315 def clearcache():
316 # _tagscache has been filteredpropertycache since 2.5 (or
316 # _tagscache has been filteredpropertycache since 2.5 (or
317 # 98c867ac1330), and delattr() can't work in such case
317 # 98c867ac1330), and delattr() can't work in such case
318 if '_tagscache' in vars(repo):
318 if '_tagscache' in vars(repo):
319 del repo.__dict__['_tagscache']
319 del repo.__dict__['_tagscache']
320 return clearcache
320 return clearcache
321
321
322 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
322 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
323 if repotags: # since 1.4 (or 5614a628d173)
323 if repotags: # since 1.4 (or 5614a628d173)
324 return lambda : repotags.set(None)
324 return lambda : repotags.set(None)
325
325
326 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
326 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
327 if repotagscache: # since 0.6 (or d7df759d0e97)
327 if repotagscache: # since 0.6 (or d7df759d0e97)
328 return lambda : repotagscache.set(None)
328 return lambda : repotagscache.set(None)
329
329
330 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
330 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
331 # this point, but it isn't so problematic, because:
331 # this point, but it isn't so problematic, because:
332 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
332 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
333 # in perftags() causes failure soon
333 # in perftags() causes failure soon
334 # - perf.py itself has been available since 1.1 (or eb240755386d)
334 # - perf.py itself has been available since 1.1 (or eb240755386d)
335 raise error.Abort(("tags API of this hg command is unknown"))
335 raise error.Abort(("tags API of this hg command is unknown"))
336
336
337 # perf commands
337 # perf commands
338
338
339 @command('perfwalk', formatteropts)
339 @command('perfwalk', formatteropts)
340 def perfwalk(ui, repo, *pats, **opts):
340 def perfwalk(ui, repo, *pats, **opts):
341 timer, fm = gettimer(ui, opts)
341 timer, fm = gettimer(ui, opts)
342 try:
342 try:
343 m = scmutil.match(repo[None], pats, {})
343 m = scmutil.match(repo[None], pats, {})
344 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
344 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
345 except Exception:
345 except Exception:
346 try:
346 try:
347 m = scmutil.match(repo[None], pats, {})
347 m = scmutil.match(repo[None], pats, {})
348 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
348 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
349 except Exception:
349 except Exception:
350 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
350 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
351 fm.end()
351 fm.end()
352
352
353 @command('perfannotate', formatteropts)
353 @command('perfannotate', formatteropts)
354 def perfannotate(ui, repo, f, **opts):
354 def perfannotate(ui, repo, f, **opts):
355 timer, fm = gettimer(ui, opts)
355 timer, fm = gettimer(ui, opts)
356 fc = repo['.'][f]
356 fc = repo['.'][f]
357 timer(lambda: len(fc.annotate(True)))
357 timer(lambda: len(fc.annotate(True)))
358 fm.end()
358 fm.end()
359
359
360 @command('perfstatus',
360 @command('perfstatus',
361 [('u', 'unknown', False,
361 [('u', 'unknown', False,
362 'ask status to look for unknown files')] + formatteropts)
362 'ask status to look for unknown files')] + formatteropts)
363 def perfstatus(ui, repo, **opts):
363 def perfstatus(ui, repo, **opts):
364 #m = match.always(repo.root, repo.getcwd())
364 #m = match.always(repo.root, repo.getcwd())
365 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
365 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
366 # False))))
366 # False))))
367 timer, fm = gettimer(ui, opts)
367 timer, fm = gettimer(ui, opts)
368 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
368 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
369 fm.end()
369 fm.end()
370
370
371 @command('perfaddremove', formatteropts)
371 @command('perfaddremove', formatteropts)
372 def perfaddremove(ui, repo, **opts):
372 def perfaddremove(ui, repo, **opts):
373 timer, fm = gettimer(ui, opts)
373 timer, fm = gettimer(ui, opts)
374 try:
374 try:
375 oldquiet = repo.ui.quiet
375 oldquiet = repo.ui.quiet
376 repo.ui.quiet = True
376 repo.ui.quiet = True
377 matcher = scmutil.match(repo[None])
377 matcher = scmutil.match(repo[None])
378 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
378 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
379 finally:
379 finally:
380 repo.ui.quiet = oldquiet
380 repo.ui.quiet = oldquiet
381 fm.end()
381 fm.end()
382
382
383 def clearcaches(cl):
383 def clearcaches(cl):
384 # behave somewhat consistently across internal API changes
384 # behave somewhat consistently across internal API changes
385 if util.safehasattr(cl, 'clearcaches'):
385 if util.safehasattr(cl, 'clearcaches'):
386 cl.clearcaches()
386 cl.clearcaches()
387 elif util.safehasattr(cl, '_nodecache'):
387 elif util.safehasattr(cl, '_nodecache'):
388 from mercurial.node import nullid, nullrev
388 from mercurial.node import nullid, nullrev
389 cl._nodecache = {nullid: nullrev}
389 cl._nodecache = {nullid: nullrev}
390 cl._nodepos = None
390 cl._nodepos = None
391
391
392 @command('perfheads', formatteropts)
392 @command('perfheads', formatteropts)
393 def perfheads(ui, repo, **opts):
393 def perfheads(ui, repo, **opts):
394 timer, fm = gettimer(ui, opts)
394 timer, fm = gettimer(ui, opts)
395 cl = repo.changelog
395 cl = repo.changelog
396 def d():
396 def d():
397 len(cl.headrevs())
397 len(cl.headrevs())
398 clearcaches(cl)
398 clearcaches(cl)
399 timer(d)
399 timer(d)
400 fm.end()
400 fm.end()
401
401
402 @command('perftags', formatteropts)
402 @command('perftags', formatteropts)
403 def perftags(ui, repo, **opts):
403 def perftags(ui, repo, **opts):
404 import mercurial.changelog
404 import mercurial.changelog
405 import mercurial.manifest
405 import mercurial.manifest
406 timer, fm = gettimer(ui, opts)
406 timer, fm = gettimer(ui, opts)
407 svfs = getsvfs(repo)
407 svfs = getsvfs(repo)
408 repocleartagscache = repocleartagscachefunc(repo)
408 repocleartagscache = repocleartagscachefunc(repo)
409 def t():
409 def t():
410 repo.changelog = mercurial.changelog.changelog(svfs)
410 repo.changelog = mercurial.changelog.changelog(svfs)
411 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
411 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
412 repocleartagscache()
412 repocleartagscache()
413 return len(repo.tags())
413 return len(repo.tags())
414 timer(t)
414 timer(t)
415 fm.end()
415 fm.end()
416
416
417 @command('perfancestors', formatteropts)
417 @command('perfancestors', formatteropts)
418 def perfancestors(ui, repo, **opts):
418 def perfancestors(ui, repo, **opts):
419 timer, fm = gettimer(ui, opts)
419 timer, fm = gettimer(ui, opts)
420 heads = repo.changelog.headrevs()
420 heads = repo.changelog.headrevs()
421 def d():
421 def d():
422 for a in repo.changelog.ancestors(heads):
422 for a in repo.changelog.ancestors(heads):
423 pass
423 pass
424 timer(d)
424 timer(d)
425 fm.end()
425 fm.end()
426
426
427 @command('perfancestorset', formatteropts)
427 @command('perfancestorset', formatteropts)
428 def perfancestorset(ui, repo, revset, **opts):
428 def perfancestorset(ui, repo, revset, **opts):
429 timer, fm = gettimer(ui, opts)
429 timer, fm = gettimer(ui, opts)
430 revs = repo.revs(revset)
430 revs = repo.revs(revset)
431 heads = repo.changelog.headrevs()
431 heads = repo.changelog.headrevs()
432 def d():
432 def d():
433 s = repo.changelog.ancestors(heads)
433 s = repo.changelog.ancestors(heads)
434 for rev in revs:
434 for rev in revs:
435 rev in s
435 rev in s
436 timer(d)
436 timer(d)
437 fm.end()
437 fm.end()
438
438
439 @command('perfchangegroupchangelog', formatteropts +
439 @command('perfchangegroupchangelog', formatteropts +
440 [('', 'version', '02', 'changegroup version'),
440 [('', 'version', '02', 'changegroup version'),
441 ('r', 'rev', '', 'revisions to add to changegroup')])
441 ('r', 'rev', '', 'revisions to add to changegroup')])
442 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
442 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
443 """Benchmark producing a changelog group for a changegroup.
443 """Benchmark producing a changelog group for a changegroup.
444
444
445 This measures the time spent processing the changelog during a
445 This measures the time spent processing the changelog during a
446 bundle operation. This occurs during `hg bundle` and on a server
446 bundle operation. This occurs during `hg bundle` and on a server
447 processing a `getbundle` wire protocol request (handles clones
447 processing a `getbundle` wire protocol request (handles clones
448 and pull requests).
448 and pull requests).
449
449
450 By default, all revisions are added to the changegroup.
450 By default, all revisions are added to the changegroup.
451 """
451 """
452 cl = repo.changelog
452 cl = repo.changelog
453 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
453 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
454 bundler = changegroup.getbundler(version, repo)
454 bundler = changegroup.getbundler(version, repo)
455
455
456 def lookup(node):
456 def lookup(node):
457 # The real bundler reads the revision in order to access the
457 # The real bundler reads the revision in order to access the
458 # manifest node and files list. Do that here.
458 # manifest node and files list. Do that here.
459 cl.read(node)
459 cl.read(node)
460 return node
460 return node
461
461
462 def d():
462 def d():
463 for chunk in bundler.group(revs, cl, lookup):
463 for chunk in bundler.group(revs, cl, lookup):
464 pass
464 pass
465
465
466 timer, fm = gettimer(ui, opts)
466 timer, fm = gettimer(ui, opts)
467 timer(d)
467 timer(d)
468 fm.end()
468 fm.end()
469
469
470 @command('perfdirs', formatteropts)
470 @command('perfdirs', formatteropts)
471 def perfdirs(ui, repo, **opts):
471 def perfdirs(ui, repo, **opts):
472 timer, fm = gettimer(ui, opts)
472 timer, fm = gettimer(ui, opts)
473 dirstate = repo.dirstate
473 dirstate = repo.dirstate
474 'a' in dirstate
474 'a' in dirstate
475 def d():
475 def d():
476 dirstate.dirs()
476 dirstate.dirs()
477 del dirstate._dirs
477 del dirstate._dirs
478 timer(d)
478 timer(d)
479 fm.end()
479 fm.end()
480
480
481 @command('perfdirstate', formatteropts)
481 @command('perfdirstate', formatteropts)
482 def perfdirstate(ui, repo, **opts):
482 def perfdirstate(ui, repo, **opts):
483 timer, fm = gettimer(ui, opts)
483 timer, fm = gettimer(ui, opts)
484 "a" in repo.dirstate
484 "a" in repo.dirstate
485 def d():
485 def d():
486 repo.dirstate.invalidate()
486 repo.dirstate.invalidate()
487 "a" in repo.dirstate
487 "a" in repo.dirstate
488 timer(d)
488 timer(d)
489 fm.end()
489 fm.end()
490
490
491 @command('perfdirstatedirs', formatteropts)
491 @command('perfdirstatedirs', formatteropts)
492 def perfdirstatedirs(ui, repo, **opts):
492 def perfdirstatedirs(ui, repo, **opts):
493 timer, fm = gettimer(ui, opts)
493 timer, fm = gettimer(ui, opts)
494 "a" in repo.dirstate
494 "a" in repo.dirstate
495 def d():
495 def d():
496 "a" in repo.dirstate._dirs
496 "a" in repo.dirstate._dirs
497 del repo.dirstate._dirs
497 del repo.dirstate._dirs
498 timer(d)
498 timer(d)
499 fm.end()
499 fm.end()
500
500
501 @command('perfdirstatefoldmap', formatteropts)
501 @command('perfdirstatefoldmap', formatteropts)
502 def perfdirstatefoldmap(ui, repo, **opts):
502 def perfdirstatefoldmap(ui, repo, **opts):
503 timer, fm = gettimer(ui, opts)
503 timer, fm = gettimer(ui, opts)
504 dirstate = repo.dirstate
504 dirstate = repo.dirstate
505 'a' in dirstate
505 'a' in dirstate
506 def d():
506 def d():
507 dirstate._filefoldmap.get('a')
507 dirstate._filefoldmap.get('a')
508 del dirstate._filefoldmap
508 del dirstate._filefoldmap
509 timer(d)
509 timer(d)
510 fm.end()
510 fm.end()
511
511
512 @command('perfdirfoldmap', formatteropts)
512 @command('perfdirfoldmap', formatteropts)
513 def perfdirfoldmap(ui, repo, **opts):
513 def perfdirfoldmap(ui, repo, **opts):
514 timer, fm = gettimer(ui, opts)
514 timer, fm = gettimer(ui, opts)
515 dirstate = repo.dirstate
515 dirstate = repo.dirstate
516 'a' in dirstate
516 'a' in dirstate
517 def d():
517 def d():
518 dirstate._dirfoldmap.get('a')
518 dirstate._dirfoldmap.get('a')
519 del dirstate._dirfoldmap
519 del dirstate._dirfoldmap
520 del dirstate._dirs
520 del dirstate._dirs
521 timer(d)
521 timer(d)
522 fm.end()
522 fm.end()
523
523
524 @command('perfdirstatewrite', formatteropts)
524 @command('perfdirstatewrite', formatteropts)
525 def perfdirstatewrite(ui, repo, **opts):
525 def perfdirstatewrite(ui, repo, **opts):
526 timer, fm = gettimer(ui, opts)
526 timer, fm = gettimer(ui, opts)
527 ds = repo.dirstate
527 ds = repo.dirstate
528 "a" in ds
528 "a" in ds
529 def d():
529 def d():
530 ds._dirty = True
530 ds._dirty = True
531 ds.write(repo.currenttransaction())
531 ds.write(repo.currenttransaction())
532 timer(d)
532 timer(d)
533 fm.end()
533 fm.end()
534
534
535 @command('perfmergecalculate',
535 @command('perfmergecalculate',
536 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
536 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
537 def perfmergecalculate(ui, repo, rev, **opts):
537 def perfmergecalculate(ui, repo, rev, **opts):
538 timer, fm = gettimer(ui, opts)
538 timer, fm = gettimer(ui, opts)
539 wctx = repo[None]
539 wctx = repo[None]
540 rctx = scmutil.revsingle(repo, rev, rev)
540 rctx = scmutil.revsingle(repo, rev, rev)
541 ancestor = wctx.ancestor(rctx)
541 ancestor = wctx.ancestor(rctx)
542 # we don't want working dir files to be stat'd in the benchmark, so prime
542 # we don't want working dir files to be stat'd in the benchmark, so prime
543 # that cache
543 # that cache
544 wctx.dirty()
544 wctx.dirty()
545 def d():
545 def d():
546 # acceptremote is True because we don't want prompts in the middle of
546 # acceptremote is True because we don't want prompts in the middle of
547 # our benchmark
547 # our benchmark
548 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
548 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
549 acceptremote=True, followcopies=True)
549 acceptremote=True, followcopies=True)
550 timer(d)
550 timer(d)
551 fm.end()
551 fm.end()
552
552
553 @command('perfpathcopies', [], "REV REV")
553 @command('perfpathcopies', [], "REV REV")
554 def perfpathcopies(ui, repo, rev1, rev2, **opts):
554 def perfpathcopies(ui, repo, rev1, rev2, **opts):
555 timer, fm = gettimer(ui, opts)
555 timer, fm = gettimer(ui, opts)
556 ctx1 = scmutil.revsingle(repo, rev1, rev1)
556 ctx1 = scmutil.revsingle(repo, rev1, rev1)
557 ctx2 = scmutil.revsingle(repo, rev2, rev2)
557 ctx2 = scmutil.revsingle(repo, rev2, rev2)
558 def d():
558 def d():
559 copies.pathcopies(ctx1, ctx2)
559 copies.pathcopies(ctx1, ctx2)
560 timer(d)
560 timer(d)
561 fm.end()
561 fm.end()
562
562
563 @command('perfmanifest', [], 'REV')
563 @command('perfmanifest', [], 'REV')
564 def perfmanifest(ui, repo, rev, **opts):
564 def perfmanifest(ui, repo, rev, **opts):
565 timer, fm = gettimer(ui, opts)
565 timer, fm = gettimer(ui, opts)
566 ctx = scmutil.revsingle(repo, rev, rev)
566 ctx = scmutil.revsingle(repo, rev, rev)
567 t = ctx.manifestnode()
567 t = ctx.manifestnode()
568 def d():
568 def d():
569 repo.manifestlog.clearcaches()
569 repo.manifestlog.clearcaches()
570 repo.manifestlog[t].read()
570 repo.manifestlog[t].read()
571 timer(d)
571 timer(d)
572 fm.end()
572 fm.end()
573
573
574 @command('perfchangeset', formatteropts)
574 @command('perfchangeset', formatteropts)
575 def perfchangeset(ui, repo, rev, **opts):
575 def perfchangeset(ui, repo, rev, **opts):
576 timer, fm = gettimer(ui, opts)
576 timer, fm = gettimer(ui, opts)
577 n = repo[rev].node()
577 n = repo[rev].node()
578 def d():
578 def d():
579 repo.changelog.read(n)
579 repo.changelog.read(n)
580 #repo.changelog._cache = None
580 #repo.changelog._cache = None
581 timer(d)
581 timer(d)
582 fm.end()
582 fm.end()
583
583
584 @command('perfindex', formatteropts)
584 @command('perfindex', formatteropts)
585 def perfindex(ui, repo, **opts):
585 def perfindex(ui, repo, **opts):
586 import mercurial.revlog
586 import mercurial.revlog
587 timer, fm = gettimer(ui, opts)
587 timer, fm = gettimer(ui, opts)
588 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
588 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
589 n = repo["tip"].node()
589 n = repo["tip"].node()
590 svfs = getsvfs(repo)
590 svfs = getsvfs(repo)
591 def d():
591 def d():
592 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
592 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
593 cl.rev(n)
593 cl.rev(n)
594 timer(d)
594 timer(d)
595 fm.end()
595 fm.end()
596
596
597 @command('perfstartup', formatteropts)
597 @command('perfstartup', formatteropts)
598 def perfstartup(ui, repo, **opts):
598 def perfstartup(ui, repo, **opts):
599 timer, fm = gettimer(ui, opts)
599 timer, fm = gettimer(ui, opts)
600 cmd = sys.argv[0]
600 cmd = sys.argv[0]
601 def d():
601 def d():
602 if os.name != 'nt':
602 if os.name != 'nt':
603 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
603 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
604 else:
604 else:
605 os.environ['HGRCPATH'] = ''
605 os.environ['HGRCPATH'] = ''
606 os.system("%s version -q > NUL" % cmd)
606 os.system("%s version -q > NUL" % cmd)
607 timer(d)
607 timer(d)
608 fm.end()
608 fm.end()
609
609
610 @command('perfparents', formatteropts)
610 @command('perfparents', formatteropts)
611 def perfparents(ui, repo, **opts):
611 def perfparents(ui, repo, **opts):
612 timer, fm = gettimer(ui, opts)
612 timer, fm = gettimer(ui, opts)
613 # control the number of commits perfparents iterates over
613 # control the number of commits perfparents iterates over
614 # experimental config: perf.parentscount
614 # experimental config: perf.parentscount
615 count = getint(ui, "perf", "parentscount", 1000)
615 count = getint(ui, "perf", "parentscount", 1000)
616 if len(repo.changelog) < count:
616 if len(repo.changelog) < count:
617 raise error.Abort("repo needs %d commits for this test" % count)
617 raise error.Abort("repo needs %d commits for this test" % count)
618 repo = repo.unfiltered()
618 repo = repo.unfiltered()
619 nl = [repo.changelog.node(i) for i in xrange(count)]
619 nl = [repo.changelog.node(i) for i in xrange(count)]
620 def d():
620 def d():
621 for n in nl:
621 for n in nl:
622 repo.changelog.parents(n)
622 repo.changelog.parents(n)
623 timer(d)
623 timer(d)
624 fm.end()
624 fm.end()
625
625
626 @command('perfctxfiles', formatteropts)
626 @command('perfctxfiles', formatteropts)
627 def perfctxfiles(ui, repo, x, **opts):
627 def perfctxfiles(ui, repo, x, **opts):
628 x = int(x)
628 x = int(x)
629 timer, fm = gettimer(ui, opts)
629 timer, fm = gettimer(ui, opts)
630 def d():
630 def d():
631 len(repo[x].files())
631 len(repo[x].files())
632 timer(d)
632 timer(d)
633 fm.end()
633 fm.end()
634
634
635 @command('perfrawfiles', formatteropts)
635 @command('perfrawfiles', formatteropts)
636 def perfrawfiles(ui, repo, x, **opts):
636 def perfrawfiles(ui, repo, x, **opts):
637 x = int(x)
637 x = int(x)
638 timer, fm = gettimer(ui, opts)
638 timer, fm = gettimer(ui, opts)
639 cl = repo.changelog
639 cl = repo.changelog
640 def d():
640 def d():
641 len(cl.read(x)[3])
641 len(cl.read(x)[3])
642 timer(d)
642 timer(d)
643 fm.end()
643 fm.end()
644
644
645 @command('perflookup', formatteropts)
645 @command('perflookup', formatteropts)
646 def perflookup(ui, repo, rev, **opts):
646 def perflookup(ui, repo, rev, **opts):
647 timer, fm = gettimer(ui, opts)
647 timer, fm = gettimer(ui, opts)
648 timer(lambda: len(repo.lookup(rev)))
648 timer(lambda: len(repo.lookup(rev)))
649 fm.end()
649 fm.end()
650
650
651 @command('perfrevrange', formatteropts)
651 @command('perfrevrange', formatteropts)
652 def perfrevrange(ui, repo, *specs, **opts):
652 def perfrevrange(ui, repo, *specs, **opts):
653 timer, fm = gettimer(ui, opts)
653 timer, fm = gettimer(ui, opts)
654 revrange = scmutil.revrange
654 revrange = scmutil.revrange
655 timer(lambda: len(revrange(repo, specs)))
655 timer(lambda: len(revrange(repo, specs)))
656 fm.end()
656 fm.end()
657
657
658 @command('perfnodelookup', formatteropts)
658 @command('perfnodelookup', formatteropts)
659 def perfnodelookup(ui, repo, rev, **opts):
659 def perfnodelookup(ui, repo, rev, **opts):
660 timer, fm = gettimer(ui, opts)
660 timer, fm = gettimer(ui, opts)
661 import mercurial.revlog
661 import mercurial.revlog
662 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
662 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
663 n = repo[rev].node()
663 n = repo[rev].node()
664 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
664 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
665 def d():
665 def d():
666 cl.rev(n)
666 cl.rev(n)
667 clearcaches(cl)
667 clearcaches(cl)
668 timer(d)
668 timer(d)
669 fm.end()
669 fm.end()
670
670
671 @command('perflog',
671 @command('perflog',
672 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
672 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
673 def perflog(ui, repo, rev=None, **opts):
673 def perflog(ui, repo, rev=None, **opts):
674 if rev is None:
674 if rev is None:
675 rev=[]
675 rev=[]
676 timer, fm = gettimer(ui, opts)
676 timer, fm = gettimer(ui, opts)
677 ui.pushbuffer()
677 ui.pushbuffer()
678 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
678 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
679 copies=opts.get('rename')))
679 copies=opts.get('rename')))
680 ui.popbuffer()
680 ui.popbuffer()
681 fm.end()
681 fm.end()
682
682
683 @command('perfmoonwalk', formatteropts)
683 @command('perfmoonwalk', formatteropts)
684 def perfmoonwalk(ui, repo, **opts):
684 def perfmoonwalk(ui, repo, **opts):
685 """benchmark walking the changelog backwards
685 """benchmark walking the changelog backwards
686
686
687 This also loads the changelog data for each revision in the changelog.
687 This also loads the changelog data for each revision in the changelog.
688 """
688 """
689 timer, fm = gettimer(ui, opts)
689 timer, fm = gettimer(ui, opts)
690 def moonwalk():
690 def moonwalk():
691 for i in xrange(len(repo), -1, -1):
691 for i in xrange(len(repo), -1, -1):
692 ctx = repo[i]
692 ctx = repo[i]
693 ctx.branch() # read changelog data (in addition to the index)
693 ctx.branch() # read changelog data (in addition to the index)
694 timer(moonwalk)
694 timer(moonwalk)
695 fm.end()
695 fm.end()
696
696
697 @command('perftemplating', formatteropts)
697 @command('perftemplating', formatteropts)
698 def perftemplating(ui, repo, rev=None, **opts):
698 def perftemplating(ui, repo, rev=None, **opts):
699 if rev is None:
699 if rev is None:
700 rev=[]
700 rev=[]
701 timer, fm = gettimer(ui, opts)
701 timer, fm = gettimer(ui, opts)
702 ui.pushbuffer()
702 ui.pushbuffer()
703 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
703 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
704 template='{date|shortdate} [{rev}:{node|short}]'
704 template='{date|shortdate} [{rev}:{node|short}]'
705 ' {author|person}: {desc|firstline}\n'))
705 ' {author|person}: {desc|firstline}\n'))
706 ui.popbuffer()
706 ui.popbuffer()
707 fm.end()
707 fm.end()
708
708
709 @command('perfcca', formatteropts)
709 @command('perfcca', formatteropts)
710 def perfcca(ui, repo, **opts):
710 def perfcca(ui, repo, **opts):
711 timer, fm = gettimer(ui, opts)
711 timer, fm = gettimer(ui, opts)
712 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
712 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
713 fm.end()
713 fm.end()
714
714
715 @command('perffncacheload', formatteropts)
715 @command('perffncacheload', formatteropts)
716 def perffncacheload(ui, repo, **opts):
716 def perffncacheload(ui, repo, **opts):
717 timer, fm = gettimer(ui, opts)
717 timer, fm = gettimer(ui, opts)
718 s = repo.store
718 s = repo.store
719 def d():
719 def d():
720 s.fncache._load()
720 s.fncache._load()
721 timer(d)
721 timer(d)
722 fm.end()
722 fm.end()
723
723
724 @command('perffncachewrite', formatteropts)
724 @command('perffncachewrite', formatteropts)
725 def perffncachewrite(ui, repo, **opts):
725 def perffncachewrite(ui, repo, **opts):
726 timer, fm = gettimer(ui, opts)
726 timer, fm = gettimer(ui, opts)
727 s = repo.store
727 s = repo.store
728 s.fncache._load()
728 s.fncache._load()
729 lock = repo.lock()
729 lock = repo.lock()
730 tr = repo.transaction('perffncachewrite')
730 tr = repo.transaction('perffncachewrite')
731 def d():
731 def d():
732 s.fncache._dirty = True
732 s.fncache._dirty = True
733 s.fncache.write(tr)
733 s.fncache.write(tr)
734 timer(d)
734 timer(d)
735 tr.close()
735 tr.close()
736 lock.release()
736 lock.release()
737 fm.end()
737 fm.end()
738
738
739 @command('perffncacheencode', formatteropts)
739 @command('perffncacheencode', formatteropts)
740 def perffncacheencode(ui, repo, **opts):
740 def perffncacheencode(ui, repo, **opts):
741 timer, fm = gettimer(ui, opts)
741 timer, fm = gettimer(ui, opts)
742 s = repo.store
742 s = repo.store
743 s.fncache._load()
743 s.fncache._load()
744 def d():
744 def d():
745 for p in s.fncache.entries:
745 for p in s.fncache.entries:
746 s.encode(p)
746 s.encode(p)
747 timer(d)
747 timer(d)
748 fm.end()
748 fm.end()
749
749
750 @command('perfbdiff', revlogopts + formatteropts + [
750 @command('perfbdiff', revlogopts + formatteropts + [
751 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
751 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
752 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
752 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
753 '-c|-m|FILE REV')
753 '-c|-m|FILE REV')
754 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
754 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
755 """benchmark a bdiff between revisions
755 """benchmark a bdiff between revisions
756
756
757 By default, benchmark a bdiff between its delta parent and itself.
757 By default, benchmark a bdiff between its delta parent and itself.
758
758
759 With ``--count``, benchmark bdiffs between delta parents and self for N
759 With ``--count``, benchmark bdiffs between delta parents and self for N
760 revisions starting at the specified revision.
760 revisions starting at the specified revision.
761
761
762 With ``--alldata``, assume the requested revision is a changeset and
762 With ``--alldata``, assume the requested revision is a changeset and
763 measure bdiffs for all changes related to that changeset (manifest
763 measure bdiffs for all changes related to that changeset (manifest
764 and filelogs).
764 and filelogs).
765 """
765 """
766 if opts['alldata']:
766 if opts['alldata']:
767 opts['changelog'] = True
767 opts['changelog'] = True
768
768
769 if opts.get('changelog') or opts.get('manifest'):
769 if opts.get('changelog') or opts.get('manifest'):
770 file_, rev = None, file_
770 file_, rev = None, file_
771 elif rev is None:
771 elif rev is None:
772 raise error.CommandError('perfbdiff', 'invalid arguments')
772 raise error.CommandError('perfbdiff', 'invalid arguments')
773
773
774 textpairs = []
774 textpairs = []
775
775
776 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
776 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
777
777
778 startrev = r.rev(r.lookup(rev))
778 startrev = r.rev(r.lookup(rev))
779 for rev in range(startrev, min(startrev + count, len(r) - 1)):
779 for rev in range(startrev, min(startrev + count, len(r) - 1)):
780 if opts['alldata']:
780 if opts['alldata']:
781 # Load revisions associated with changeset.
781 # Load revisions associated with changeset.
782 ctx = repo[rev]
782 ctx = repo[rev]
783 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
783 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
784 for pctx in ctx.parents():
784 for pctx in ctx.parents():
785 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
785 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
786 textpairs.append((pman, mtext))
786 textpairs.append((pman, mtext))
787
787
788 # Load filelog revisions by iterating manifest delta.
788 # Load filelog revisions by iterating manifest delta.
789 man = ctx.manifest()
789 man = ctx.manifest()
790 pman = ctx.p1().manifest()
790 pman = ctx.p1().manifest()
791 for filename, change in pman.diff(man).items():
791 for filename, change in pman.diff(man).items():
792 fctx = repo.file(filename)
792 fctx = repo.file(filename)
793 f1 = fctx.revision(change[0][0] or -1)
793 f1 = fctx.revision(change[0][0] or -1)
794 f2 = fctx.revision(change[1][0] or -1)
794 f2 = fctx.revision(change[1][0] or -1)
795 textpairs.append((f1, f2))
795 textpairs.append((f1, f2))
796 else:
796 else:
797 dp = r.deltaparent(rev)
797 dp = r.deltaparent(rev)
798 textpairs.append((r.revision(dp), r.revision(rev)))
798 textpairs.append((r.revision(dp), r.revision(rev)))
799
799
800 def d():
800 def d():
801 for pair in textpairs:
801 for pair in textpairs:
802 bdiff.bdiff(*pair)
802 bdiff.bdiff(*pair)
803
803
804 timer, fm = gettimer(ui, opts)
804 timer, fm = gettimer(ui, opts)
805 timer(d)
805 timer(d)
806 fm.end()
806 fm.end()
807
807
808 @command('perfdiffwd', formatteropts)
808 @command('perfdiffwd', formatteropts)
809 def perfdiffwd(ui, repo, **opts):
809 def perfdiffwd(ui, repo, **opts):
810 """Profile diff of working directory changes"""
810 """Profile diff of working directory changes"""
811 timer, fm = gettimer(ui, opts)
811 timer, fm = gettimer(ui, opts)
812 options = {
812 options = {
813 'w': 'ignore_all_space',
813 'w': 'ignore_all_space',
814 'b': 'ignore_space_change',
814 'b': 'ignore_space_change',
815 'B': 'ignore_blank_lines',
815 'B': 'ignore_blank_lines',
816 }
816 }
817
817
818 for diffopt in ('', 'w', 'b', 'B', 'wB'):
818 for diffopt in ('', 'w', 'b', 'B', 'wB'):
819 opts = dict((options[c], '1') for c in diffopt)
819 opts = dict((options[c], '1') for c in diffopt)
820 def d():
820 def d():
821 ui.pushbuffer()
821 ui.pushbuffer()
822 commands.diff(ui, repo, **opts)
822 commands.diff(ui, repo, **opts)
823 ui.popbuffer()
823 ui.popbuffer()
824 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
824 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
825 timer(d, title)
825 timer(d, title)
826 fm.end()
826 fm.end()
827
827
828 @command('perfrevlog', revlogopts + formatteropts +
828 @command('perfrevlog', revlogopts + formatteropts +
829 [('d', 'dist', 100, 'distance between the revisions'),
829 [('d', 'dist', 100, 'distance between the revisions'),
830 ('s', 'startrev', 0, 'revision to start reading at'),
830 ('s', 'startrev', 0, 'revision to start reading at'),
831 ('', 'reverse', False, 'read in reverse')],
831 ('', 'reverse', False, 'read in reverse')],
832 '-c|-m|FILE')
832 '-c|-m|FILE')
833 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
833 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
834 """Benchmark reading a series of revisions from a revlog.
834 """Benchmark reading a series of revisions from a revlog.
835
835
836 By default, we read every ``-d/--dist`` revision from 0 to tip of
836 By default, we read every ``-d/--dist`` revision from 0 to tip of
837 the specified revlog.
837 the specified revlog.
838
838
839 The start revision can be defined via ``-s/--startrev``.
839 The start revision can be defined via ``-s/--startrev``.
840 """
840 """
841 timer, fm = gettimer(ui, opts)
841 timer, fm = gettimer(ui, opts)
842 _len = getlen(ui)
842 _len = getlen(ui)
843
843
844 def d():
844 def d():
845 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
845 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
846
846
847 startrev = 0
847 startrev = 0
848 endrev = _len(r)
848 endrev = _len(r)
849 dist = opts['dist']
849 dist = opts['dist']
850
850
851 if reverse:
851 if reverse:
852 startrev, endrev = endrev, startrev
852 startrev, endrev = endrev, startrev
853 dist = -1 * dist
853 dist = -1 * dist
854
854
855 for x in xrange(startrev, endrev, dist):
855 for x in xrange(startrev, endrev, dist):
856 r.revision(r.node(x))
856 r.revision(r.node(x))
857
857
858 timer(d)
858 timer(d)
859 fm.end()
859 fm.end()
860
860
861 @command('perfrevlogchunks', revlogopts + formatteropts +
861 @command('perfrevlogchunks', revlogopts + formatteropts +
862 [('e', 'engines', '', 'compression engines to use'),
862 [('e', 'engines', '', 'compression engines to use'),
863 ('s', 'startrev', 0, 'revision to start at')],
863 ('s', 'startrev', 0, 'revision to start at')],
864 '-c|-m|FILE')
864 '-c|-m|FILE')
865 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
865 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
866 """Benchmark operations on revlog chunks.
866 """Benchmark operations on revlog chunks.
867
867
868 Logically, each revlog is a collection of fulltext revisions. However,
868 Logically, each revlog is a collection of fulltext revisions. However,
869 stored within each revlog are "chunks" of possibly compressed data. This
869 stored within each revlog are "chunks" of possibly compressed data. This
870 data needs to be read and decompressed or compressed and written.
870 data needs to be read and decompressed or compressed and written.
871
871
872 This command measures the time it takes to read+decompress and recompress
872 This command measures the time it takes to read+decompress and recompress
873 chunks in a revlog. It effectively isolates I/O and compression performance.
873 chunks in a revlog. It effectively isolates I/O and compression performance.
874 For measurements of higher-level operations like resolving revisions,
874 For measurements of higher-level operations like resolving revisions,
875 see ``perfrevlog`` and ``perfrevlogrevision``.
875 see ``perfrevlog`` and ``perfrevlogrevision``.
876 """
876 """
877 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
877 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
878
878
879 # Verify engines argument.
879 # Verify engines argument.
880 if engines:
880 if engines:
881 engines = set(e.strip() for e in engines.split(','))
881 engines = set(e.strip() for e in engines.split(','))
882 for engine in engines:
882 for engine in engines:
883 try:
883 try:
884 util.compressionengines[engine]
884 util.compressionengines[engine]
885 except KeyError:
885 except KeyError:
886 raise error.Abort('unknown compression engine: %s' % engine)
886 raise error.Abort('unknown compression engine: %s' % engine)
887 else:
887 else:
888 engines = []
888 engines = []
889 for e in util.compengines:
889 for e in util.compengines:
890 engine = util.compengines[e]
890 engine = util.compengines[e]
891 try:
891 try:
892 if engine.available():
892 if engine.available():
893 engine.revlogcompressor().compress('dummy')
893 engine.revlogcompressor().compress('dummy')
894 engines.append(e)
894 engines.append(e)
895 except NotImplementedError:
895 except NotImplementedError:
896 pass
896 pass
897
897
898 revs = list(rl.revs(startrev, len(rl) - 1))
898 revs = list(rl.revs(startrev, len(rl) - 1))
899
899
900 def rlfh(rl):
900 def rlfh(rl):
901 if rl._inline:
901 if rl._inline:
902 return getsvfs(repo)(rl.indexfile)
902 return getsvfs(repo)(rl.indexfile)
903 else:
903 else:
904 return getsvfs(repo)(rl.datafile)
904 return getsvfs(repo)(rl.datafile)
905
905
906 def doread():
906 def doread():
907 rl.clearcaches()
907 rl.clearcaches()
908 for rev in revs:
908 for rev in revs:
909 rl._chunkraw(rev, rev)
909 rl._chunkraw(rev, rev)
910
910
911 def doreadcachedfh():
911 def doreadcachedfh():
912 rl.clearcaches()
912 rl.clearcaches()
913 fh = rlfh(rl)
913 fh = rlfh(rl)
914 for rev in revs:
914 for rev in revs:
915 rl._chunkraw(rev, rev, df=fh)
915 rl._chunkraw(rev, rev, df=fh)
916
916
917 def doreadbatch():
917 def doreadbatch():
918 rl.clearcaches()
918 rl.clearcaches()
919 rl._chunkraw(revs[0], revs[-1])
919 rl._chunkraw(revs[0], revs[-1])
920
920
921 def doreadbatchcachedfh():
921 def doreadbatchcachedfh():
922 rl.clearcaches()
922 rl.clearcaches()
923 fh = rlfh(rl)
923 fh = rlfh(rl)
924 rl._chunkraw(revs[0], revs[-1], df=fh)
924 rl._chunkraw(revs[0], revs[-1], df=fh)
925
925
926 def dochunk():
926 def dochunk():
927 rl.clearcaches()
927 rl.clearcaches()
928 fh = rlfh(rl)
928 fh = rlfh(rl)
929 for rev in revs:
929 for rev in revs:
930 rl._chunk(rev, df=fh)
930 rl._chunk(rev, df=fh)
931
931
932 chunks = [None]
932 chunks = [None]
933
933
934 def dochunkbatch():
934 def dochunkbatch():
935 rl.clearcaches()
935 rl.clearcaches()
936 fh = rlfh(rl)
936 fh = rlfh(rl)
937 # Save chunks as a side-effect.
937 # Save chunks as a side-effect.
938 chunks[0] = rl._chunks(revs, df=fh)
938 chunks[0] = rl._chunks(revs, df=fh)
939
939
940 def docompress(compressor):
940 def docompress(compressor):
941 rl.clearcaches()
941 rl.clearcaches()
942
942
943 try:
943 try:
944 # Swap in the requested compression engine.
944 # Swap in the requested compression engine.
945 oldcompressor = rl._compressor
945 oldcompressor = rl._compressor
946 rl._compressor = compressor
946 rl._compressor = compressor
947 for chunk in chunks[0]:
947 for chunk in chunks[0]:
948 rl.compress(chunk)
948 rl.compress(chunk)
949 finally:
949 finally:
950 rl._compressor = oldcompressor
950 rl._compressor = oldcompressor
951
951
952 benches = [
952 benches = [
953 (lambda: doread(), 'read'),
953 (lambda: doread(), 'read'),
954 (lambda: doreadcachedfh(), 'read w/ reused fd'),
954 (lambda: doreadcachedfh(), 'read w/ reused fd'),
955 (lambda: doreadbatch(), 'read batch'),
955 (lambda: doreadbatch(), 'read batch'),
956 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
956 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
957 (lambda: dochunk(), 'chunk'),
957 (lambda: dochunk(), 'chunk'),
958 (lambda: dochunkbatch(), 'chunk batch'),
958 (lambda: dochunkbatch(), 'chunk batch'),
959 ]
959 ]
960
960
961 for engine in sorted(engines):
961 for engine in sorted(engines):
962 compressor = util.compengines[engine].revlogcompressor()
962 compressor = util.compengines[engine].revlogcompressor()
963 benches.append((functools.partial(docompress, compressor),
963 benches.append((functools.partial(docompress, compressor),
964 'compress w/ %s' % engine))
964 'compress w/ %s' % engine))
965
965
966 for fn, title in benches:
966 for fn, title in benches:
967 timer, fm = gettimer(ui, opts)
967 timer, fm = gettimer(ui, opts)
968 timer(fn, title=title)
968 timer(fn, title=title)
969 fm.end()
969 fm.end()
970
970
971 @command('perfrevlogrevision', revlogopts + formatteropts +
971 @command('perfrevlogrevision', revlogopts + formatteropts +
972 [('', 'cache', False, 'use caches instead of clearing')],
972 [('', 'cache', False, 'use caches instead of clearing')],
973 '-c|-m|FILE REV')
973 '-c|-m|FILE REV')
974 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
974 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
975 """Benchmark obtaining a revlog revision.
975 """Benchmark obtaining a revlog revision.
976
976
977 Obtaining a revlog revision consists of roughly the following steps:
977 Obtaining a revlog revision consists of roughly the following steps:
978
978
979 1. Compute the delta chain
979 1. Compute the delta chain
980 2. Obtain the raw chunks for that delta chain
980 2. Obtain the raw chunks for that delta chain
981 3. Decompress each raw chunk
981 3. Decompress each raw chunk
982 4. Apply binary patches to obtain fulltext
982 4. Apply binary patches to obtain fulltext
983 5. Verify hash of fulltext
983 5. Verify hash of fulltext
984
984
985 This command measures the time spent in each of these phases.
985 This command measures the time spent in each of these phases.
986 """
986 """
987 if opts.get('changelog') or opts.get('manifest'):
987 if opts.get('changelog') or opts.get('manifest'):
988 file_, rev = None, file_
988 file_, rev = None, file_
989 elif rev is None:
989 elif rev is None:
990 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
990 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
991
991
992 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
992 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
993 node = r.lookup(rev)
993 node = r.lookup(rev)
994 rev = r.rev(node)
994 rev = r.rev(node)
995
995
996 def getrawchunks(data, chain):
996 def getrawchunks(data, chain):
997 start = r.start
997 start = r.start
998 length = r.length
998 length = r.length
999 inline = r._inline
999 inline = r._inline
1000 iosize = r._io.size
1000 iosize = r._io.size
1001 buffer = util.buffer
1001 buffer = util.buffer
1002 offset = start(chain[0])
1002 offset = start(chain[0])
1003
1003
1004 chunks = []
1004 chunks = []
1005 ladd = chunks.append
1005 ladd = chunks.append
1006
1006
1007 for rev in chain:
1007 for rev in chain:
1008 chunkstart = start(rev)
1008 chunkstart = start(rev)
1009 if inline:
1009 if inline:
1010 chunkstart += (rev + 1) * iosize
1010 chunkstart += (rev + 1) * iosize
1011 chunklength = length(rev)
1011 chunklength = length(rev)
1012 ladd(buffer(data, chunkstart - offset, chunklength))
1012 ladd(buffer(data, chunkstart - offset, chunklength))
1013
1013
1014 return chunks
1014 return chunks
1015
1015
1016 def dodeltachain(rev):
1016 def dodeltachain(rev):
1017 if not cache:
1017 if not cache:
1018 r.clearcaches()
1018 r.clearcaches()
1019 r._deltachain(rev)
1019 r._deltachain(rev)
1020
1020
1021 def doread(chain):
1021 def doread(chain):
1022 if not cache:
1022 if not cache:
1023 r.clearcaches()
1023 r.clearcaches()
1024 r._chunkraw(chain[0], chain[-1])
1024 r._chunkraw(chain[0], chain[-1])
1025
1025
1026 def dorawchunks(data, chain):
1026 def dorawchunks(data, chain):
1027 if not cache:
1027 if not cache:
1028 r.clearcaches()
1028 r.clearcaches()
1029 getrawchunks(data, chain)
1029 getrawchunks(data, chain)
1030
1030
1031 def dodecompress(chunks):
1031 def dodecompress(chunks):
1032 decomp = r.decompress
1032 decomp = r.decompress
1033 for chunk in chunks:
1033 for chunk in chunks:
1034 decomp(chunk)
1034 decomp(chunk)
1035
1035
1036 def dopatch(text, bins):
1036 def dopatch(text, bins):
1037 if not cache:
1037 if not cache:
1038 r.clearcaches()
1038 r.clearcaches()
1039 mdiff.patches(text, bins)
1039 mdiff.patches(text, bins)
1040
1040
1041 def dohash(text):
1041 def dohash(text):
1042 if not cache:
1042 if not cache:
1043 r.clearcaches()
1043 r.clearcaches()
1044 r.checkhash(text, node, rev=rev)
1044 r.checkhash(text, node, rev=rev)
1045
1045
1046 def dorevision():
1046 def dorevision():
1047 if not cache:
1047 if not cache:
1048 r.clearcaches()
1048 r.clearcaches()
1049 r.revision(node)
1049 r.revision(node)
1050
1050
1051 chain = r._deltachain(rev)[0]
1051 chain = r._deltachain(rev)[0]
1052 data = r._chunkraw(chain[0], chain[-1])[1]
1052 data = r._chunkraw(chain[0], chain[-1])[1]
1053 rawchunks = getrawchunks(data, chain)
1053 rawchunks = getrawchunks(data, chain)
1054 bins = r._chunks(chain)
1054 bins = r._chunks(chain)
1055 text = str(bins[0])
1055 text = str(bins[0])
1056 bins = bins[1:]
1056 bins = bins[1:]
1057 text = mdiff.patches(text, bins)
1057 text = mdiff.patches(text, bins)
1058
1058
1059 benches = [
1059 benches = [
1060 (lambda: dorevision(), 'full'),
1060 (lambda: dorevision(), 'full'),
1061 (lambda: dodeltachain(rev), 'deltachain'),
1061 (lambda: dodeltachain(rev), 'deltachain'),
1062 (lambda: doread(chain), 'read'),
1062 (lambda: doread(chain), 'read'),
1063 (lambda: dorawchunks(data, chain), 'rawchunks'),
1063 (lambda: dorawchunks(data, chain), 'rawchunks'),
1064 (lambda: dodecompress(rawchunks), 'decompress'),
1064 (lambda: dodecompress(rawchunks), 'decompress'),
1065 (lambda: dopatch(text, bins), 'patch'),
1065 (lambda: dopatch(text, bins), 'patch'),
1066 (lambda: dohash(text), 'hash'),
1066 (lambda: dohash(text), 'hash'),
1067 ]
1067 ]
1068
1068
1069 for fn, title in benches:
1069 for fn, title in benches:
1070 timer, fm = gettimer(ui, opts)
1070 timer, fm = gettimer(ui, opts)
1071 timer(fn, title=title)
1071 timer(fn, title=title)
1072 fm.end()
1072 fm.end()
1073
1073
1074 @command('perfrevset',
1074 @command('perfrevset',
1075 [('C', 'clear', False, 'clear volatile cache between each call.'),
1075 [('C', 'clear', False, 'clear volatile cache between each call.'),
1076 ('', 'contexts', False, 'obtain changectx for each revision')]
1076 ('', 'contexts', False, 'obtain changectx for each revision')]
1077 + formatteropts, "REVSET")
1077 + formatteropts, "REVSET")
1078 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1078 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1079 """benchmark the execution time of a revset
1079 """benchmark the execution time of a revset
1080
1080
1081 Use the --clean option if need to evaluate the impact of build volatile
1081 Use the --clean option if need to evaluate the impact of build volatile
1082 revisions set cache on the revset execution. Volatile cache hold filtered
1082 revisions set cache on the revset execution. Volatile cache hold filtered
1083 and obsolete related cache."""
1083 and obsolete related cache."""
1084 timer, fm = gettimer(ui, opts)
1084 timer, fm = gettimer(ui, opts)
1085 def d():
1085 def d():
1086 if clear:
1086 if clear:
1087 repo.invalidatevolatilesets()
1087 repo.invalidatevolatilesets()
1088 if contexts:
1088 if contexts:
1089 for ctx in repo.set(expr): pass
1089 for ctx in repo.set(expr): pass
1090 else:
1090 else:
1091 for r in repo.revs(expr): pass
1091 for r in repo.revs(expr): pass
1092 timer(d)
1092 timer(d)
1093 fm.end()
1093 fm.end()
1094
1094
1095 @command('perfvolatilesets', formatteropts)
1095 @command('perfvolatilesets', formatteropts)
1096 def perfvolatilesets(ui, repo, *names, **opts):
1096 def perfvolatilesets(ui, repo, *names, **opts):
1097 """benchmark the computation of various volatile set
1097 """benchmark the computation of various volatile set
1098
1098
1099 Volatile set computes element related to filtering and obsolescence."""
1099 Volatile set computes element related to filtering and obsolescence."""
1100 timer, fm = gettimer(ui, opts)
1100 timer, fm = gettimer(ui, opts)
1101 repo = repo.unfiltered()
1101 repo = repo.unfiltered()
1102
1102
1103 def getobs(name):
1103 def getobs(name):
1104 def d():
1104 def d():
1105 repo.invalidatevolatilesets()
1105 repo.invalidatevolatilesets()
1106 obsolete.getrevs(repo, name)
1106 obsolete.getrevs(repo, name)
1107 return d
1107 return d
1108
1108
1109 allobs = sorted(obsolete.cachefuncs)
1109 allobs = sorted(obsolete.cachefuncs)
1110 if names:
1110 if names:
1111 allobs = [n for n in allobs if n in names]
1111 allobs = [n for n in allobs if n in names]
1112
1112
1113 for name in allobs:
1113 for name in allobs:
1114 timer(getobs(name), title=name)
1114 timer(getobs(name), title=name)
1115
1115
1116 def getfiltered(name):
1116 def getfiltered(name):
1117 def d():
1117 def d():
1118 repo.invalidatevolatilesets()
1118 repo.invalidatevolatilesets()
1119 repoview.filterrevs(repo, name)
1119 repoview.filterrevs(repo, name)
1120 return d
1120 return d
1121
1121
1122 allfilter = sorted(repoview.filtertable)
1122 allfilter = sorted(repoview.filtertable)
1123 if names:
1123 if names:
1124 allfilter = [n for n in allfilter if n in names]
1124 allfilter = [n for n in allfilter if n in names]
1125
1125
1126 for name in allfilter:
1126 for name in allfilter:
1127 timer(getfiltered(name), title=name)
1127 timer(getfiltered(name), title=name)
1128 fm.end()
1128 fm.end()
1129
1129
1130 @command('perfbranchmap',
1130 @command('perfbranchmap',
1131 [('f', 'full', False,
1131 [('f', 'full', False,
1132 'Includes build time of subset'),
1132 'Includes build time of subset'),
1133 ] + formatteropts)
1133 ] + formatteropts)
1134 def perfbranchmap(ui, repo, full=False, **opts):
1134 def perfbranchmap(ui, repo, full=False, **opts):
1135 """benchmark the update of a branchmap
1135 """benchmark the update of a branchmap
1136
1136
1137 This benchmarks the full repo.branchmap() call with read and write disabled
1137 This benchmarks the full repo.branchmap() call with read and write disabled
1138 """
1138 """
1139 timer, fm = gettimer(ui, opts)
1139 timer, fm = gettimer(ui, opts)
1140 def getbranchmap(filtername):
1140 def getbranchmap(filtername):
1141 """generate a benchmark function for the filtername"""
1141 """generate a benchmark function for the filtername"""
1142 if filtername is None:
1142 if filtername is None:
1143 view = repo
1143 view = repo
1144 else:
1144 else:
1145 view = repo.filtered(filtername)
1145 view = repo.filtered(filtername)
1146 def d():
1146 def d():
1147 if full:
1147 if full:
1148 view._branchcaches.clear()
1148 view._branchcaches.clear()
1149 else:
1149 else:
1150 view._branchcaches.pop(filtername, None)
1150 view._branchcaches.pop(filtername, None)
1151 view.branchmap()
1151 view.branchmap()
1152 return d
1152 return d
1153 # add filter in smaller subset to bigger subset
1153 # add filter in smaller subset to bigger subset
1154 possiblefilters = set(repoview.filtertable)
1154 possiblefilters = set(repoview.filtertable)
1155 subsettable = getbranchmapsubsettable()
1155 subsettable = getbranchmapsubsettable()
1156 allfilters = []
1156 allfilters = []
1157 while possiblefilters:
1157 while possiblefilters:
1158 for name in possiblefilters:
1158 for name in possiblefilters:
1159 subset = subsettable.get(name)
1159 subset = subsettable.get(name)
1160 if subset not in possiblefilters:
1160 if subset not in possiblefilters:
1161 break
1161 break
1162 else:
1162 else:
1163 assert False, 'subset cycle %s!' % possiblefilters
1163 assert False, 'subset cycle %s!' % possiblefilters
1164 allfilters.append(name)
1164 allfilters.append(name)
1165 possiblefilters.remove(name)
1165 possiblefilters.remove(name)
1166
1166
1167 # warm the cache
1167 # warm the cache
1168 if not full:
1168 if not full:
1169 for name in allfilters:
1169 for name in allfilters:
1170 repo.filtered(name).branchmap()
1170 repo.filtered(name).branchmap()
1171 # add unfiltered
1171 # add unfiltered
1172 allfilters.append(None)
1172 allfilters.append(None)
1173
1173
1174 branchcacheread = safeattrsetter(branchmap, 'read')
1174 branchcacheread = safeattrsetter(branchmap, 'read')
1175 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1175 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1176 branchcacheread.set(lambda repo: None)
1176 branchcacheread.set(lambda repo: None)
1177 branchcachewrite.set(lambda bc, repo: None)
1177 branchcachewrite.set(lambda bc, repo: None)
1178 try:
1178 try:
1179 for name in allfilters:
1179 for name in allfilters:
1180 timer(getbranchmap(name), title=str(name))
1180 timer(getbranchmap(name), title=str(name))
1181 finally:
1181 finally:
1182 branchcacheread.restore()
1182 branchcacheread.restore()
1183 branchcachewrite.restore()
1183 branchcachewrite.restore()
1184 fm.end()
1184 fm.end()
1185
1185
1186 @command('perfloadmarkers')
1186 @command('perfloadmarkers')
1187 def perfloadmarkers(ui, repo):
1187 def perfloadmarkers(ui, repo):
1188 """benchmark the time to parse the on-disk markers for a repo
1188 """benchmark the time to parse the on-disk markers for a repo
1189
1189
1190 Result is the number of markers in the repo."""
1190 Result is the number of markers in the repo."""
1191 timer, fm = gettimer(ui)
1191 timer, fm = gettimer(ui)
1192 svfs = getsvfs(repo)
1192 svfs = getsvfs(repo)
1193 timer(lambda: len(obsolete.obsstore(svfs)))
1193 timer(lambda: len(obsolete.obsstore(svfs)))
1194 fm.end()
1194 fm.end()
1195
1195
1196 @command('perflrucachedict', formatteropts +
1196 @command('perflrucachedict', formatteropts +
1197 [('', 'size', 4, 'size of cache'),
1197 [('', 'size', 4, 'size of cache'),
1198 ('', 'gets', 10000, 'number of key lookups'),
1198 ('', 'gets', 10000, 'number of key lookups'),
1199 ('', 'sets', 10000, 'number of key sets'),
1199 ('', 'sets', 10000, 'number of key sets'),
1200 ('', 'mixed', 10000, 'number of mixed mode operations'),
1200 ('', 'mixed', 10000, 'number of mixed mode operations'),
1201 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1201 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1202 norepo=True)
1202 norepo=True)
1203 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1203 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1204 mixedgetfreq=50, **opts):
1204 mixedgetfreq=50, **opts):
1205 def doinit():
1205 def doinit():
1206 for i in xrange(10000):
1206 for i in xrange(10000):
1207 util.lrucachedict(size)
1207 util.lrucachedict(size)
1208
1208
1209 values = []
1209 values = []
1210 for i in xrange(size):
1210 for i in xrange(size):
1211 values.append(random.randint(0, sys.maxint))
1211 values.append(random.randint(0, sys.maxint))
1212
1212
1213 # Get mode fills the cache and tests raw lookup performance with no
1213 # Get mode fills the cache and tests raw lookup performance with no
1214 # eviction.
1214 # eviction.
1215 getseq = []
1215 getseq = []
1216 for i in xrange(gets):
1216 for i in xrange(gets):
1217 getseq.append(random.choice(values))
1217 getseq.append(random.choice(values))
1218
1218
1219 def dogets():
1219 def dogets():
1220 d = util.lrucachedict(size)
1220 d = util.lrucachedict(size)
1221 for v in values:
1221 for v in values:
1222 d[v] = v
1222 d[v] = v
1223 for key in getseq:
1223 for key in getseq:
1224 value = d[key]
1224 value = d[key]
1225 value # silence pyflakes warning
1225 value # silence pyflakes warning
1226
1226
1227 # Set mode tests insertion speed with cache eviction.
1227 # Set mode tests insertion speed with cache eviction.
1228 setseq = []
1228 setseq = []
1229 for i in xrange(sets):
1229 for i in xrange(sets):
1230 setseq.append(random.randint(0, sys.maxint))
1230 setseq.append(random.randint(0, sys.maxint))
1231
1231
1232 def dosets():
1232 def dosets():
1233 d = util.lrucachedict(size)
1233 d = util.lrucachedict(size)
1234 for v in setseq:
1234 for v in setseq:
1235 d[v] = v
1235 d[v] = v
1236
1236
1237 # Mixed mode randomly performs gets and sets with eviction.
1237 # Mixed mode randomly performs gets and sets with eviction.
1238 mixedops = []
1238 mixedops = []
1239 for i in xrange(mixed):
1239 for i in xrange(mixed):
1240 r = random.randint(0, 100)
1240 r = random.randint(0, 100)
1241 if r < mixedgetfreq:
1241 if r < mixedgetfreq:
1242 op = 0
1242 op = 0
1243 else:
1243 else:
1244 op = 1
1244 op = 1
1245
1245
1246 mixedops.append((op, random.randint(0, size * 2)))
1246 mixedops.append((op, random.randint(0, size * 2)))
1247
1247
1248 def domixed():
1248 def domixed():
1249 d = util.lrucachedict(size)
1249 d = util.lrucachedict(size)
1250
1250
1251 for op, v in mixedops:
1251 for op, v in mixedops:
1252 if op == 0:
1252 if op == 0:
1253 try:
1253 try:
1254 d[v]
1254 d[v]
1255 except KeyError:
1255 except KeyError:
1256 pass
1256 pass
1257 else:
1257 else:
1258 d[v] = v
1258 d[v] = v
1259
1259
1260 benches = [
1260 benches = [
1261 (doinit, 'init'),
1261 (doinit, 'init'),
1262 (dogets, 'gets'),
1262 (dogets, 'gets'),
1263 (dosets, 'sets'),
1263 (dosets, 'sets'),
1264 (domixed, 'mixed')
1264 (domixed, 'mixed')
1265 ]
1265 ]
1266
1266
1267 for fn, title in benches:
1267 for fn, title in benches:
1268 timer, fm = gettimer(ui, opts)
1268 timer, fm = gettimer(ui, opts)
1269 timer(fn, title=title)
1269 timer(fn, title=title)
1270 fm.end()
1270 fm.end()
1271
1271
1272 @command('perfwrite', formatteropts)
1273 def perfwrite(ui, repo, **opts):
1274 """microbenchmark ui.write
1275 """
1276 timer, fm = gettimer(ui, opts)
1277 def write():
1278 for i in range(100000):
1279 ui.write(('Testing write performance\n'))
1280 timer(write)
1281 fm.end()
1282
1272 def uisetup(ui):
1283 def uisetup(ui):
1273 if (util.safehasattr(cmdutil, 'openrevlog') and
1284 if (util.safehasattr(cmdutil, 'openrevlog') and
1274 not util.safehasattr(commands, 'debugrevlogopts')):
1285 not util.safehasattr(commands, 'debugrevlogopts')):
1275 # for "historical portability":
1286 # for "historical portability":
1276 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1287 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1277 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1288 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1278 # openrevlog() should cause failure, because it has been
1289 # openrevlog() should cause failure, because it has been
1279 # available since 3.5 (or 49c583ca48c4).
1290 # available since 3.5 (or 49c583ca48c4).
1280 def openrevlog(orig, repo, cmd, file_, opts):
1291 def openrevlog(orig, repo, cmd, file_, opts):
1281 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1292 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1282 raise error.Abort("This version doesn't support --dir option",
1293 raise error.Abort("This version doesn't support --dir option",
1283 hint="use 3.5 or later")
1294 hint="use 3.5 or later")
1284 return orig(repo, cmd, file_, opts)
1295 return orig(repo, cmd, file_, opts)
1285 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1296 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
@@ -1,166 +1,167 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 perfbranchmap
54 perfbranchmap
55 benchmark the update of a branchmap
55 benchmark the update of a branchmap
56 perfcca (no help text available)
56 perfcca (no help text available)
57 perfchangegroupchangelog
57 perfchangegroupchangelog
58 Benchmark producing a changelog group for a changegroup.
58 Benchmark producing a changelog group for a changegroup.
59 perfchangeset
59 perfchangeset
60 (no help text available)
60 (no help text available)
61 perfctxfiles (no help text available)
61 perfctxfiles (no help text available)
62 perfdiffwd Profile diff of working directory changes
62 perfdiffwd Profile diff of working directory changes
63 perfdirfoldmap
63 perfdirfoldmap
64 (no help text available)
64 (no help text available)
65 perfdirs (no help text available)
65 perfdirs (no help text available)
66 perfdirstate (no help text available)
66 perfdirstate (no help text available)
67 perfdirstatedirs
67 perfdirstatedirs
68 (no help text available)
68 (no help text available)
69 perfdirstatefoldmap
69 perfdirstatefoldmap
70 (no help text available)
70 (no help text available)
71 perfdirstatewrite
71 perfdirstatewrite
72 (no help text available)
72 (no help text available)
73 perffncacheencode
73 perffncacheencode
74 (no help text available)
74 (no help text available)
75 perffncacheload
75 perffncacheload
76 (no help text available)
76 (no help text available)
77 perffncachewrite
77 perffncachewrite
78 (no help text available)
78 (no help text available)
79 perfheads (no help text available)
79 perfheads (no help text available)
80 perfindex (no help text available)
80 perfindex (no help text available)
81 perfloadmarkers
81 perfloadmarkers
82 benchmark the time to parse the on-disk markers for a repo
82 benchmark the time to parse the on-disk markers for a repo
83 perflog (no help text available)
83 perflog (no help text available)
84 perflookup (no help text available)
84 perflookup (no help text available)
85 perflrucachedict
85 perflrucachedict
86 (no help text available)
86 (no help text available)
87 perfmanifest (no help text available)
87 perfmanifest (no help text available)
88 perfmergecalculate
88 perfmergecalculate
89 (no help text available)
89 (no help text available)
90 perfmoonwalk benchmark walking the changelog backwards
90 perfmoonwalk benchmark walking the changelog backwards
91 perfnodelookup
91 perfnodelookup
92 (no help text available)
92 (no help text available)
93 perfparents (no help text available)
93 perfparents (no help text available)
94 perfpathcopies
94 perfpathcopies
95 (no help text available)
95 (no help text available)
96 perfrawfiles (no help text available)
96 perfrawfiles (no help text available)
97 perfrevlog Benchmark reading a series of revisions from a revlog.
97 perfrevlog Benchmark reading a series of revisions from a revlog.
98 perfrevlogchunks
98 perfrevlogchunks
99 Benchmark operations on revlog chunks.
99 Benchmark operations on revlog chunks.
100 perfrevlogrevision
100 perfrevlogrevision
101 Benchmark obtaining a revlog revision.
101 Benchmark obtaining a revlog revision.
102 perfrevrange (no help text available)
102 perfrevrange (no help text available)
103 perfrevset benchmark the execution time of a revset
103 perfrevset benchmark the execution time of a revset
104 perfstartup (no help text available)
104 perfstartup (no help text available)
105 perfstatus (no help text available)
105 perfstatus (no help text available)
106 perftags (no help text available)
106 perftags (no help text available)
107 perftemplating
107 perftemplating
108 (no help text available)
108 (no help text available)
109 perfvolatilesets
109 perfvolatilesets
110 benchmark the computation of various volatile set
110 benchmark the computation of various volatile set
111 perfwalk (no help text available)
111 perfwalk (no help text available)
112 perfwrite microbenchmark ui.write
112
113
113 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
114 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
114 $ hg perfaddremove
115 $ hg perfaddremove
115 $ hg perfancestors
116 $ hg perfancestors
116 $ hg perfancestorset 2
117 $ hg perfancestorset 2
117 $ hg perfannotate a
118 $ hg perfannotate a
118 $ hg perfbdiff -c 1
119 $ hg perfbdiff -c 1
119 $ hg perfbdiff --alldata 1
120 $ hg perfbdiff --alldata 1
120 $ hg perfbranchmap
121 $ hg perfbranchmap
121 $ hg perfcca
122 $ hg perfcca
122 $ hg perfchangegroupchangelog
123 $ hg perfchangegroupchangelog
123 $ hg perfchangeset 2
124 $ hg perfchangeset 2
124 $ hg perfctxfiles 2
125 $ hg perfctxfiles 2
125 $ hg perfdiffwd
126 $ hg perfdiffwd
126 $ hg perfdirfoldmap
127 $ hg perfdirfoldmap
127 $ hg perfdirs
128 $ hg perfdirs
128 $ hg perfdirstate
129 $ hg perfdirstate
129 $ hg perfdirstatedirs
130 $ hg perfdirstatedirs
130 $ hg perfdirstatefoldmap
131 $ hg perfdirstatefoldmap
131 $ hg perfdirstatewrite
132 $ hg perfdirstatewrite
132 $ hg perffncacheencode
133 $ hg perffncacheencode
133 $ hg perffncacheload
134 $ hg perffncacheload
134 $ hg perffncachewrite
135 $ hg perffncachewrite
135 $ hg perfheads
136 $ hg perfheads
136 $ hg perfindex
137 $ hg perfindex
137 $ hg perfloadmarkers
138 $ hg perfloadmarkers
138 $ hg perflog
139 $ hg perflog
139 $ hg perflookup 2
140 $ hg perflookup 2
140 $ hg perflrucache
141 $ hg perflrucache
141 $ hg perfmanifest 2
142 $ hg perfmanifest 2
142 $ hg perfmergecalculate -r 3
143 $ hg perfmergecalculate -r 3
143 $ hg perfmoonwalk
144 $ hg perfmoonwalk
144 $ hg perfnodelookup 2
145 $ hg perfnodelookup 2
145 $ hg perfpathcopies 1 2
146 $ hg perfpathcopies 1 2
146 $ hg perfrawfiles 2
147 $ hg perfrawfiles 2
147 $ hg perfrevlog .hg/store/data/a.i
148 $ hg perfrevlog .hg/store/data/a.i
148 $ hg perfrevlogrevision -m 0
149 $ hg perfrevlogrevision -m 0
149 $ hg perfrevlogchunks -c
150 $ hg perfrevlogchunks -c
150 $ hg perfrevrange
151 $ hg perfrevrange
151 $ hg perfrevset 'all()'
152 $ hg perfrevset 'all()'
152 $ hg perfstartup
153 $ hg perfstartup
153 $ hg perfstatus
154 $ hg perfstatus
154 $ hg perftags
155 $ hg perftags
155 $ hg perftemplating
156 $ hg perftemplating
156 $ hg perfvolatilesets
157 $ hg perfvolatilesets
157 $ hg perfwalk
158 $ hg perfwalk
158 $ hg perfparents
159 $ hg perfparents
159
160
160 Check perf.py for historical portability
161 Check perf.py for historical portability
161
162
162 $ cd "$TESTDIR/.."
163 $ cd "$TESTDIR/.."
163
164
164 $ (hg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
165 $ (hg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
165 > hg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
166 > hg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
166 > "$TESTDIR"/check-perf-code.py contrib/perf.py
167 > "$TESTDIR"/check-perf-code.py contrib/perf.py
General Comments 0
You need to be logged in to leave comments. Login now