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