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