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