##// END OF EJS Templates
perfbranchmap: add an option to purge the revbranch cache...
marmoute -
r32710:326c0e2c default
parent child Browse files
Show More
@@ -1,1475 +1,1479
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 # perf commands
360 # perf commands
361
361
362 @command('perfwalk', formatteropts)
362 @command('perfwalk', formatteropts)
363 def perfwalk(ui, repo, *pats, **opts):
363 def perfwalk(ui, repo, *pats, **opts):
364 timer, fm = gettimer(ui, opts)
364 timer, fm = gettimer(ui, opts)
365 try:
365 try:
366 m = scmutil.match(repo[None], pats, {})
366 m = scmutil.match(repo[None], pats, {})
367 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
367 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
368 except Exception:
368 except Exception:
369 try:
369 try:
370 m = scmutil.match(repo[None], pats, {})
370 m = scmutil.match(repo[None], pats, {})
371 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
371 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
372 except Exception:
372 except Exception:
373 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
373 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
374 fm.end()
374 fm.end()
375
375
376 @command('perfannotate', formatteropts)
376 @command('perfannotate', formatteropts)
377 def perfannotate(ui, repo, f, **opts):
377 def perfannotate(ui, repo, f, **opts):
378 timer, fm = gettimer(ui, opts)
378 timer, fm = gettimer(ui, opts)
379 fc = repo['.'][f]
379 fc = repo['.'][f]
380 timer(lambda: len(fc.annotate(True)))
380 timer(lambda: len(fc.annotate(True)))
381 fm.end()
381 fm.end()
382
382
383 @command('perfstatus',
383 @command('perfstatus',
384 [('u', 'unknown', False,
384 [('u', 'unknown', False,
385 'ask status to look for unknown files')] + formatteropts)
385 'ask status to look for unknown files')] + formatteropts)
386 def perfstatus(ui, repo, **opts):
386 def perfstatus(ui, repo, **opts):
387 #m = match.always(repo.root, repo.getcwd())
387 #m = match.always(repo.root, repo.getcwd())
388 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
388 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
389 # False))))
389 # False))))
390 timer, fm = gettimer(ui, opts)
390 timer, fm = gettimer(ui, opts)
391 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
391 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
392 fm.end()
392 fm.end()
393
393
394 @command('perfaddremove', formatteropts)
394 @command('perfaddremove', formatteropts)
395 def perfaddremove(ui, repo, **opts):
395 def perfaddremove(ui, repo, **opts):
396 timer, fm = gettimer(ui, opts)
396 timer, fm = gettimer(ui, opts)
397 try:
397 try:
398 oldquiet = repo.ui.quiet
398 oldquiet = repo.ui.quiet
399 repo.ui.quiet = True
399 repo.ui.quiet = True
400 matcher = scmutil.match(repo[None])
400 matcher = scmutil.match(repo[None])
401 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
401 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
402 finally:
402 finally:
403 repo.ui.quiet = oldquiet
403 repo.ui.quiet = oldquiet
404 fm.end()
404 fm.end()
405
405
406 def clearcaches(cl):
406 def clearcaches(cl):
407 # behave somewhat consistently across internal API changes
407 # behave somewhat consistently across internal API changes
408 if util.safehasattr(cl, 'clearcaches'):
408 if util.safehasattr(cl, 'clearcaches'):
409 cl.clearcaches()
409 cl.clearcaches()
410 elif util.safehasattr(cl, '_nodecache'):
410 elif util.safehasattr(cl, '_nodecache'):
411 from mercurial.node import nullid, nullrev
411 from mercurial.node import nullid, nullrev
412 cl._nodecache = {nullid: nullrev}
412 cl._nodecache = {nullid: nullrev}
413 cl._nodepos = None
413 cl._nodepos = None
414
414
415 @command('perfheads', formatteropts)
415 @command('perfheads', formatteropts)
416 def perfheads(ui, repo, **opts):
416 def perfheads(ui, repo, **opts):
417 timer, fm = gettimer(ui, opts)
417 timer, fm = gettimer(ui, opts)
418 cl = repo.changelog
418 cl = repo.changelog
419 def d():
419 def d():
420 len(cl.headrevs())
420 len(cl.headrevs())
421 clearcaches(cl)
421 clearcaches(cl)
422 timer(d)
422 timer(d)
423 fm.end()
423 fm.end()
424
424
425 @command('perftags', formatteropts)
425 @command('perftags', formatteropts)
426 def perftags(ui, repo, **opts):
426 def perftags(ui, repo, **opts):
427 import mercurial.changelog
427 import mercurial.changelog
428 import mercurial.manifest
428 import mercurial.manifest
429 timer, fm = gettimer(ui, opts)
429 timer, fm = gettimer(ui, opts)
430 svfs = getsvfs(repo)
430 svfs = getsvfs(repo)
431 repocleartagscache = repocleartagscachefunc(repo)
431 repocleartagscache = repocleartagscachefunc(repo)
432 def t():
432 def t():
433 repo.changelog = mercurial.changelog.changelog(svfs)
433 repo.changelog = mercurial.changelog.changelog(svfs)
434 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
434 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
435 repocleartagscache()
435 repocleartagscache()
436 return len(repo.tags())
436 return len(repo.tags())
437 timer(t)
437 timer(t)
438 fm.end()
438 fm.end()
439
439
440 @command('perfancestors', formatteropts)
440 @command('perfancestors', formatteropts)
441 def perfancestors(ui, repo, **opts):
441 def perfancestors(ui, repo, **opts):
442 timer, fm = gettimer(ui, opts)
442 timer, fm = gettimer(ui, opts)
443 heads = repo.changelog.headrevs()
443 heads = repo.changelog.headrevs()
444 def d():
444 def d():
445 for a in repo.changelog.ancestors(heads):
445 for a in repo.changelog.ancestors(heads):
446 pass
446 pass
447 timer(d)
447 timer(d)
448 fm.end()
448 fm.end()
449
449
450 @command('perfancestorset', formatteropts)
450 @command('perfancestorset', formatteropts)
451 def perfancestorset(ui, repo, revset, **opts):
451 def perfancestorset(ui, repo, revset, **opts):
452 timer, fm = gettimer(ui, opts)
452 timer, fm = gettimer(ui, opts)
453 revs = repo.revs(revset)
453 revs = repo.revs(revset)
454 heads = repo.changelog.headrevs()
454 heads = repo.changelog.headrevs()
455 def d():
455 def d():
456 s = repo.changelog.ancestors(heads)
456 s = repo.changelog.ancestors(heads)
457 for rev in revs:
457 for rev in revs:
458 rev in s
458 rev in s
459 timer(d)
459 timer(d)
460 fm.end()
460 fm.end()
461
461
462 @command('perfchangegroupchangelog', formatteropts +
462 @command('perfchangegroupchangelog', formatteropts +
463 [('', 'version', '02', 'changegroup version'),
463 [('', 'version', '02', 'changegroup version'),
464 ('r', 'rev', '', 'revisions to add to changegroup')])
464 ('r', 'rev', '', 'revisions to add to changegroup')])
465 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
465 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
466 """Benchmark producing a changelog group for a changegroup.
466 """Benchmark producing a changelog group for a changegroup.
467
467
468 This measures the time spent processing the changelog during a
468 This measures the time spent processing the changelog during a
469 bundle operation. This occurs during `hg bundle` and on a server
469 bundle operation. This occurs during `hg bundle` and on a server
470 processing a `getbundle` wire protocol request (handles clones
470 processing a `getbundle` wire protocol request (handles clones
471 and pull requests).
471 and pull requests).
472
472
473 By default, all revisions are added to the changegroup.
473 By default, all revisions are added to the changegroup.
474 """
474 """
475 cl = repo.changelog
475 cl = repo.changelog
476 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
476 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
477 bundler = changegroup.getbundler(version, repo)
477 bundler = changegroup.getbundler(version, repo)
478
478
479 def lookup(node):
479 def lookup(node):
480 # The real bundler reads the revision in order to access the
480 # The real bundler reads the revision in order to access the
481 # manifest node and files list. Do that here.
481 # manifest node and files list. Do that here.
482 cl.read(node)
482 cl.read(node)
483 return node
483 return node
484
484
485 def d():
485 def d():
486 for chunk in bundler.group(revs, cl, lookup):
486 for chunk in bundler.group(revs, cl, lookup):
487 pass
487 pass
488
488
489 timer, fm = gettimer(ui, opts)
489 timer, fm = gettimer(ui, opts)
490 timer(d)
490 timer(d)
491 fm.end()
491 fm.end()
492
492
493 @command('perfdirs', formatteropts)
493 @command('perfdirs', formatteropts)
494 def perfdirs(ui, repo, **opts):
494 def perfdirs(ui, repo, **opts):
495 timer, fm = gettimer(ui, opts)
495 timer, fm = gettimer(ui, opts)
496 dirstate = repo.dirstate
496 dirstate = repo.dirstate
497 'a' in dirstate
497 'a' in dirstate
498 def d():
498 def d():
499 dirstate.dirs()
499 dirstate.dirs()
500 del dirstate._dirs
500 del dirstate._dirs
501 timer(d)
501 timer(d)
502 fm.end()
502 fm.end()
503
503
504 @command('perfdirstate', formatteropts)
504 @command('perfdirstate', formatteropts)
505 def perfdirstate(ui, repo, **opts):
505 def perfdirstate(ui, repo, **opts):
506 timer, fm = gettimer(ui, opts)
506 timer, fm = gettimer(ui, opts)
507 "a" in repo.dirstate
507 "a" in repo.dirstate
508 def d():
508 def d():
509 repo.dirstate.invalidate()
509 repo.dirstate.invalidate()
510 "a" in repo.dirstate
510 "a" in repo.dirstate
511 timer(d)
511 timer(d)
512 fm.end()
512 fm.end()
513
513
514 @command('perfdirstatedirs', formatteropts)
514 @command('perfdirstatedirs', formatteropts)
515 def perfdirstatedirs(ui, repo, **opts):
515 def perfdirstatedirs(ui, repo, **opts):
516 timer, fm = gettimer(ui, opts)
516 timer, fm = gettimer(ui, opts)
517 "a" in repo.dirstate
517 "a" in repo.dirstate
518 def d():
518 def d():
519 "a" in repo.dirstate._dirs
519 "a" in repo.dirstate._dirs
520 del repo.dirstate._dirs
520 del repo.dirstate._dirs
521 timer(d)
521 timer(d)
522 fm.end()
522 fm.end()
523
523
524 @command('perfdirstatefoldmap', formatteropts)
524 @command('perfdirstatefoldmap', formatteropts)
525 def perfdirstatefoldmap(ui, repo, **opts):
525 def perfdirstatefoldmap(ui, repo, **opts):
526 timer, fm = gettimer(ui, opts)
526 timer, fm = gettimer(ui, opts)
527 dirstate = repo.dirstate
527 dirstate = repo.dirstate
528 'a' in dirstate
528 'a' in dirstate
529 def d():
529 def d():
530 dirstate._filefoldmap.get('a')
530 dirstate._filefoldmap.get('a')
531 del dirstate._filefoldmap
531 del dirstate._filefoldmap
532 timer(d)
532 timer(d)
533 fm.end()
533 fm.end()
534
534
535 @command('perfdirfoldmap', formatteropts)
535 @command('perfdirfoldmap', formatteropts)
536 def perfdirfoldmap(ui, repo, **opts):
536 def perfdirfoldmap(ui, repo, **opts):
537 timer, fm = gettimer(ui, opts)
537 timer, fm = gettimer(ui, opts)
538 dirstate = repo.dirstate
538 dirstate = repo.dirstate
539 'a' in dirstate
539 'a' in dirstate
540 def d():
540 def d():
541 dirstate._dirfoldmap.get('a')
541 dirstate._dirfoldmap.get('a')
542 del dirstate._dirfoldmap
542 del dirstate._dirfoldmap
543 del dirstate._dirs
543 del dirstate._dirs
544 timer(d)
544 timer(d)
545 fm.end()
545 fm.end()
546
546
547 @command('perfdirstatewrite', formatteropts)
547 @command('perfdirstatewrite', formatteropts)
548 def perfdirstatewrite(ui, repo, **opts):
548 def perfdirstatewrite(ui, repo, **opts):
549 timer, fm = gettimer(ui, opts)
549 timer, fm = gettimer(ui, opts)
550 ds = repo.dirstate
550 ds = repo.dirstate
551 "a" in ds
551 "a" in ds
552 def d():
552 def d():
553 ds._dirty = True
553 ds._dirty = True
554 ds.write(repo.currenttransaction())
554 ds.write(repo.currenttransaction())
555 timer(d)
555 timer(d)
556 fm.end()
556 fm.end()
557
557
558 @command('perfmergecalculate',
558 @command('perfmergecalculate',
559 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
559 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
560 def perfmergecalculate(ui, repo, rev, **opts):
560 def perfmergecalculate(ui, repo, rev, **opts):
561 timer, fm = gettimer(ui, opts)
561 timer, fm = gettimer(ui, opts)
562 wctx = repo[None]
562 wctx = repo[None]
563 rctx = scmutil.revsingle(repo, rev, rev)
563 rctx = scmutil.revsingle(repo, rev, rev)
564 ancestor = wctx.ancestor(rctx)
564 ancestor = wctx.ancestor(rctx)
565 # we don't want working dir files to be stat'd in the benchmark, so prime
565 # we don't want working dir files to be stat'd in the benchmark, so prime
566 # that cache
566 # that cache
567 wctx.dirty()
567 wctx.dirty()
568 def d():
568 def d():
569 # acceptremote is True because we don't want prompts in the middle of
569 # acceptremote is True because we don't want prompts in the middle of
570 # our benchmark
570 # our benchmark
571 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
571 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
572 acceptremote=True, followcopies=True)
572 acceptremote=True, followcopies=True)
573 timer(d)
573 timer(d)
574 fm.end()
574 fm.end()
575
575
576 @command('perfpathcopies', [], "REV REV")
576 @command('perfpathcopies', [], "REV REV")
577 def perfpathcopies(ui, repo, rev1, rev2, **opts):
577 def perfpathcopies(ui, repo, rev1, rev2, **opts):
578 timer, fm = gettimer(ui, opts)
578 timer, fm = gettimer(ui, opts)
579 ctx1 = scmutil.revsingle(repo, rev1, rev1)
579 ctx1 = scmutil.revsingle(repo, rev1, rev1)
580 ctx2 = scmutil.revsingle(repo, rev2, rev2)
580 ctx2 = scmutil.revsingle(repo, rev2, rev2)
581 def d():
581 def d():
582 copies.pathcopies(ctx1, ctx2)
582 copies.pathcopies(ctx1, ctx2)
583 timer(d)
583 timer(d)
584 fm.end()
584 fm.end()
585
585
586 @command('perfphases', [], "")
586 @command('perfphases', [], "")
587 def perfphases(ui, repo, **opts):
587 def perfphases(ui, repo, **opts):
588 """benchmark phasesets computation"""
588 """benchmark phasesets computation"""
589 timer, fm = gettimer(ui, opts)
589 timer, fm = gettimer(ui, opts)
590 phases = repo._phasecache
590 phases = repo._phasecache
591 def d():
591 def d():
592 phases.invalidate()
592 phases.invalidate()
593 phases.loadphaserevs(repo)
593 phases.loadphaserevs(repo)
594 timer(d)
594 timer(d)
595 fm.end()
595 fm.end()
596
596
597 @command('perfmanifest', [], 'REV')
597 @command('perfmanifest', [], 'REV')
598 def perfmanifest(ui, repo, rev, **opts):
598 def perfmanifest(ui, repo, rev, **opts):
599 timer, fm = gettimer(ui, opts)
599 timer, fm = gettimer(ui, opts)
600 ctx = scmutil.revsingle(repo, rev, rev)
600 ctx = scmutil.revsingle(repo, rev, rev)
601 t = ctx.manifestnode()
601 t = ctx.manifestnode()
602 def d():
602 def d():
603 repo.manifestlog.clearcaches()
603 repo.manifestlog.clearcaches()
604 repo.manifestlog[t].read()
604 repo.manifestlog[t].read()
605 timer(d)
605 timer(d)
606 fm.end()
606 fm.end()
607
607
608 @command('perfchangeset', formatteropts)
608 @command('perfchangeset', formatteropts)
609 def perfchangeset(ui, repo, rev, **opts):
609 def perfchangeset(ui, repo, rev, **opts):
610 timer, fm = gettimer(ui, opts)
610 timer, fm = gettimer(ui, opts)
611 n = repo[rev].node()
611 n = repo[rev].node()
612 def d():
612 def d():
613 repo.changelog.read(n)
613 repo.changelog.read(n)
614 #repo.changelog._cache = None
614 #repo.changelog._cache = None
615 timer(d)
615 timer(d)
616 fm.end()
616 fm.end()
617
617
618 @command('perfindex', formatteropts)
618 @command('perfindex', formatteropts)
619 def perfindex(ui, repo, **opts):
619 def perfindex(ui, repo, **opts):
620 import mercurial.revlog
620 import mercurial.revlog
621 timer, fm = gettimer(ui, opts)
621 timer, fm = gettimer(ui, opts)
622 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
622 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
623 n = repo["tip"].node()
623 n = repo["tip"].node()
624 svfs = getsvfs(repo)
624 svfs = getsvfs(repo)
625 def d():
625 def d():
626 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
626 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
627 cl.rev(n)
627 cl.rev(n)
628 timer(d)
628 timer(d)
629 fm.end()
629 fm.end()
630
630
631 @command('perfstartup', formatteropts)
631 @command('perfstartup', formatteropts)
632 def perfstartup(ui, repo, **opts):
632 def perfstartup(ui, repo, **opts):
633 timer, fm = gettimer(ui, opts)
633 timer, fm = gettimer(ui, opts)
634 cmd = sys.argv[0]
634 cmd = sys.argv[0]
635 def d():
635 def d():
636 if os.name != 'nt':
636 if os.name != 'nt':
637 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
637 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
638 else:
638 else:
639 os.environ['HGRCPATH'] = ''
639 os.environ['HGRCPATH'] = ''
640 os.system("%s version -q > NUL" % cmd)
640 os.system("%s version -q > NUL" % cmd)
641 timer(d)
641 timer(d)
642 fm.end()
642 fm.end()
643
643
644 @command('perfparents', formatteropts)
644 @command('perfparents', formatteropts)
645 def perfparents(ui, repo, **opts):
645 def perfparents(ui, repo, **opts):
646 timer, fm = gettimer(ui, opts)
646 timer, fm = gettimer(ui, opts)
647 # control the number of commits perfparents iterates over
647 # control the number of commits perfparents iterates over
648 # experimental config: perf.parentscount
648 # experimental config: perf.parentscount
649 count = getint(ui, "perf", "parentscount", 1000)
649 count = getint(ui, "perf", "parentscount", 1000)
650 if len(repo.changelog) < count:
650 if len(repo.changelog) < count:
651 raise error.Abort("repo needs %d commits for this test" % count)
651 raise error.Abort("repo needs %d commits for this test" % count)
652 repo = repo.unfiltered()
652 repo = repo.unfiltered()
653 nl = [repo.changelog.node(i) for i in xrange(count)]
653 nl = [repo.changelog.node(i) for i in xrange(count)]
654 def d():
654 def d():
655 for n in nl:
655 for n in nl:
656 repo.changelog.parents(n)
656 repo.changelog.parents(n)
657 timer(d)
657 timer(d)
658 fm.end()
658 fm.end()
659
659
660 @command('perfctxfiles', formatteropts)
660 @command('perfctxfiles', formatteropts)
661 def perfctxfiles(ui, repo, x, **opts):
661 def perfctxfiles(ui, repo, x, **opts):
662 x = int(x)
662 x = int(x)
663 timer, fm = gettimer(ui, opts)
663 timer, fm = gettimer(ui, opts)
664 def d():
664 def d():
665 len(repo[x].files())
665 len(repo[x].files())
666 timer(d)
666 timer(d)
667 fm.end()
667 fm.end()
668
668
669 @command('perfrawfiles', formatteropts)
669 @command('perfrawfiles', formatteropts)
670 def perfrawfiles(ui, repo, x, **opts):
670 def perfrawfiles(ui, repo, x, **opts):
671 x = int(x)
671 x = int(x)
672 timer, fm = gettimer(ui, opts)
672 timer, fm = gettimer(ui, opts)
673 cl = repo.changelog
673 cl = repo.changelog
674 def d():
674 def d():
675 len(cl.read(x)[3])
675 len(cl.read(x)[3])
676 timer(d)
676 timer(d)
677 fm.end()
677 fm.end()
678
678
679 @command('perflookup', formatteropts)
679 @command('perflookup', formatteropts)
680 def perflookup(ui, repo, rev, **opts):
680 def perflookup(ui, repo, rev, **opts):
681 timer, fm = gettimer(ui, opts)
681 timer, fm = gettimer(ui, opts)
682 timer(lambda: len(repo.lookup(rev)))
682 timer(lambda: len(repo.lookup(rev)))
683 fm.end()
683 fm.end()
684
684
685 @command('perfrevrange', formatteropts)
685 @command('perfrevrange', formatteropts)
686 def perfrevrange(ui, repo, *specs, **opts):
686 def perfrevrange(ui, repo, *specs, **opts):
687 timer, fm = gettimer(ui, opts)
687 timer, fm = gettimer(ui, opts)
688 revrange = scmutil.revrange
688 revrange = scmutil.revrange
689 timer(lambda: len(revrange(repo, specs)))
689 timer(lambda: len(revrange(repo, specs)))
690 fm.end()
690 fm.end()
691
691
692 @command('perfnodelookup', formatteropts)
692 @command('perfnodelookup', formatteropts)
693 def perfnodelookup(ui, repo, rev, **opts):
693 def perfnodelookup(ui, repo, rev, **opts):
694 timer, fm = gettimer(ui, opts)
694 timer, fm = gettimer(ui, opts)
695 import mercurial.revlog
695 import mercurial.revlog
696 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
696 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
697 n = repo[rev].node()
697 n = repo[rev].node()
698 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
698 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
699 def d():
699 def d():
700 cl.rev(n)
700 cl.rev(n)
701 clearcaches(cl)
701 clearcaches(cl)
702 timer(d)
702 timer(d)
703 fm.end()
703 fm.end()
704
704
705 @command('perflog',
705 @command('perflog',
706 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
706 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
707 def perflog(ui, repo, rev=None, **opts):
707 def perflog(ui, repo, rev=None, **opts):
708 if rev is None:
708 if rev is None:
709 rev=[]
709 rev=[]
710 timer, fm = gettimer(ui, opts)
710 timer, fm = gettimer(ui, opts)
711 ui.pushbuffer()
711 ui.pushbuffer()
712 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
712 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
713 copies=opts.get('rename')))
713 copies=opts.get('rename')))
714 ui.popbuffer()
714 ui.popbuffer()
715 fm.end()
715 fm.end()
716
716
717 @command('perfmoonwalk', formatteropts)
717 @command('perfmoonwalk', formatteropts)
718 def perfmoonwalk(ui, repo, **opts):
718 def perfmoonwalk(ui, repo, **opts):
719 """benchmark walking the changelog backwards
719 """benchmark walking the changelog backwards
720
720
721 This also loads the changelog data for each revision in the changelog.
721 This also loads the changelog data for each revision in the changelog.
722 """
722 """
723 timer, fm = gettimer(ui, opts)
723 timer, fm = gettimer(ui, opts)
724 def moonwalk():
724 def moonwalk():
725 for i in xrange(len(repo), -1, -1):
725 for i in xrange(len(repo), -1, -1):
726 ctx = repo[i]
726 ctx = repo[i]
727 ctx.branch() # read changelog data (in addition to the index)
727 ctx.branch() # read changelog data (in addition to the index)
728 timer(moonwalk)
728 timer(moonwalk)
729 fm.end()
729 fm.end()
730
730
731 @command('perftemplating', formatteropts)
731 @command('perftemplating', formatteropts)
732 def perftemplating(ui, repo, rev=None, **opts):
732 def perftemplating(ui, repo, rev=None, **opts):
733 if rev is None:
733 if rev is None:
734 rev=[]
734 rev=[]
735 timer, fm = gettimer(ui, opts)
735 timer, fm = gettimer(ui, opts)
736 ui.pushbuffer()
736 ui.pushbuffer()
737 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
737 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
738 template='{date|shortdate} [{rev}:{node|short}]'
738 template='{date|shortdate} [{rev}:{node|short}]'
739 ' {author|person}: {desc|firstline}\n'))
739 ' {author|person}: {desc|firstline}\n'))
740 ui.popbuffer()
740 ui.popbuffer()
741 fm.end()
741 fm.end()
742
742
743 @command('perfcca', formatteropts)
743 @command('perfcca', formatteropts)
744 def perfcca(ui, repo, **opts):
744 def perfcca(ui, repo, **opts):
745 timer, fm = gettimer(ui, opts)
745 timer, fm = gettimer(ui, opts)
746 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
746 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
747 fm.end()
747 fm.end()
748
748
749 @command('perffncacheload', formatteropts)
749 @command('perffncacheload', formatteropts)
750 def perffncacheload(ui, repo, **opts):
750 def perffncacheload(ui, repo, **opts):
751 timer, fm = gettimer(ui, opts)
751 timer, fm = gettimer(ui, opts)
752 s = repo.store
752 s = repo.store
753 def d():
753 def d():
754 s.fncache._load()
754 s.fncache._load()
755 timer(d)
755 timer(d)
756 fm.end()
756 fm.end()
757
757
758 @command('perffncachewrite', formatteropts)
758 @command('perffncachewrite', formatteropts)
759 def perffncachewrite(ui, repo, **opts):
759 def perffncachewrite(ui, repo, **opts):
760 timer, fm = gettimer(ui, opts)
760 timer, fm = gettimer(ui, opts)
761 s = repo.store
761 s = repo.store
762 s.fncache._load()
762 s.fncache._load()
763 lock = repo.lock()
763 lock = repo.lock()
764 tr = repo.transaction('perffncachewrite')
764 tr = repo.transaction('perffncachewrite')
765 def d():
765 def d():
766 s.fncache._dirty = True
766 s.fncache._dirty = True
767 s.fncache.write(tr)
767 s.fncache.write(tr)
768 timer(d)
768 timer(d)
769 tr.close()
769 tr.close()
770 lock.release()
770 lock.release()
771 fm.end()
771 fm.end()
772
772
773 @command('perffncacheencode', formatteropts)
773 @command('perffncacheencode', formatteropts)
774 def perffncacheencode(ui, repo, **opts):
774 def perffncacheencode(ui, repo, **opts):
775 timer, fm = gettimer(ui, opts)
775 timer, fm = gettimer(ui, opts)
776 s = repo.store
776 s = repo.store
777 s.fncache._load()
777 s.fncache._load()
778 def d():
778 def d():
779 for p in s.fncache.entries:
779 for p in s.fncache.entries:
780 s.encode(p)
780 s.encode(p)
781 timer(d)
781 timer(d)
782 fm.end()
782 fm.end()
783
783
784 @command('perfbdiff', revlogopts + formatteropts + [
784 @command('perfbdiff', revlogopts + formatteropts + [
785 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
785 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
786 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
786 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
787 '-c|-m|FILE REV')
787 '-c|-m|FILE REV')
788 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
788 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
789 """benchmark a bdiff between revisions
789 """benchmark a bdiff between revisions
790
790
791 By default, benchmark a bdiff between its delta parent and itself.
791 By default, benchmark a bdiff between its delta parent and itself.
792
792
793 With ``--count``, benchmark bdiffs between delta parents and self for N
793 With ``--count``, benchmark bdiffs between delta parents and self for N
794 revisions starting at the specified revision.
794 revisions starting at the specified revision.
795
795
796 With ``--alldata``, assume the requested revision is a changeset and
796 With ``--alldata``, assume the requested revision is a changeset and
797 measure bdiffs for all changes related to that changeset (manifest
797 measure bdiffs for all changes related to that changeset (manifest
798 and filelogs).
798 and filelogs).
799 """
799 """
800 if opts['alldata']:
800 if opts['alldata']:
801 opts['changelog'] = True
801 opts['changelog'] = True
802
802
803 if opts.get('changelog') or opts.get('manifest'):
803 if opts.get('changelog') or opts.get('manifest'):
804 file_, rev = None, file_
804 file_, rev = None, file_
805 elif rev is None:
805 elif rev is None:
806 raise error.CommandError('perfbdiff', 'invalid arguments')
806 raise error.CommandError('perfbdiff', 'invalid arguments')
807
807
808 textpairs = []
808 textpairs = []
809
809
810 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
810 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
811
811
812 startrev = r.rev(r.lookup(rev))
812 startrev = r.rev(r.lookup(rev))
813 for rev in range(startrev, min(startrev + count, len(r) - 1)):
813 for rev in range(startrev, min(startrev + count, len(r) - 1)):
814 if opts['alldata']:
814 if opts['alldata']:
815 # Load revisions associated with changeset.
815 # Load revisions associated with changeset.
816 ctx = repo[rev]
816 ctx = repo[rev]
817 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
817 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
818 for pctx in ctx.parents():
818 for pctx in ctx.parents():
819 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
819 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
820 textpairs.append((pman, mtext))
820 textpairs.append((pman, mtext))
821
821
822 # Load filelog revisions by iterating manifest delta.
822 # Load filelog revisions by iterating manifest delta.
823 man = ctx.manifest()
823 man = ctx.manifest()
824 pman = ctx.p1().manifest()
824 pman = ctx.p1().manifest()
825 for filename, change in pman.diff(man).items():
825 for filename, change in pman.diff(man).items():
826 fctx = repo.file(filename)
826 fctx = repo.file(filename)
827 f1 = fctx.revision(change[0][0] or -1)
827 f1 = fctx.revision(change[0][0] or -1)
828 f2 = fctx.revision(change[1][0] or -1)
828 f2 = fctx.revision(change[1][0] or -1)
829 textpairs.append((f1, f2))
829 textpairs.append((f1, f2))
830 else:
830 else:
831 dp = r.deltaparent(rev)
831 dp = r.deltaparent(rev)
832 textpairs.append((r.revision(dp), r.revision(rev)))
832 textpairs.append((r.revision(dp), r.revision(rev)))
833
833
834 def d():
834 def d():
835 for pair in textpairs:
835 for pair in textpairs:
836 mdiff.textdiff(*pair)
836 mdiff.textdiff(*pair)
837
837
838 timer, fm = gettimer(ui, opts)
838 timer, fm = gettimer(ui, opts)
839 timer(d)
839 timer(d)
840 fm.end()
840 fm.end()
841
841
842 @command('perfdiffwd', formatteropts)
842 @command('perfdiffwd', formatteropts)
843 def perfdiffwd(ui, repo, **opts):
843 def perfdiffwd(ui, repo, **opts):
844 """Profile diff of working directory changes"""
844 """Profile diff of working directory changes"""
845 timer, fm = gettimer(ui, opts)
845 timer, fm = gettimer(ui, opts)
846 options = {
846 options = {
847 'w': 'ignore_all_space',
847 'w': 'ignore_all_space',
848 'b': 'ignore_space_change',
848 'b': 'ignore_space_change',
849 'B': 'ignore_blank_lines',
849 'B': 'ignore_blank_lines',
850 }
850 }
851
851
852 for diffopt in ('', 'w', 'b', 'B', 'wB'):
852 for diffopt in ('', 'w', 'b', 'B', 'wB'):
853 opts = dict((options[c], '1') for c in diffopt)
853 opts = dict((options[c], '1') for c in diffopt)
854 def d():
854 def d():
855 ui.pushbuffer()
855 ui.pushbuffer()
856 commands.diff(ui, repo, **opts)
856 commands.diff(ui, repo, **opts)
857 ui.popbuffer()
857 ui.popbuffer()
858 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
858 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
859 timer(d, title)
859 timer(d, title)
860 fm.end()
860 fm.end()
861
861
862 @command('perfrevlogindex', revlogopts + formatteropts,
862 @command('perfrevlogindex', revlogopts + formatteropts,
863 '-c|-m|FILE')
863 '-c|-m|FILE')
864 def perfrevlogindex(ui, repo, file_=None, **opts):
864 def perfrevlogindex(ui, repo, file_=None, **opts):
865 """Benchmark operations against a revlog index.
865 """Benchmark operations against a revlog index.
866
866
867 This tests constructing a revlog instance, reading index data,
867 This tests constructing a revlog instance, reading index data,
868 parsing index data, and performing various operations related to
868 parsing index data, and performing various operations related to
869 index data.
869 index data.
870 """
870 """
871
871
872 rl = cmdutil.openrevlog(repo, 'perfrevlogindex', file_, opts)
872 rl = cmdutil.openrevlog(repo, 'perfrevlogindex', file_, opts)
873
873
874 opener = getattr(rl, 'opener') # trick linter
874 opener = getattr(rl, 'opener') # trick linter
875 indexfile = rl.indexfile
875 indexfile = rl.indexfile
876 data = opener.read(indexfile)
876 data = opener.read(indexfile)
877
877
878 header = struct.unpack('>I', data[0:4])[0]
878 header = struct.unpack('>I', data[0:4])[0]
879 version = header & 0xFFFF
879 version = header & 0xFFFF
880 if version == 1:
880 if version == 1:
881 revlogio = revlog.revlogio()
881 revlogio = revlog.revlogio()
882 inline = header & (1 << 16)
882 inline = header & (1 << 16)
883 else:
883 else:
884 raise error.Abort(('unsupported revlog version: %d') % version)
884 raise error.Abort(('unsupported revlog version: %d') % version)
885
885
886 rllen = len(rl)
886 rllen = len(rl)
887
887
888 node0 = rl.node(0)
888 node0 = rl.node(0)
889 node25 = rl.node(rllen // 4)
889 node25 = rl.node(rllen // 4)
890 node50 = rl.node(rllen // 2)
890 node50 = rl.node(rllen // 2)
891 node75 = rl.node(rllen // 4 * 3)
891 node75 = rl.node(rllen // 4 * 3)
892 node100 = rl.node(rllen - 1)
892 node100 = rl.node(rllen - 1)
893
893
894 allrevs = range(rllen)
894 allrevs = range(rllen)
895 allrevsrev = list(reversed(allrevs))
895 allrevsrev = list(reversed(allrevs))
896 allnodes = [rl.node(rev) for rev in range(rllen)]
896 allnodes = [rl.node(rev) for rev in range(rllen)]
897 allnodesrev = list(reversed(allnodes))
897 allnodesrev = list(reversed(allnodes))
898
898
899 def constructor():
899 def constructor():
900 revlog.revlog(opener, indexfile)
900 revlog.revlog(opener, indexfile)
901
901
902 def read():
902 def read():
903 with opener(indexfile) as fh:
903 with opener(indexfile) as fh:
904 fh.read()
904 fh.read()
905
905
906 def parseindex():
906 def parseindex():
907 revlogio.parseindex(data, inline)
907 revlogio.parseindex(data, inline)
908
908
909 def getentry(revornode):
909 def getentry(revornode):
910 index = revlogio.parseindex(data, inline)[0]
910 index = revlogio.parseindex(data, inline)[0]
911 index[revornode]
911 index[revornode]
912
912
913 def getentries(revs, count=1):
913 def getentries(revs, count=1):
914 index = revlogio.parseindex(data, inline)[0]
914 index = revlogio.parseindex(data, inline)[0]
915
915
916 for i in range(count):
916 for i in range(count):
917 for rev in revs:
917 for rev in revs:
918 index[rev]
918 index[rev]
919
919
920 def resolvenode(node):
920 def resolvenode(node):
921 nodemap = revlogio.parseindex(data, inline)[1]
921 nodemap = revlogio.parseindex(data, inline)[1]
922 # This only works for the C code.
922 # This only works for the C code.
923 if nodemap is None:
923 if nodemap is None:
924 return
924 return
925
925
926 try:
926 try:
927 nodemap[node]
927 nodemap[node]
928 except error.RevlogError:
928 except error.RevlogError:
929 pass
929 pass
930
930
931 def resolvenodes(nodes, count=1):
931 def resolvenodes(nodes, count=1):
932 nodemap = revlogio.parseindex(data, inline)[1]
932 nodemap = revlogio.parseindex(data, inline)[1]
933 if nodemap is None:
933 if nodemap is None:
934 return
934 return
935
935
936 for i in range(count):
936 for i in range(count):
937 for node in nodes:
937 for node in nodes:
938 try:
938 try:
939 nodemap[node]
939 nodemap[node]
940 except error.RevlogError:
940 except error.RevlogError:
941 pass
941 pass
942
942
943 benches = [
943 benches = [
944 (constructor, 'revlog constructor'),
944 (constructor, 'revlog constructor'),
945 (read, 'read'),
945 (read, 'read'),
946 (parseindex, 'create index object'),
946 (parseindex, 'create index object'),
947 (lambda: getentry(0), 'retrieve index entry for rev 0'),
947 (lambda: getentry(0), 'retrieve index entry for rev 0'),
948 (lambda: resolvenode('a' * 20), 'look up missing node'),
948 (lambda: resolvenode('a' * 20), 'look up missing node'),
949 (lambda: resolvenode(node0), 'look up node at rev 0'),
949 (lambda: resolvenode(node0), 'look up node at rev 0'),
950 (lambda: resolvenode(node25), 'look up node at 1/4 len'),
950 (lambda: resolvenode(node25), 'look up node at 1/4 len'),
951 (lambda: resolvenode(node50), 'look up node at 1/2 len'),
951 (lambda: resolvenode(node50), 'look up node at 1/2 len'),
952 (lambda: resolvenode(node75), 'look up node at 3/4 len'),
952 (lambda: resolvenode(node75), 'look up node at 3/4 len'),
953 (lambda: resolvenode(node100), 'look up node at tip'),
953 (lambda: resolvenode(node100), 'look up node at tip'),
954 # 2x variation is to measure caching impact.
954 # 2x variation is to measure caching impact.
955 (lambda: resolvenodes(allnodes),
955 (lambda: resolvenodes(allnodes),
956 'look up all nodes (forward)'),
956 'look up all nodes (forward)'),
957 (lambda: resolvenodes(allnodes, 2),
957 (lambda: resolvenodes(allnodes, 2),
958 'look up all nodes 2x (forward)'),
958 'look up all nodes 2x (forward)'),
959 (lambda: resolvenodes(allnodesrev),
959 (lambda: resolvenodes(allnodesrev),
960 'look up all nodes (reverse)'),
960 'look up all nodes (reverse)'),
961 (lambda: resolvenodes(allnodesrev, 2),
961 (lambda: resolvenodes(allnodesrev, 2),
962 'look up all nodes 2x (reverse)'),
962 'look up all nodes 2x (reverse)'),
963 (lambda: getentries(allrevs),
963 (lambda: getentries(allrevs),
964 'retrieve all index entries (forward)'),
964 'retrieve all index entries (forward)'),
965 (lambda: getentries(allrevs, 2),
965 (lambda: getentries(allrevs, 2),
966 'retrieve all index entries 2x (forward)'),
966 'retrieve all index entries 2x (forward)'),
967 (lambda: getentries(allrevsrev),
967 (lambda: getentries(allrevsrev),
968 'retrieve all index entries (reverse)'),
968 'retrieve all index entries (reverse)'),
969 (lambda: getentries(allrevsrev, 2),
969 (lambda: getentries(allrevsrev, 2),
970 'retrieve all index entries 2x (reverse)'),
970 'retrieve all index entries 2x (reverse)'),
971 ]
971 ]
972
972
973 for fn, title in benches:
973 for fn, title in benches:
974 timer, fm = gettimer(ui, opts)
974 timer, fm = gettimer(ui, opts)
975 timer(fn, title=title)
975 timer(fn, title=title)
976 fm.end()
976 fm.end()
977
977
978 @command('perfrevlogrevisions', revlogopts + formatteropts +
978 @command('perfrevlogrevisions', revlogopts + formatteropts +
979 [('d', 'dist', 100, 'distance between the revisions'),
979 [('d', 'dist', 100, 'distance between the revisions'),
980 ('s', 'startrev', 0, 'revision to start reading at'),
980 ('s', 'startrev', 0, 'revision to start reading at'),
981 ('', 'reverse', False, 'read in reverse')],
981 ('', 'reverse', False, 'read in reverse')],
982 '-c|-m|FILE')
982 '-c|-m|FILE')
983 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
983 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
984 **opts):
984 **opts):
985 """Benchmark reading a series of revisions from a revlog.
985 """Benchmark reading a series of revisions from a revlog.
986
986
987 By default, we read every ``-d/--dist`` revision from 0 to tip of
987 By default, we read every ``-d/--dist`` revision from 0 to tip of
988 the specified revlog.
988 the specified revlog.
989
989
990 The start revision can be defined via ``-s/--startrev``.
990 The start revision can be defined via ``-s/--startrev``.
991 """
991 """
992 rl = cmdutil.openrevlog(repo, 'perfrevlogrevisions', file_, opts)
992 rl = cmdutil.openrevlog(repo, 'perfrevlogrevisions', file_, opts)
993 rllen = getlen(ui)(rl)
993 rllen = getlen(ui)(rl)
994
994
995 def d():
995 def d():
996 rl.clearcaches()
996 rl.clearcaches()
997
997
998 beginrev = startrev
998 beginrev = startrev
999 endrev = rllen
999 endrev = rllen
1000 dist = opts['dist']
1000 dist = opts['dist']
1001
1001
1002 if reverse:
1002 if reverse:
1003 beginrev, endrev = endrev, beginrev
1003 beginrev, endrev = endrev, beginrev
1004 dist = -1 * dist
1004 dist = -1 * dist
1005
1005
1006 for x in xrange(beginrev, endrev, dist):
1006 for x in xrange(beginrev, endrev, dist):
1007 # Old revisions don't support passing int.
1007 # Old revisions don't support passing int.
1008 n = rl.node(x)
1008 n = rl.node(x)
1009 rl.revision(n)
1009 rl.revision(n)
1010
1010
1011 timer, fm = gettimer(ui, opts)
1011 timer, fm = gettimer(ui, opts)
1012 timer(d)
1012 timer(d)
1013 fm.end()
1013 fm.end()
1014
1014
1015 @command('perfrevlogchunks', revlogopts + formatteropts +
1015 @command('perfrevlogchunks', revlogopts + formatteropts +
1016 [('e', 'engines', '', 'compression engines to use'),
1016 [('e', 'engines', '', 'compression engines to use'),
1017 ('s', 'startrev', 0, 'revision to start at')],
1017 ('s', 'startrev', 0, 'revision to start at')],
1018 '-c|-m|FILE')
1018 '-c|-m|FILE')
1019 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1019 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1020 """Benchmark operations on revlog chunks.
1020 """Benchmark operations on revlog chunks.
1021
1021
1022 Logically, each revlog is a collection of fulltext revisions. However,
1022 Logically, each revlog is a collection of fulltext revisions. However,
1023 stored within each revlog are "chunks" of possibly compressed data. This
1023 stored within each revlog are "chunks" of possibly compressed data. This
1024 data needs to be read and decompressed or compressed and written.
1024 data needs to be read and decompressed or compressed and written.
1025
1025
1026 This command measures the time it takes to read+decompress and recompress
1026 This command measures the time it takes to read+decompress and recompress
1027 chunks in a revlog. It effectively isolates I/O and compression performance.
1027 chunks in a revlog. It effectively isolates I/O and compression performance.
1028 For measurements of higher-level operations like resolving revisions,
1028 For measurements of higher-level operations like resolving revisions,
1029 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1029 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1030 """
1030 """
1031 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
1031 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
1032
1032
1033 # _chunkraw was renamed to _getsegmentforrevs.
1033 # _chunkraw was renamed to _getsegmentforrevs.
1034 try:
1034 try:
1035 segmentforrevs = rl._getsegmentforrevs
1035 segmentforrevs = rl._getsegmentforrevs
1036 except AttributeError:
1036 except AttributeError:
1037 segmentforrevs = rl._chunkraw
1037 segmentforrevs = rl._chunkraw
1038
1038
1039 # Verify engines argument.
1039 # Verify engines argument.
1040 if engines:
1040 if engines:
1041 engines = set(e.strip() for e in engines.split(','))
1041 engines = set(e.strip() for e in engines.split(','))
1042 for engine in engines:
1042 for engine in engines:
1043 try:
1043 try:
1044 util.compressionengines[engine]
1044 util.compressionengines[engine]
1045 except KeyError:
1045 except KeyError:
1046 raise error.Abort('unknown compression engine: %s' % engine)
1046 raise error.Abort('unknown compression engine: %s' % engine)
1047 else:
1047 else:
1048 engines = []
1048 engines = []
1049 for e in util.compengines:
1049 for e in util.compengines:
1050 engine = util.compengines[e]
1050 engine = util.compengines[e]
1051 try:
1051 try:
1052 if engine.available():
1052 if engine.available():
1053 engine.revlogcompressor().compress('dummy')
1053 engine.revlogcompressor().compress('dummy')
1054 engines.append(e)
1054 engines.append(e)
1055 except NotImplementedError:
1055 except NotImplementedError:
1056 pass
1056 pass
1057
1057
1058 revs = list(rl.revs(startrev, len(rl) - 1))
1058 revs = list(rl.revs(startrev, len(rl) - 1))
1059
1059
1060 def rlfh(rl):
1060 def rlfh(rl):
1061 if rl._inline:
1061 if rl._inline:
1062 return getsvfs(repo)(rl.indexfile)
1062 return getsvfs(repo)(rl.indexfile)
1063 else:
1063 else:
1064 return getsvfs(repo)(rl.datafile)
1064 return getsvfs(repo)(rl.datafile)
1065
1065
1066 def doread():
1066 def doread():
1067 rl.clearcaches()
1067 rl.clearcaches()
1068 for rev in revs:
1068 for rev in revs:
1069 segmentforrevs(rev, rev)
1069 segmentforrevs(rev, rev)
1070
1070
1071 def doreadcachedfh():
1071 def doreadcachedfh():
1072 rl.clearcaches()
1072 rl.clearcaches()
1073 fh = rlfh(rl)
1073 fh = rlfh(rl)
1074 for rev in revs:
1074 for rev in revs:
1075 segmentforrevs(rev, rev, df=fh)
1075 segmentforrevs(rev, rev, df=fh)
1076
1076
1077 def doreadbatch():
1077 def doreadbatch():
1078 rl.clearcaches()
1078 rl.clearcaches()
1079 segmentforrevs(revs[0], revs[-1])
1079 segmentforrevs(revs[0], revs[-1])
1080
1080
1081 def doreadbatchcachedfh():
1081 def doreadbatchcachedfh():
1082 rl.clearcaches()
1082 rl.clearcaches()
1083 fh = rlfh(rl)
1083 fh = rlfh(rl)
1084 segmentforrevs(revs[0], revs[-1], df=fh)
1084 segmentforrevs(revs[0], revs[-1], df=fh)
1085
1085
1086 def dochunk():
1086 def dochunk():
1087 rl.clearcaches()
1087 rl.clearcaches()
1088 fh = rlfh(rl)
1088 fh = rlfh(rl)
1089 for rev in revs:
1089 for rev in revs:
1090 rl._chunk(rev, df=fh)
1090 rl._chunk(rev, df=fh)
1091
1091
1092 chunks = [None]
1092 chunks = [None]
1093
1093
1094 def dochunkbatch():
1094 def dochunkbatch():
1095 rl.clearcaches()
1095 rl.clearcaches()
1096 fh = rlfh(rl)
1096 fh = rlfh(rl)
1097 # Save chunks as a side-effect.
1097 # Save chunks as a side-effect.
1098 chunks[0] = rl._chunks(revs, df=fh)
1098 chunks[0] = rl._chunks(revs, df=fh)
1099
1099
1100 def docompress(compressor):
1100 def docompress(compressor):
1101 rl.clearcaches()
1101 rl.clearcaches()
1102
1102
1103 try:
1103 try:
1104 # Swap in the requested compression engine.
1104 # Swap in the requested compression engine.
1105 oldcompressor = rl._compressor
1105 oldcompressor = rl._compressor
1106 rl._compressor = compressor
1106 rl._compressor = compressor
1107 for chunk in chunks[0]:
1107 for chunk in chunks[0]:
1108 rl.compress(chunk)
1108 rl.compress(chunk)
1109 finally:
1109 finally:
1110 rl._compressor = oldcompressor
1110 rl._compressor = oldcompressor
1111
1111
1112 benches = [
1112 benches = [
1113 (lambda: doread(), 'read'),
1113 (lambda: doread(), 'read'),
1114 (lambda: doreadcachedfh(), 'read w/ reused fd'),
1114 (lambda: doreadcachedfh(), 'read w/ reused fd'),
1115 (lambda: doreadbatch(), 'read batch'),
1115 (lambda: doreadbatch(), 'read batch'),
1116 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
1116 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
1117 (lambda: dochunk(), 'chunk'),
1117 (lambda: dochunk(), 'chunk'),
1118 (lambda: dochunkbatch(), 'chunk batch'),
1118 (lambda: dochunkbatch(), 'chunk batch'),
1119 ]
1119 ]
1120
1120
1121 for engine in sorted(engines):
1121 for engine in sorted(engines):
1122 compressor = util.compengines[engine].revlogcompressor()
1122 compressor = util.compengines[engine].revlogcompressor()
1123 benches.append((functools.partial(docompress, compressor),
1123 benches.append((functools.partial(docompress, compressor),
1124 'compress w/ %s' % engine))
1124 'compress w/ %s' % engine))
1125
1125
1126 for fn, title in benches:
1126 for fn, title in benches:
1127 timer, fm = gettimer(ui, opts)
1127 timer, fm = gettimer(ui, opts)
1128 timer(fn, title=title)
1128 timer(fn, title=title)
1129 fm.end()
1129 fm.end()
1130
1130
1131 @command('perfrevlogrevision', revlogopts + formatteropts +
1131 @command('perfrevlogrevision', revlogopts + formatteropts +
1132 [('', 'cache', False, 'use caches instead of clearing')],
1132 [('', 'cache', False, 'use caches instead of clearing')],
1133 '-c|-m|FILE REV')
1133 '-c|-m|FILE REV')
1134 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1134 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1135 """Benchmark obtaining a revlog revision.
1135 """Benchmark obtaining a revlog revision.
1136
1136
1137 Obtaining a revlog revision consists of roughly the following steps:
1137 Obtaining a revlog revision consists of roughly the following steps:
1138
1138
1139 1. Compute the delta chain
1139 1. Compute the delta chain
1140 2. Obtain the raw chunks for that delta chain
1140 2. Obtain the raw chunks for that delta chain
1141 3. Decompress each raw chunk
1141 3. Decompress each raw chunk
1142 4. Apply binary patches to obtain fulltext
1142 4. Apply binary patches to obtain fulltext
1143 5. Verify hash of fulltext
1143 5. Verify hash of fulltext
1144
1144
1145 This command measures the time spent in each of these phases.
1145 This command measures the time spent in each of these phases.
1146 """
1146 """
1147 if opts.get('changelog') or opts.get('manifest'):
1147 if opts.get('changelog') or opts.get('manifest'):
1148 file_, rev = None, file_
1148 file_, rev = None, file_
1149 elif rev is None:
1149 elif rev is None:
1150 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1150 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1151
1151
1152 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1152 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1153
1153
1154 # _chunkraw was renamed to _getsegmentforrevs.
1154 # _chunkraw was renamed to _getsegmentforrevs.
1155 try:
1155 try:
1156 segmentforrevs = r._getsegmentforrevs
1156 segmentforrevs = r._getsegmentforrevs
1157 except AttributeError:
1157 except AttributeError:
1158 segmentforrevs = r._chunkraw
1158 segmentforrevs = r._chunkraw
1159
1159
1160 node = r.lookup(rev)
1160 node = r.lookup(rev)
1161 rev = r.rev(node)
1161 rev = r.rev(node)
1162
1162
1163 def getrawchunks(data, chain):
1163 def getrawchunks(data, chain):
1164 start = r.start
1164 start = r.start
1165 length = r.length
1165 length = r.length
1166 inline = r._inline
1166 inline = r._inline
1167 iosize = r._io.size
1167 iosize = r._io.size
1168 buffer = util.buffer
1168 buffer = util.buffer
1169 offset = start(chain[0])
1169 offset = start(chain[0])
1170
1170
1171 chunks = []
1171 chunks = []
1172 ladd = chunks.append
1172 ladd = chunks.append
1173
1173
1174 for rev in chain:
1174 for rev in chain:
1175 chunkstart = start(rev)
1175 chunkstart = start(rev)
1176 if inline:
1176 if inline:
1177 chunkstart += (rev + 1) * iosize
1177 chunkstart += (rev + 1) * iosize
1178 chunklength = length(rev)
1178 chunklength = length(rev)
1179 ladd(buffer(data, chunkstart - offset, chunklength))
1179 ladd(buffer(data, chunkstart - offset, chunklength))
1180
1180
1181 return chunks
1181 return chunks
1182
1182
1183 def dodeltachain(rev):
1183 def dodeltachain(rev):
1184 if not cache:
1184 if not cache:
1185 r.clearcaches()
1185 r.clearcaches()
1186 r._deltachain(rev)
1186 r._deltachain(rev)
1187
1187
1188 def doread(chain):
1188 def doread(chain):
1189 if not cache:
1189 if not cache:
1190 r.clearcaches()
1190 r.clearcaches()
1191 segmentforrevs(chain[0], chain[-1])
1191 segmentforrevs(chain[0], chain[-1])
1192
1192
1193 def dorawchunks(data, chain):
1193 def dorawchunks(data, chain):
1194 if not cache:
1194 if not cache:
1195 r.clearcaches()
1195 r.clearcaches()
1196 getrawchunks(data, chain)
1196 getrawchunks(data, chain)
1197
1197
1198 def dodecompress(chunks):
1198 def dodecompress(chunks):
1199 decomp = r.decompress
1199 decomp = r.decompress
1200 for chunk in chunks:
1200 for chunk in chunks:
1201 decomp(chunk)
1201 decomp(chunk)
1202
1202
1203 def dopatch(text, bins):
1203 def dopatch(text, bins):
1204 if not cache:
1204 if not cache:
1205 r.clearcaches()
1205 r.clearcaches()
1206 mdiff.patches(text, bins)
1206 mdiff.patches(text, bins)
1207
1207
1208 def dohash(text):
1208 def dohash(text):
1209 if not cache:
1209 if not cache:
1210 r.clearcaches()
1210 r.clearcaches()
1211 r.checkhash(text, node, rev=rev)
1211 r.checkhash(text, node, rev=rev)
1212
1212
1213 def dorevision():
1213 def dorevision():
1214 if not cache:
1214 if not cache:
1215 r.clearcaches()
1215 r.clearcaches()
1216 r.revision(node)
1216 r.revision(node)
1217
1217
1218 chain = r._deltachain(rev)[0]
1218 chain = r._deltachain(rev)[0]
1219 data = segmentforrevs(chain[0], chain[-1])[1]
1219 data = segmentforrevs(chain[0], chain[-1])[1]
1220 rawchunks = getrawchunks(data, chain)
1220 rawchunks = getrawchunks(data, chain)
1221 bins = r._chunks(chain)
1221 bins = r._chunks(chain)
1222 text = str(bins[0])
1222 text = str(bins[0])
1223 bins = bins[1:]
1223 bins = bins[1:]
1224 text = mdiff.patches(text, bins)
1224 text = mdiff.patches(text, bins)
1225
1225
1226 benches = [
1226 benches = [
1227 (lambda: dorevision(), 'full'),
1227 (lambda: dorevision(), 'full'),
1228 (lambda: dodeltachain(rev), 'deltachain'),
1228 (lambda: dodeltachain(rev), 'deltachain'),
1229 (lambda: doread(chain), 'read'),
1229 (lambda: doread(chain), 'read'),
1230 (lambda: dorawchunks(data, chain), 'rawchunks'),
1230 (lambda: dorawchunks(data, chain), 'rawchunks'),
1231 (lambda: dodecompress(rawchunks), 'decompress'),
1231 (lambda: dodecompress(rawchunks), 'decompress'),
1232 (lambda: dopatch(text, bins), 'patch'),
1232 (lambda: dopatch(text, bins), 'patch'),
1233 (lambda: dohash(text), 'hash'),
1233 (lambda: dohash(text), 'hash'),
1234 ]
1234 ]
1235
1235
1236 for fn, title in benches:
1236 for fn, title in benches:
1237 timer, fm = gettimer(ui, opts)
1237 timer, fm = gettimer(ui, opts)
1238 timer(fn, title=title)
1238 timer(fn, title=title)
1239 fm.end()
1239 fm.end()
1240
1240
1241 @command('perfrevset',
1241 @command('perfrevset',
1242 [('C', 'clear', False, 'clear volatile cache between each call.'),
1242 [('C', 'clear', False, 'clear volatile cache between each call.'),
1243 ('', 'contexts', False, 'obtain changectx for each revision')]
1243 ('', 'contexts', False, 'obtain changectx for each revision')]
1244 + formatteropts, "REVSET")
1244 + formatteropts, "REVSET")
1245 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1245 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1246 """benchmark the execution time of a revset
1246 """benchmark the execution time of a revset
1247
1247
1248 Use the --clean option if need to evaluate the impact of build volatile
1248 Use the --clean option if need to evaluate the impact of build volatile
1249 revisions set cache on the revset execution. Volatile cache hold filtered
1249 revisions set cache on the revset execution. Volatile cache hold filtered
1250 and obsolete related cache."""
1250 and obsolete related cache."""
1251 timer, fm = gettimer(ui, opts)
1251 timer, fm = gettimer(ui, opts)
1252 def d():
1252 def d():
1253 if clear:
1253 if clear:
1254 repo.invalidatevolatilesets()
1254 repo.invalidatevolatilesets()
1255 if contexts:
1255 if contexts:
1256 for ctx in repo.set(expr): pass
1256 for ctx in repo.set(expr): pass
1257 else:
1257 else:
1258 for r in repo.revs(expr): pass
1258 for r in repo.revs(expr): pass
1259 timer(d)
1259 timer(d)
1260 fm.end()
1260 fm.end()
1261
1261
1262 def _clearobsstore(repo):
1262 def _clearobsstore(repo):
1263 unfi = repo.unfiltered()
1263 unfi = repo.unfiltered()
1264 if 'obsstore' in vars(unfi):
1264 if 'obsstore' in vars(unfi):
1265 del unfi.obsstore
1265 del unfi.obsstore
1266 del unfi._filecache['obsstore']
1266 del unfi._filecache['obsstore']
1267
1267
1268 @command('perfvolatilesets',
1268 @command('perfvolatilesets',
1269 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1269 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1270 ] + formatteropts)
1270 ] + formatteropts)
1271 def perfvolatilesets(ui, repo, *names, **opts):
1271 def perfvolatilesets(ui, repo, *names, **opts):
1272 """benchmark the computation of various volatile set
1272 """benchmark the computation of various volatile set
1273
1273
1274 Volatile set computes element related to filtering and obsolescence."""
1274 Volatile set computes element related to filtering and obsolescence."""
1275 timer, fm = gettimer(ui, opts)
1275 timer, fm = gettimer(ui, opts)
1276 repo = repo.unfiltered()
1276 repo = repo.unfiltered()
1277
1277
1278 def getobs(name):
1278 def getobs(name):
1279 def d():
1279 def d():
1280 repo.invalidatevolatilesets()
1280 repo.invalidatevolatilesets()
1281 if opts['clear_obsstore']:
1281 if opts['clear_obsstore']:
1282 _clearobsstore(repo)
1282 _clearobsstore(repo)
1283 obsolete.getrevs(repo, name)
1283 obsolete.getrevs(repo, name)
1284 return d
1284 return d
1285
1285
1286 allobs = sorted(obsolete.cachefuncs)
1286 allobs = sorted(obsolete.cachefuncs)
1287 if names:
1287 if names:
1288 allobs = [n for n in allobs if n in names]
1288 allobs = [n for n in allobs if n in names]
1289
1289
1290 for name in allobs:
1290 for name in allobs:
1291 timer(getobs(name), title=name)
1291 timer(getobs(name), title=name)
1292
1292
1293 def getfiltered(name):
1293 def getfiltered(name):
1294 def d():
1294 def d():
1295 repo.invalidatevolatilesets()
1295 repo.invalidatevolatilesets()
1296 if opts['clear_obsstore']:
1296 if opts['clear_obsstore']:
1297 _clearobsstore(repo)
1297 _clearobsstore(repo)
1298 repoview.filterrevs(repo, name)
1298 repoview.filterrevs(repo, name)
1299 return d
1299 return d
1300
1300
1301 allfilter = sorted(repoview.filtertable)
1301 allfilter = sorted(repoview.filtertable)
1302 if names:
1302 if names:
1303 allfilter = [n for n in allfilter if n in names]
1303 allfilter = [n for n in allfilter if n in names]
1304
1304
1305 for name in allfilter:
1305 for name in allfilter:
1306 timer(getfiltered(name), title=name)
1306 timer(getfiltered(name), title=name)
1307 fm.end()
1307 fm.end()
1308
1308
1309 @command('perfbranchmap',
1309 @command('perfbranchmap',
1310 [('f', 'full', False,
1310 [('f', 'full', False,
1311 'Includes build time of subset'),
1311 'Includes build time of subset'),
1312 ('', 'clear-revbranch', False,
1313 'purge the revbranch cache between computation'),
1312 ] + formatteropts)
1314 ] + formatteropts)
1313 def perfbranchmap(ui, repo, full=False, **opts):
1315 def perfbranchmap(ui, repo, full=False, clear_revbranch=False, **opts):
1314 """benchmark the update of a branchmap
1316 """benchmark the update of a branchmap
1315
1317
1316 This benchmarks the full repo.branchmap() call with read and write disabled
1318 This benchmarks the full repo.branchmap() call with read and write disabled
1317 """
1319 """
1318 timer, fm = gettimer(ui, opts)
1320 timer, fm = gettimer(ui, opts)
1319 def getbranchmap(filtername):
1321 def getbranchmap(filtername):
1320 """generate a benchmark function for the filtername"""
1322 """generate a benchmark function for the filtername"""
1321 if filtername is None:
1323 if filtername is None:
1322 view = repo
1324 view = repo
1323 else:
1325 else:
1324 view = repo.filtered(filtername)
1326 view = repo.filtered(filtername)
1325 def d():
1327 def d():
1328 if clear_revbranch:
1329 repo.revbranchcache()._clear()
1326 if full:
1330 if full:
1327 view._branchcaches.clear()
1331 view._branchcaches.clear()
1328 else:
1332 else:
1329 view._branchcaches.pop(filtername, None)
1333 view._branchcaches.pop(filtername, None)
1330 view.branchmap()
1334 view.branchmap()
1331 return d
1335 return d
1332 # add filter in smaller subset to bigger subset
1336 # add filter in smaller subset to bigger subset
1333 possiblefilters = set(repoview.filtertable)
1337 possiblefilters = set(repoview.filtertable)
1334 subsettable = getbranchmapsubsettable()
1338 subsettable = getbranchmapsubsettable()
1335 allfilters = []
1339 allfilters = []
1336 while possiblefilters:
1340 while possiblefilters:
1337 for name in possiblefilters:
1341 for name in possiblefilters:
1338 subset = subsettable.get(name)
1342 subset = subsettable.get(name)
1339 if subset not in possiblefilters:
1343 if subset not in possiblefilters:
1340 break
1344 break
1341 else:
1345 else:
1342 assert False, 'subset cycle %s!' % possiblefilters
1346 assert False, 'subset cycle %s!' % possiblefilters
1343 allfilters.append(name)
1347 allfilters.append(name)
1344 possiblefilters.remove(name)
1348 possiblefilters.remove(name)
1345
1349
1346 # warm the cache
1350 # warm the cache
1347 if not full:
1351 if not full:
1348 for name in allfilters:
1352 for name in allfilters:
1349 repo.filtered(name).branchmap()
1353 repo.filtered(name).branchmap()
1350 # add unfiltered
1354 # add unfiltered
1351 allfilters.append(None)
1355 allfilters.append(None)
1352
1356
1353 branchcacheread = safeattrsetter(branchmap, 'read')
1357 branchcacheread = safeattrsetter(branchmap, 'read')
1354 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1358 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1355 branchcacheread.set(lambda repo: None)
1359 branchcacheread.set(lambda repo: None)
1356 branchcachewrite.set(lambda bc, repo: None)
1360 branchcachewrite.set(lambda bc, repo: None)
1357 try:
1361 try:
1358 for name in allfilters:
1362 for name in allfilters:
1359 timer(getbranchmap(name), title=str(name))
1363 timer(getbranchmap(name), title=str(name))
1360 finally:
1364 finally:
1361 branchcacheread.restore()
1365 branchcacheread.restore()
1362 branchcachewrite.restore()
1366 branchcachewrite.restore()
1363 fm.end()
1367 fm.end()
1364
1368
1365 @command('perfloadmarkers')
1369 @command('perfloadmarkers')
1366 def perfloadmarkers(ui, repo):
1370 def perfloadmarkers(ui, repo):
1367 """benchmark the time to parse the on-disk markers for a repo
1371 """benchmark the time to parse the on-disk markers for a repo
1368
1372
1369 Result is the number of markers in the repo."""
1373 Result is the number of markers in the repo."""
1370 timer, fm = gettimer(ui)
1374 timer, fm = gettimer(ui)
1371 svfs = getsvfs(repo)
1375 svfs = getsvfs(repo)
1372 timer(lambda: len(obsolete.obsstore(svfs)))
1376 timer(lambda: len(obsolete.obsstore(svfs)))
1373 fm.end()
1377 fm.end()
1374
1378
1375 @command('perflrucachedict', formatteropts +
1379 @command('perflrucachedict', formatteropts +
1376 [('', 'size', 4, 'size of cache'),
1380 [('', 'size', 4, 'size of cache'),
1377 ('', 'gets', 10000, 'number of key lookups'),
1381 ('', 'gets', 10000, 'number of key lookups'),
1378 ('', 'sets', 10000, 'number of key sets'),
1382 ('', 'sets', 10000, 'number of key sets'),
1379 ('', 'mixed', 10000, 'number of mixed mode operations'),
1383 ('', 'mixed', 10000, 'number of mixed mode operations'),
1380 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1384 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1381 norepo=True)
1385 norepo=True)
1382 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1386 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1383 mixedgetfreq=50, **opts):
1387 mixedgetfreq=50, **opts):
1384 def doinit():
1388 def doinit():
1385 for i in xrange(10000):
1389 for i in xrange(10000):
1386 util.lrucachedict(size)
1390 util.lrucachedict(size)
1387
1391
1388 values = []
1392 values = []
1389 for i in xrange(size):
1393 for i in xrange(size):
1390 values.append(random.randint(0, sys.maxint))
1394 values.append(random.randint(0, sys.maxint))
1391
1395
1392 # Get mode fills the cache and tests raw lookup performance with no
1396 # Get mode fills the cache and tests raw lookup performance with no
1393 # eviction.
1397 # eviction.
1394 getseq = []
1398 getseq = []
1395 for i in xrange(gets):
1399 for i in xrange(gets):
1396 getseq.append(random.choice(values))
1400 getseq.append(random.choice(values))
1397
1401
1398 def dogets():
1402 def dogets():
1399 d = util.lrucachedict(size)
1403 d = util.lrucachedict(size)
1400 for v in values:
1404 for v in values:
1401 d[v] = v
1405 d[v] = v
1402 for key in getseq:
1406 for key in getseq:
1403 value = d[key]
1407 value = d[key]
1404 value # silence pyflakes warning
1408 value # silence pyflakes warning
1405
1409
1406 # Set mode tests insertion speed with cache eviction.
1410 # Set mode tests insertion speed with cache eviction.
1407 setseq = []
1411 setseq = []
1408 for i in xrange(sets):
1412 for i in xrange(sets):
1409 setseq.append(random.randint(0, sys.maxint))
1413 setseq.append(random.randint(0, sys.maxint))
1410
1414
1411 def dosets():
1415 def dosets():
1412 d = util.lrucachedict(size)
1416 d = util.lrucachedict(size)
1413 for v in setseq:
1417 for v in setseq:
1414 d[v] = v
1418 d[v] = v
1415
1419
1416 # Mixed mode randomly performs gets and sets with eviction.
1420 # Mixed mode randomly performs gets and sets with eviction.
1417 mixedops = []
1421 mixedops = []
1418 for i in xrange(mixed):
1422 for i in xrange(mixed):
1419 r = random.randint(0, 100)
1423 r = random.randint(0, 100)
1420 if r < mixedgetfreq:
1424 if r < mixedgetfreq:
1421 op = 0
1425 op = 0
1422 else:
1426 else:
1423 op = 1
1427 op = 1
1424
1428
1425 mixedops.append((op, random.randint(0, size * 2)))
1429 mixedops.append((op, random.randint(0, size * 2)))
1426
1430
1427 def domixed():
1431 def domixed():
1428 d = util.lrucachedict(size)
1432 d = util.lrucachedict(size)
1429
1433
1430 for op, v in mixedops:
1434 for op, v in mixedops:
1431 if op == 0:
1435 if op == 0:
1432 try:
1436 try:
1433 d[v]
1437 d[v]
1434 except KeyError:
1438 except KeyError:
1435 pass
1439 pass
1436 else:
1440 else:
1437 d[v] = v
1441 d[v] = v
1438
1442
1439 benches = [
1443 benches = [
1440 (doinit, 'init'),
1444 (doinit, 'init'),
1441 (dogets, 'gets'),
1445 (dogets, 'gets'),
1442 (dosets, 'sets'),
1446 (dosets, 'sets'),
1443 (domixed, 'mixed')
1447 (domixed, 'mixed')
1444 ]
1448 ]
1445
1449
1446 for fn, title in benches:
1450 for fn, title in benches:
1447 timer, fm = gettimer(ui, opts)
1451 timer, fm = gettimer(ui, opts)
1448 timer(fn, title=title)
1452 timer(fn, title=title)
1449 fm.end()
1453 fm.end()
1450
1454
1451 @command('perfwrite', formatteropts)
1455 @command('perfwrite', formatteropts)
1452 def perfwrite(ui, repo, **opts):
1456 def perfwrite(ui, repo, **opts):
1453 """microbenchmark ui.write
1457 """microbenchmark ui.write
1454 """
1458 """
1455 timer, fm = gettimer(ui, opts)
1459 timer, fm = gettimer(ui, opts)
1456 def write():
1460 def write():
1457 for i in range(100000):
1461 for i in range(100000):
1458 ui.write(('Testing write performance\n'))
1462 ui.write(('Testing write performance\n'))
1459 timer(write)
1463 timer(write)
1460 fm.end()
1464 fm.end()
1461
1465
1462 def uisetup(ui):
1466 def uisetup(ui):
1463 if (util.safehasattr(cmdutil, 'openrevlog') and
1467 if (util.safehasattr(cmdutil, 'openrevlog') and
1464 not util.safehasattr(commands, 'debugrevlogopts')):
1468 not util.safehasattr(commands, 'debugrevlogopts')):
1465 # for "historical portability":
1469 # for "historical portability":
1466 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1470 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1467 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1471 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1468 # openrevlog() should cause failure, because it has been
1472 # openrevlog() should cause failure, because it has been
1469 # available since 3.5 (or 49c583ca48c4).
1473 # available since 3.5 (or 49c583ca48c4).
1470 def openrevlog(orig, repo, cmd, file_, opts):
1474 def openrevlog(orig, repo, cmd, file_, opts):
1471 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1475 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1472 raise error.Abort("This version doesn't support --dir option",
1476 raise error.Abort("This version doesn't support --dir option",
1473 hint="use 3.5 or later")
1477 hint="use 3.5 or later")
1474 return orig(repo, cmd, file_, opts)
1478 return orig(repo, cmd, file_, opts)
1475 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1479 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now