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