##// END OF EJS Templates
perf: add --reverse to perfrevlog...
Gregory Szorc -
r30017:973cf6c3 default
parent child Browse files
Show More
@@ -1,902 +1,912 b''
1 # perf.py - performance test routines
1 # perf.py - performance test routines
2 '''helper extension to measure performance'''
2 '''helper extension to measure performance'''
3
3
4 # "historical portability" policy of perf.py:
4 # "historical portability" policy of perf.py:
5 #
5 #
6 # We have to do:
6 # We have to do:
7 # - make perf.py "loadable" with as wide Mercurial version as possible
7 # - make perf.py "loadable" with as wide Mercurial version as possible
8 # This doesn't mean that perf commands work correctly with that Mercurial.
8 # This doesn't mean that perf commands work correctly with that Mercurial.
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
10 # - make historical perf command work correctly with as wide Mercurial
10 # - make historical perf command work correctly with as wide Mercurial
11 # version as possible
11 # version as possible
12 #
12 #
13 # We have to do, if possible with reasonable cost:
13 # We have to do, if possible with reasonable cost:
14 # - make recent perf command for historical feature work correctly
14 # - make recent perf command for historical feature work correctly
15 # with early Mercurial
15 # with early Mercurial
16 #
16 #
17 # We don't have to do:
17 # We don't have to do:
18 # - make perf command for recent feature work correctly with early
18 # - make perf command for recent feature work correctly with early
19 # Mercurial
19 # Mercurial
20
20
21 from __future__ import absolute_import
21 from __future__ import absolute_import
22 import functools
22 import functools
23 import os
23 import os
24 import random
24 import random
25 import sys
25 import sys
26 import time
26 import time
27 from mercurial import (
27 from mercurial import (
28 cmdutil,
28 cmdutil,
29 commands,
29 commands,
30 copies,
30 copies,
31 error,
31 error,
32 extensions,
32 extensions,
33 mdiff,
33 mdiff,
34 merge,
34 merge,
35 revlog,
35 revlog,
36 util,
36 util,
37 )
37 )
38
38
39 # for "historical portability":
39 # for "historical portability":
40 # try to import modules separately (in dict order), and ignore
40 # try to import modules separately (in dict order), and ignore
41 # failure, because these aren't available with early Mercurial
41 # failure, because these aren't available with early Mercurial
42 try:
42 try:
43 from mercurial import branchmap # since 2.5 (or bcee63733aad)
43 from mercurial import branchmap # since 2.5 (or bcee63733aad)
44 except ImportError:
44 except ImportError:
45 pass
45 pass
46 try:
46 try:
47 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
47 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
48 except ImportError:
48 except ImportError:
49 pass
49 pass
50 try:
50 try:
51 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
51 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
52 except ImportError:
52 except ImportError:
53 pass
53 pass
54 try:
54 try:
55 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
55 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
56 except ImportError:
56 except ImportError:
57 pass
57 pass
58
58
59 # for "historical portability":
59 # for "historical portability":
60 # define util.safehasattr forcibly, because util.safehasattr has been
60 # define util.safehasattr forcibly, because util.safehasattr has been
61 # available since 1.9.3 (or 94b200a11cf7)
61 # available since 1.9.3 (or 94b200a11cf7)
62 _undefined = object()
62 _undefined = object()
63 def safehasattr(thing, attr):
63 def safehasattr(thing, attr):
64 return getattr(thing, attr, _undefined) is not _undefined
64 return getattr(thing, attr, _undefined) is not _undefined
65 setattr(util, 'safehasattr', safehasattr)
65 setattr(util, 'safehasattr', safehasattr)
66
66
67 # for "historical portability":
67 # for "historical portability":
68 # use locally defined empty option list, if formatteropts isn't
68 # use locally defined empty option list, if formatteropts isn't
69 # available, because commands.formatteropts has been available since
69 # available, because commands.formatteropts has been available since
70 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
70 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
71 # available since 2.2 (or ae5f92e154d3)
71 # available since 2.2 (or ae5f92e154d3)
72 formatteropts = getattr(commands, "formatteropts", [])
72 formatteropts = getattr(commands, "formatteropts", [])
73
73
74 # for "historical portability":
74 # for "historical portability":
75 # use locally defined option list, if debugrevlogopts isn't available,
75 # use locally defined option list, if debugrevlogopts isn't available,
76 # because commands.debugrevlogopts has been available since 3.7 (or
76 # because commands.debugrevlogopts has been available since 3.7 (or
77 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
77 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
78 # since 1.9 (or a79fea6b3e77).
78 # since 1.9 (or a79fea6b3e77).
79 revlogopts = getattr(commands, "debugrevlogopts", [
79 revlogopts = getattr(commands, "debugrevlogopts", [
80 ('c', 'changelog', False, ('open changelog')),
80 ('c', 'changelog', False, ('open changelog')),
81 ('m', 'manifest', False, ('open manifest')),
81 ('m', 'manifest', False, ('open manifest')),
82 ('', 'dir', False, ('open directory manifest')),
82 ('', 'dir', False, ('open directory manifest')),
83 ])
83 ])
84
84
85 cmdtable = {}
85 cmdtable = {}
86
86
87 # for "historical portability":
87 # for "historical portability":
88 # define parsealiases locally, because cmdutil.parsealiases has been
88 # define parsealiases locally, because cmdutil.parsealiases has been
89 # available since 1.5 (or 6252852b4332)
89 # available since 1.5 (or 6252852b4332)
90 def parsealiases(cmd):
90 def parsealiases(cmd):
91 return cmd.lstrip("^").split("|")
91 return cmd.lstrip("^").split("|")
92
92
93 if safehasattr(cmdutil, 'command'):
93 if safehasattr(cmdutil, 'command'):
94 import inspect
94 import inspect
95 command = cmdutil.command(cmdtable)
95 command = cmdutil.command(cmdtable)
96 if 'norepo' not in inspect.getargspec(command)[0]:
96 if 'norepo' not in inspect.getargspec(command)[0]:
97 # for "historical portability":
97 # for "historical portability":
98 # wrap original cmdutil.command, because "norepo" option has
98 # wrap original cmdutil.command, because "norepo" option has
99 # been available since 3.1 (or 75a96326cecb)
99 # been available since 3.1 (or 75a96326cecb)
100 _command = command
100 _command = command
101 def command(name, options=(), synopsis=None, norepo=False):
101 def command(name, options=(), synopsis=None, norepo=False):
102 if norepo:
102 if norepo:
103 commands.norepo += ' %s' % ' '.join(parsealiases(name))
103 commands.norepo += ' %s' % ' '.join(parsealiases(name))
104 return _command(name, list(options), synopsis)
104 return _command(name, list(options), synopsis)
105 else:
105 else:
106 # for "historical portability":
106 # for "historical portability":
107 # define "@command" annotation locally, because cmdutil.command
107 # define "@command" annotation locally, because cmdutil.command
108 # has been available since 1.9 (or 2daa5179e73f)
108 # has been available since 1.9 (or 2daa5179e73f)
109 def command(name, options=(), synopsis=None, norepo=False):
109 def command(name, options=(), synopsis=None, norepo=False):
110 def decorator(func):
110 def decorator(func):
111 if synopsis:
111 if synopsis:
112 cmdtable[name] = func, list(options), synopsis
112 cmdtable[name] = func, list(options), synopsis
113 else:
113 else:
114 cmdtable[name] = func, list(options)
114 cmdtable[name] = func, list(options)
115 if norepo:
115 if norepo:
116 commands.norepo += ' %s' % ' '.join(parsealiases(name))
116 commands.norepo += ' %s' % ' '.join(parsealiases(name))
117 return func
117 return func
118 return decorator
118 return decorator
119
119
120 def getlen(ui):
120 def getlen(ui):
121 if ui.configbool("perf", "stub"):
121 if ui.configbool("perf", "stub"):
122 return lambda x: 1
122 return lambda x: 1
123 return len
123 return len
124
124
125 def gettimer(ui, opts=None):
125 def gettimer(ui, opts=None):
126 """return a timer function and formatter: (timer, formatter)
126 """return a timer function and formatter: (timer, formatter)
127
127
128 This function exists to gather the creation of formatter in a single
128 This function exists to gather the creation of formatter in a single
129 place instead of duplicating it in all performance commands."""
129 place instead of duplicating it in all performance commands."""
130
130
131 # enforce an idle period before execution to counteract power management
131 # enforce an idle period before execution to counteract power management
132 # experimental config: perf.presleep
132 # experimental config: perf.presleep
133 time.sleep(ui.configint("perf", "presleep", 1))
133 time.sleep(ui.configint("perf", "presleep", 1))
134
134
135 if opts is None:
135 if opts is None:
136 opts = {}
136 opts = {}
137 # redirect all to stderr
137 # redirect all to stderr
138 ui = ui.copy()
138 ui = ui.copy()
139 ui.fout = ui.ferr
139 ui.fout = ui.ferr
140 # get a formatter
140 # get a formatter
141 fm = ui.formatter('perf', opts)
141 fm = ui.formatter('perf', opts)
142 # stub function, runs code only once instead of in a loop
142 # stub function, runs code only once instead of in a loop
143 # experimental config: perf.stub
143 # experimental config: perf.stub
144 if ui.configbool("perf", "stub"):
144 if ui.configbool("perf", "stub"):
145 return functools.partial(stub_timer, fm), fm
145 return functools.partial(stub_timer, fm), fm
146 return functools.partial(_timer, fm), fm
146 return functools.partial(_timer, fm), fm
147
147
148 def stub_timer(fm, func, title=None):
148 def stub_timer(fm, func, title=None):
149 func()
149 func()
150
150
151 def _timer(fm, func, title=None):
151 def _timer(fm, func, title=None):
152 results = []
152 results = []
153 begin = time.time()
153 begin = time.time()
154 count = 0
154 count = 0
155 while True:
155 while True:
156 ostart = os.times()
156 ostart = os.times()
157 cstart = time.time()
157 cstart = time.time()
158 r = func()
158 r = func()
159 cstop = time.time()
159 cstop = time.time()
160 ostop = os.times()
160 ostop = os.times()
161 count += 1
161 count += 1
162 a, b = ostart, ostop
162 a, b = ostart, ostop
163 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
163 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
164 if cstop - begin > 3 and count >= 100:
164 if cstop - begin > 3 and count >= 100:
165 break
165 break
166 if cstop - begin > 10 and count >= 3:
166 if cstop - begin > 10 and count >= 3:
167 break
167 break
168
168
169 fm.startitem()
169 fm.startitem()
170
170
171 if title:
171 if title:
172 fm.write('title', '! %s\n', title)
172 fm.write('title', '! %s\n', title)
173 if r:
173 if r:
174 fm.write('result', '! result: %s\n', r)
174 fm.write('result', '! result: %s\n', r)
175 m = min(results)
175 m = min(results)
176 fm.plain('!')
176 fm.plain('!')
177 fm.write('wall', ' wall %f', m[0])
177 fm.write('wall', ' wall %f', m[0])
178 fm.write('comb', ' comb %f', m[1] + m[2])
178 fm.write('comb', ' comb %f', m[1] + m[2])
179 fm.write('user', ' user %f', m[1])
179 fm.write('user', ' user %f', m[1])
180 fm.write('sys', ' sys %f', m[2])
180 fm.write('sys', ' sys %f', m[2])
181 fm.write('count', ' (best of %d)', count)
181 fm.write('count', ' (best of %d)', count)
182 fm.plain('\n')
182 fm.plain('\n')
183
183
184 @command('perfwalk', formatteropts)
184 @command('perfwalk', formatteropts)
185 def perfwalk(ui, repo, *pats, **opts):
185 def perfwalk(ui, repo, *pats, **opts):
186 timer, fm = gettimer(ui, opts)
186 timer, fm = gettimer(ui, opts)
187 try:
187 try:
188 m = scmutil.match(repo[None], pats, {})
188 m = scmutil.match(repo[None], pats, {})
189 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
189 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
190 except Exception:
190 except Exception:
191 try:
191 try:
192 m = scmutil.match(repo[None], pats, {})
192 m = scmutil.match(repo[None], pats, {})
193 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
193 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
194 except Exception:
194 except Exception:
195 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
195 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
196 fm.end()
196 fm.end()
197
197
198 @command('perfannotate', formatteropts)
198 @command('perfannotate', formatteropts)
199 def perfannotate(ui, repo, f, **opts):
199 def perfannotate(ui, repo, f, **opts):
200 timer, fm = gettimer(ui, opts)
200 timer, fm = gettimer(ui, opts)
201 fc = repo['.'][f]
201 fc = repo['.'][f]
202 timer(lambda: len(fc.annotate(True)))
202 timer(lambda: len(fc.annotate(True)))
203 fm.end()
203 fm.end()
204
204
205 @command('perfstatus',
205 @command('perfstatus',
206 [('u', 'unknown', False,
206 [('u', 'unknown', False,
207 'ask status to look for unknown files')] + formatteropts)
207 'ask status to look for unknown files')] + formatteropts)
208 def perfstatus(ui, repo, **opts):
208 def perfstatus(ui, repo, **opts):
209 #m = match.always(repo.root, repo.getcwd())
209 #m = match.always(repo.root, repo.getcwd())
210 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
210 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
211 # False))))
211 # False))))
212 timer, fm = gettimer(ui, opts)
212 timer, fm = gettimer(ui, opts)
213 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
213 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
214 fm.end()
214 fm.end()
215
215
216 @command('perfaddremove', formatteropts)
216 @command('perfaddremove', formatteropts)
217 def perfaddremove(ui, repo, **opts):
217 def perfaddremove(ui, repo, **opts):
218 timer, fm = gettimer(ui, opts)
218 timer, fm = gettimer(ui, opts)
219 try:
219 try:
220 oldquiet = repo.ui.quiet
220 oldquiet = repo.ui.quiet
221 repo.ui.quiet = True
221 repo.ui.quiet = True
222 matcher = scmutil.match(repo[None])
222 matcher = scmutil.match(repo[None])
223 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
223 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
224 finally:
224 finally:
225 repo.ui.quiet = oldquiet
225 repo.ui.quiet = oldquiet
226 fm.end()
226 fm.end()
227
227
228 def clearcaches(cl):
228 def clearcaches(cl):
229 # behave somewhat consistently across internal API changes
229 # behave somewhat consistently across internal API changes
230 if util.safehasattr(cl, 'clearcaches'):
230 if util.safehasattr(cl, 'clearcaches'):
231 cl.clearcaches()
231 cl.clearcaches()
232 elif util.safehasattr(cl, '_nodecache'):
232 elif util.safehasattr(cl, '_nodecache'):
233 from mercurial.node import nullid, nullrev
233 from mercurial.node import nullid, nullrev
234 cl._nodecache = {nullid: nullrev}
234 cl._nodecache = {nullid: nullrev}
235 cl._nodepos = None
235 cl._nodepos = None
236
236
237 @command('perfheads', formatteropts)
237 @command('perfheads', formatteropts)
238 def perfheads(ui, repo, **opts):
238 def perfheads(ui, repo, **opts):
239 timer, fm = gettimer(ui, opts)
239 timer, fm = gettimer(ui, opts)
240 cl = repo.changelog
240 cl = repo.changelog
241 def d():
241 def d():
242 len(cl.headrevs())
242 len(cl.headrevs())
243 clearcaches(cl)
243 clearcaches(cl)
244 timer(d)
244 timer(d)
245 fm.end()
245 fm.end()
246
246
247 @command('perftags', formatteropts)
247 @command('perftags', formatteropts)
248 def perftags(ui, repo, **opts):
248 def perftags(ui, repo, **opts):
249 import mercurial.changelog
249 import mercurial.changelog
250 import mercurial.manifest
250 import mercurial.manifest
251 timer, fm = gettimer(ui, opts)
251 timer, fm = gettimer(ui, opts)
252 def t():
252 def t():
253 repo.changelog = mercurial.changelog.changelog(repo.svfs)
253 repo.changelog = mercurial.changelog.changelog(repo.svfs)
254 repo.manifest = mercurial.manifest.manifest(repo.svfs)
254 repo.manifest = mercurial.manifest.manifest(repo.svfs)
255 repo._tags = None
255 repo._tags = None
256 return len(repo.tags())
256 return len(repo.tags())
257 timer(t)
257 timer(t)
258 fm.end()
258 fm.end()
259
259
260 @command('perfancestors', formatteropts)
260 @command('perfancestors', formatteropts)
261 def perfancestors(ui, repo, **opts):
261 def perfancestors(ui, repo, **opts):
262 timer, fm = gettimer(ui, opts)
262 timer, fm = gettimer(ui, opts)
263 heads = repo.changelog.headrevs()
263 heads = repo.changelog.headrevs()
264 def d():
264 def d():
265 for a in repo.changelog.ancestors(heads):
265 for a in repo.changelog.ancestors(heads):
266 pass
266 pass
267 timer(d)
267 timer(d)
268 fm.end()
268 fm.end()
269
269
270 @command('perfancestorset', formatteropts)
270 @command('perfancestorset', formatteropts)
271 def perfancestorset(ui, repo, revset, **opts):
271 def perfancestorset(ui, repo, revset, **opts):
272 timer, fm = gettimer(ui, opts)
272 timer, fm = gettimer(ui, opts)
273 revs = repo.revs(revset)
273 revs = repo.revs(revset)
274 heads = repo.changelog.headrevs()
274 heads = repo.changelog.headrevs()
275 def d():
275 def d():
276 s = repo.changelog.ancestors(heads)
276 s = repo.changelog.ancestors(heads)
277 for rev in revs:
277 for rev in revs:
278 rev in s
278 rev in s
279 timer(d)
279 timer(d)
280 fm.end()
280 fm.end()
281
281
282 @command('perfdirs', formatteropts)
282 @command('perfdirs', formatteropts)
283 def perfdirs(ui, repo, **opts):
283 def perfdirs(ui, repo, **opts):
284 timer, fm = gettimer(ui, opts)
284 timer, fm = gettimer(ui, opts)
285 dirstate = repo.dirstate
285 dirstate = repo.dirstate
286 'a' in dirstate
286 'a' in dirstate
287 def d():
287 def d():
288 dirstate.dirs()
288 dirstate.dirs()
289 del dirstate._dirs
289 del dirstate._dirs
290 timer(d)
290 timer(d)
291 fm.end()
291 fm.end()
292
292
293 @command('perfdirstate', formatteropts)
293 @command('perfdirstate', formatteropts)
294 def perfdirstate(ui, repo, **opts):
294 def perfdirstate(ui, repo, **opts):
295 timer, fm = gettimer(ui, opts)
295 timer, fm = gettimer(ui, opts)
296 "a" in repo.dirstate
296 "a" in repo.dirstate
297 def d():
297 def d():
298 repo.dirstate.invalidate()
298 repo.dirstate.invalidate()
299 "a" in repo.dirstate
299 "a" in repo.dirstate
300 timer(d)
300 timer(d)
301 fm.end()
301 fm.end()
302
302
303 @command('perfdirstatedirs', formatteropts)
303 @command('perfdirstatedirs', formatteropts)
304 def perfdirstatedirs(ui, repo, **opts):
304 def perfdirstatedirs(ui, repo, **opts):
305 timer, fm = gettimer(ui, opts)
305 timer, fm = gettimer(ui, opts)
306 "a" in repo.dirstate
306 "a" in repo.dirstate
307 def d():
307 def d():
308 "a" in repo.dirstate._dirs
308 "a" in repo.dirstate._dirs
309 del repo.dirstate._dirs
309 del repo.dirstate._dirs
310 timer(d)
310 timer(d)
311 fm.end()
311 fm.end()
312
312
313 @command('perfdirstatefoldmap', formatteropts)
313 @command('perfdirstatefoldmap', formatteropts)
314 def perfdirstatefoldmap(ui, repo, **opts):
314 def perfdirstatefoldmap(ui, repo, **opts):
315 timer, fm = gettimer(ui, opts)
315 timer, fm = gettimer(ui, opts)
316 dirstate = repo.dirstate
316 dirstate = repo.dirstate
317 'a' in dirstate
317 'a' in dirstate
318 def d():
318 def d():
319 dirstate._filefoldmap.get('a')
319 dirstate._filefoldmap.get('a')
320 del dirstate._filefoldmap
320 del dirstate._filefoldmap
321 timer(d)
321 timer(d)
322 fm.end()
322 fm.end()
323
323
324 @command('perfdirfoldmap', formatteropts)
324 @command('perfdirfoldmap', formatteropts)
325 def perfdirfoldmap(ui, repo, **opts):
325 def perfdirfoldmap(ui, repo, **opts):
326 timer, fm = gettimer(ui, opts)
326 timer, fm = gettimer(ui, opts)
327 dirstate = repo.dirstate
327 dirstate = repo.dirstate
328 'a' in dirstate
328 'a' in dirstate
329 def d():
329 def d():
330 dirstate._dirfoldmap.get('a')
330 dirstate._dirfoldmap.get('a')
331 del dirstate._dirfoldmap
331 del dirstate._dirfoldmap
332 del dirstate._dirs
332 del dirstate._dirs
333 timer(d)
333 timer(d)
334 fm.end()
334 fm.end()
335
335
336 @command('perfdirstatewrite', formatteropts)
336 @command('perfdirstatewrite', formatteropts)
337 def perfdirstatewrite(ui, repo, **opts):
337 def perfdirstatewrite(ui, repo, **opts):
338 timer, fm = gettimer(ui, opts)
338 timer, fm = gettimer(ui, opts)
339 ds = repo.dirstate
339 ds = repo.dirstate
340 "a" in ds
340 "a" in ds
341 def d():
341 def d():
342 ds._dirty = True
342 ds._dirty = True
343 ds.write(repo.currenttransaction())
343 ds.write(repo.currenttransaction())
344 timer(d)
344 timer(d)
345 fm.end()
345 fm.end()
346
346
347 @command('perfmergecalculate',
347 @command('perfmergecalculate',
348 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
348 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
349 def perfmergecalculate(ui, repo, rev, **opts):
349 def perfmergecalculate(ui, repo, rev, **opts):
350 timer, fm = gettimer(ui, opts)
350 timer, fm = gettimer(ui, opts)
351 wctx = repo[None]
351 wctx = repo[None]
352 rctx = scmutil.revsingle(repo, rev, rev)
352 rctx = scmutil.revsingle(repo, rev, rev)
353 ancestor = wctx.ancestor(rctx)
353 ancestor = wctx.ancestor(rctx)
354 # we don't want working dir files to be stat'd in the benchmark, so prime
354 # we don't want working dir files to be stat'd in the benchmark, so prime
355 # that cache
355 # that cache
356 wctx.dirty()
356 wctx.dirty()
357 def d():
357 def d():
358 # acceptremote is True because we don't want prompts in the middle of
358 # acceptremote is True because we don't want prompts in the middle of
359 # our benchmark
359 # our benchmark
360 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
360 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
361 acceptremote=True, followcopies=True)
361 acceptremote=True, followcopies=True)
362 timer(d)
362 timer(d)
363 fm.end()
363 fm.end()
364
364
365 @command('perfpathcopies', [], "REV REV")
365 @command('perfpathcopies', [], "REV REV")
366 def perfpathcopies(ui, repo, rev1, rev2, **opts):
366 def perfpathcopies(ui, repo, rev1, rev2, **opts):
367 timer, fm = gettimer(ui, opts)
367 timer, fm = gettimer(ui, opts)
368 ctx1 = scmutil.revsingle(repo, rev1, rev1)
368 ctx1 = scmutil.revsingle(repo, rev1, rev1)
369 ctx2 = scmutil.revsingle(repo, rev2, rev2)
369 ctx2 = scmutil.revsingle(repo, rev2, rev2)
370 def d():
370 def d():
371 copies.pathcopies(ctx1, ctx2)
371 copies.pathcopies(ctx1, ctx2)
372 timer(d)
372 timer(d)
373 fm.end()
373 fm.end()
374
374
375 @command('perfmanifest', [], 'REV')
375 @command('perfmanifest', [], 'REV')
376 def perfmanifest(ui, repo, rev, **opts):
376 def perfmanifest(ui, repo, rev, **opts):
377 timer, fm = gettimer(ui, opts)
377 timer, fm = gettimer(ui, opts)
378 ctx = scmutil.revsingle(repo, rev, rev)
378 ctx = scmutil.revsingle(repo, rev, rev)
379 t = ctx.manifestnode()
379 t = ctx.manifestnode()
380 def d():
380 def d():
381 repo.manifest.clearcaches()
381 repo.manifest.clearcaches()
382 repo.manifest.read(t)
382 repo.manifest.read(t)
383 timer(d)
383 timer(d)
384 fm.end()
384 fm.end()
385
385
386 @command('perfchangeset', formatteropts)
386 @command('perfchangeset', formatteropts)
387 def perfchangeset(ui, repo, rev, **opts):
387 def perfchangeset(ui, repo, rev, **opts):
388 timer, fm = gettimer(ui, opts)
388 timer, fm = gettimer(ui, opts)
389 n = repo[rev].node()
389 n = repo[rev].node()
390 def d():
390 def d():
391 repo.changelog.read(n)
391 repo.changelog.read(n)
392 #repo.changelog._cache = None
392 #repo.changelog._cache = None
393 timer(d)
393 timer(d)
394 fm.end()
394 fm.end()
395
395
396 @command('perfindex', formatteropts)
396 @command('perfindex', formatteropts)
397 def perfindex(ui, repo, **opts):
397 def perfindex(ui, repo, **opts):
398 import mercurial.revlog
398 import mercurial.revlog
399 timer, fm = gettimer(ui, opts)
399 timer, fm = gettimer(ui, opts)
400 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
400 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
401 n = repo["tip"].node()
401 n = repo["tip"].node()
402 def d():
402 def d():
403 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
403 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
404 cl.rev(n)
404 cl.rev(n)
405 timer(d)
405 timer(d)
406 fm.end()
406 fm.end()
407
407
408 @command('perfstartup', formatteropts)
408 @command('perfstartup', formatteropts)
409 def perfstartup(ui, repo, **opts):
409 def perfstartup(ui, repo, **opts):
410 timer, fm = gettimer(ui, opts)
410 timer, fm = gettimer(ui, opts)
411 cmd = sys.argv[0]
411 cmd = sys.argv[0]
412 def d():
412 def d():
413 if os.name != 'nt':
413 if os.name != 'nt':
414 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
414 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
415 else:
415 else:
416 os.environ['HGRCPATH'] = ''
416 os.environ['HGRCPATH'] = ''
417 os.system("%s version -q > NUL" % cmd)
417 os.system("%s version -q > NUL" % cmd)
418 timer(d)
418 timer(d)
419 fm.end()
419 fm.end()
420
420
421 @command('perfparents', formatteropts)
421 @command('perfparents', formatteropts)
422 def perfparents(ui, repo, **opts):
422 def perfparents(ui, repo, **opts):
423 timer, fm = gettimer(ui, opts)
423 timer, fm = gettimer(ui, opts)
424 # control the number of commits perfparents iterates over
424 # control the number of commits perfparents iterates over
425 # experimental config: perf.parentscount
425 # experimental config: perf.parentscount
426 count = ui.configint("perf", "parentscount", 1000)
426 count = ui.configint("perf", "parentscount", 1000)
427 if len(repo.changelog) < count:
427 if len(repo.changelog) < count:
428 raise error.Abort("repo needs %d commits for this test" % count)
428 raise error.Abort("repo needs %d commits for this test" % count)
429 repo = repo.unfiltered()
429 repo = repo.unfiltered()
430 nl = [repo.changelog.node(i) for i in xrange(count)]
430 nl = [repo.changelog.node(i) for i in xrange(count)]
431 def d():
431 def d():
432 for n in nl:
432 for n in nl:
433 repo.changelog.parents(n)
433 repo.changelog.parents(n)
434 timer(d)
434 timer(d)
435 fm.end()
435 fm.end()
436
436
437 @command('perfctxfiles', formatteropts)
437 @command('perfctxfiles', formatteropts)
438 def perfctxfiles(ui, repo, x, **opts):
438 def perfctxfiles(ui, repo, x, **opts):
439 x = int(x)
439 x = int(x)
440 timer, fm = gettimer(ui, opts)
440 timer, fm = gettimer(ui, opts)
441 def d():
441 def d():
442 len(repo[x].files())
442 len(repo[x].files())
443 timer(d)
443 timer(d)
444 fm.end()
444 fm.end()
445
445
446 @command('perfrawfiles', formatteropts)
446 @command('perfrawfiles', formatteropts)
447 def perfrawfiles(ui, repo, x, **opts):
447 def perfrawfiles(ui, repo, x, **opts):
448 x = int(x)
448 x = int(x)
449 timer, fm = gettimer(ui, opts)
449 timer, fm = gettimer(ui, opts)
450 cl = repo.changelog
450 cl = repo.changelog
451 def d():
451 def d():
452 len(cl.read(x)[3])
452 len(cl.read(x)[3])
453 timer(d)
453 timer(d)
454 fm.end()
454 fm.end()
455
455
456 @command('perflookup', formatteropts)
456 @command('perflookup', formatteropts)
457 def perflookup(ui, repo, rev, **opts):
457 def perflookup(ui, repo, rev, **opts):
458 timer, fm = gettimer(ui, opts)
458 timer, fm = gettimer(ui, opts)
459 timer(lambda: len(repo.lookup(rev)))
459 timer(lambda: len(repo.lookup(rev)))
460 fm.end()
460 fm.end()
461
461
462 @command('perfrevrange', formatteropts)
462 @command('perfrevrange', formatteropts)
463 def perfrevrange(ui, repo, *specs, **opts):
463 def perfrevrange(ui, repo, *specs, **opts):
464 timer, fm = gettimer(ui, opts)
464 timer, fm = gettimer(ui, opts)
465 revrange = scmutil.revrange
465 revrange = scmutil.revrange
466 timer(lambda: len(revrange(repo, specs)))
466 timer(lambda: len(revrange(repo, specs)))
467 fm.end()
467 fm.end()
468
468
469 @command('perfnodelookup', formatteropts)
469 @command('perfnodelookup', formatteropts)
470 def perfnodelookup(ui, repo, rev, **opts):
470 def perfnodelookup(ui, repo, rev, **opts):
471 timer, fm = gettimer(ui, opts)
471 timer, fm = gettimer(ui, opts)
472 import mercurial.revlog
472 import mercurial.revlog
473 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
473 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
474 n = repo[rev].node()
474 n = repo[rev].node()
475 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
475 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
476 def d():
476 def d():
477 cl.rev(n)
477 cl.rev(n)
478 clearcaches(cl)
478 clearcaches(cl)
479 timer(d)
479 timer(d)
480 fm.end()
480 fm.end()
481
481
482 @command('perflog',
482 @command('perflog',
483 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
483 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
484 def perflog(ui, repo, rev=None, **opts):
484 def perflog(ui, repo, rev=None, **opts):
485 if rev is None:
485 if rev is None:
486 rev=[]
486 rev=[]
487 timer, fm = gettimer(ui, opts)
487 timer, fm = gettimer(ui, opts)
488 ui.pushbuffer()
488 ui.pushbuffer()
489 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
489 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
490 copies=opts.get('rename')))
490 copies=opts.get('rename')))
491 ui.popbuffer()
491 ui.popbuffer()
492 fm.end()
492 fm.end()
493
493
494 @command('perfmoonwalk', formatteropts)
494 @command('perfmoonwalk', formatteropts)
495 def perfmoonwalk(ui, repo, **opts):
495 def perfmoonwalk(ui, repo, **opts):
496 """benchmark walking the changelog backwards
496 """benchmark walking the changelog backwards
497
497
498 This also loads the changelog data for each revision in the changelog.
498 This also loads the changelog data for each revision in the changelog.
499 """
499 """
500 timer, fm = gettimer(ui, opts)
500 timer, fm = gettimer(ui, opts)
501 def moonwalk():
501 def moonwalk():
502 for i in xrange(len(repo), -1, -1):
502 for i in xrange(len(repo), -1, -1):
503 ctx = repo[i]
503 ctx = repo[i]
504 ctx.branch() # read changelog data (in addition to the index)
504 ctx.branch() # read changelog data (in addition to the index)
505 timer(moonwalk)
505 timer(moonwalk)
506 fm.end()
506 fm.end()
507
507
508 @command('perftemplating', formatteropts)
508 @command('perftemplating', formatteropts)
509 def perftemplating(ui, repo, rev=None, **opts):
509 def perftemplating(ui, repo, rev=None, **opts):
510 if rev is None:
510 if rev is None:
511 rev=[]
511 rev=[]
512 timer, fm = gettimer(ui, opts)
512 timer, fm = gettimer(ui, opts)
513 ui.pushbuffer()
513 ui.pushbuffer()
514 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
514 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
515 template='{date|shortdate} [{rev}:{node|short}]'
515 template='{date|shortdate} [{rev}:{node|short}]'
516 ' {author|person}: {desc|firstline}\n'))
516 ' {author|person}: {desc|firstline}\n'))
517 ui.popbuffer()
517 ui.popbuffer()
518 fm.end()
518 fm.end()
519
519
520 @command('perfcca', formatteropts)
520 @command('perfcca', formatteropts)
521 def perfcca(ui, repo, **opts):
521 def perfcca(ui, repo, **opts):
522 timer, fm = gettimer(ui, opts)
522 timer, fm = gettimer(ui, opts)
523 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
523 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
524 fm.end()
524 fm.end()
525
525
526 @command('perffncacheload', formatteropts)
526 @command('perffncacheload', formatteropts)
527 def perffncacheload(ui, repo, **opts):
527 def perffncacheload(ui, repo, **opts):
528 timer, fm = gettimer(ui, opts)
528 timer, fm = gettimer(ui, opts)
529 s = repo.store
529 s = repo.store
530 def d():
530 def d():
531 s.fncache._load()
531 s.fncache._load()
532 timer(d)
532 timer(d)
533 fm.end()
533 fm.end()
534
534
535 @command('perffncachewrite', formatteropts)
535 @command('perffncachewrite', formatteropts)
536 def perffncachewrite(ui, repo, **opts):
536 def perffncachewrite(ui, repo, **opts):
537 timer, fm = gettimer(ui, opts)
537 timer, fm = gettimer(ui, opts)
538 s = repo.store
538 s = repo.store
539 s.fncache._load()
539 s.fncache._load()
540 lock = repo.lock()
540 lock = repo.lock()
541 tr = repo.transaction('perffncachewrite')
541 tr = repo.transaction('perffncachewrite')
542 def d():
542 def d():
543 s.fncache._dirty = True
543 s.fncache._dirty = True
544 s.fncache.write(tr)
544 s.fncache.write(tr)
545 timer(d)
545 timer(d)
546 lock.release()
546 lock.release()
547 tr.close()
547 tr.close()
548 fm.end()
548 fm.end()
549
549
550 @command('perffncacheencode', formatteropts)
550 @command('perffncacheencode', formatteropts)
551 def perffncacheencode(ui, repo, **opts):
551 def perffncacheencode(ui, repo, **opts):
552 timer, fm = gettimer(ui, opts)
552 timer, fm = gettimer(ui, opts)
553 s = repo.store
553 s = repo.store
554 s.fncache._load()
554 s.fncache._load()
555 def d():
555 def d():
556 for p in s.fncache.entries:
556 for p in s.fncache.entries:
557 s.encode(p)
557 s.encode(p)
558 timer(d)
558 timer(d)
559 fm.end()
559 fm.end()
560
560
561 @command('perfdiffwd', formatteropts)
561 @command('perfdiffwd', formatteropts)
562 def perfdiffwd(ui, repo, **opts):
562 def perfdiffwd(ui, repo, **opts):
563 """Profile diff of working directory changes"""
563 """Profile diff of working directory changes"""
564 timer, fm = gettimer(ui, opts)
564 timer, fm = gettimer(ui, opts)
565 options = {
565 options = {
566 'w': 'ignore_all_space',
566 'w': 'ignore_all_space',
567 'b': 'ignore_space_change',
567 'b': 'ignore_space_change',
568 'B': 'ignore_blank_lines',
568 'B': 'ignore_blank_lines',
569 }
569 }
570
570
571 for diffopt in ('', 'w', 'b', 'B', 'wB'):
571 for diffopt in ('', 'w', 'b', 'B', 'wB'):
572 opts = dict((options[c], '1') for c in diffopt)
572 opts = dict((options[c], '1') for c in diffopt)
573 def d():
573 def d():
574 ui.pushbuffer()
574 ui.pushbuffer()
575 commands.diff(ui, repo, **opts)
575 commands.diff(ui, repo, **opts)
576 ui.popbuffer()
576 ui.popbuffer()
577 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
577 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
578 timer(d, title)
578 timer(d, title)
579 fm.end()
579 fm.end()
580
580
581 @command('perfrevlog', revlogopts + formatteropts +
581 @command('perfrevlog', revlogopts + formatteropts +
582 [('d', 'dist', 100, 'distance between the revisions'),
582 [('d', 'dist', 100, 'distance between the revisions'),
583 ('s', 'startrev', 0, 'revision to start reading at')],
583 ('s', 'startrev', 0, 'revision to start reading at'),
584 ('', 'reverse', False, 'read in reverse')],
584 '-c|-m|FILE')
585 '-c|-m|FILE')
585 def perfrevlog(ui, repo, file_=None, startrev=0, **opts):
586 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
586 """Benchmark reading a series of revisions from a revlog.
587 """Benchmark reading a series of revisions from a revlog.
587
588
588 By default, we read every ``-d/--dist`` revision from 0 to tip of
589 By default, we read every ``-d/--dist`` revision from 0 to tip of
589 the specified revlog.
590 the specified revlog.
590
591
591 The start revision can be defined via ``-s/--startrev``.
592 The start revision can be defined via ``-s/--startrev``.
592 """
593 """
593 timer, fm = gettimer(ui, opts)
594 timer, fm = gettimer(ui, opts)
594 dist = opts['dist']
595 _len = getlen(ui)
595 _len = getlen(ui)
596
596 def d():
597 def d():
597 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
598 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
598 for x in xrange(startrev, _len(r), dist):
599
600 startrev = 0
601 endrev = _len(r)
602 dist = opts['dist']
603
604 if reverse:
605 startrev, endrev = endrev, startrev
606 dist = -1 * dist
607
608 for x in xrange(startrev, endrev, dist):
599 r.revision(r.node(x))
609 r.revision(r.node(x))
600
610
601 timer(d)
611 timer(d)
602 fm.end()
612 fm.end()
603
613
604 @command('perfrevlogrevision', revlogopts + formatteropts +
614 @command('perfrevlogrevision', revlogopts + formatteropts +
605 [('', 'cache', False, 'use caches instead of clearing')],
615 [('', 'cache', False, 'use caches instead of clearing')],
606 '-c|-m|FILE REV')
616 '-c|-m|FILE REV')
607 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
617 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
608 """Benchmark obtaining a revlog revision.
618 """Benchmark obtaining a revlog revision.
609
619
610 Obtaining a revlog revision consists of roughly the following steps:
620 Obtaining a revlog revision consists of roughly the following steps:
611
621
612 1. Compute the delta chain
622 1. Compute the delta chain
613 2. Obtain the raw chunks for that delta chain
623 2. Obtain the raw chunks for that delta chain
614 3. Decompress each raw chunk
624 3. Decompress each raw chunk
615 4. Apply binary patches to obtain fulltext
625 4. Apply binary patches to obtain fulltext
616 5. Verify hash of fulltext
626 5. Verify hash of fulltext
617
627
618 This command measures the time spent in each of these phases.
628 This command measures the time spent in each of these phases.
619 """
629 """
620 if opts.get('changelog') or opts.get('manifest'):
630 if opts.get('changelog') or opts.get('manifest'):
621 file_, rev = None, file_
631 file_, rev = None, file_
622 elif rev is None:
632 elif rev is None:
623 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
633 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
624
634
625 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
635 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
626 node = r.lookup(rev)
636 node = r.lookup(rev)
627 rev = r.rev(node)
637 rev = r.rev(node)
628
638
629 def dodeltachain(rev):
639 def dodeltachain(rev):
630 if not cache:
640 if not cache:
631 r.clearcaches()
641 r.clearcaches()
632 r._deltachain(rev)
642 r._deltachain(rev)
633
643
634 def doread(chain):
644 def doread(chain):
635 if not cache:
645 if not cache:
636 r.clearcaches()
646 r.clearcaches()
637 r._chunkraw(chain[0], chain[-1])
647 r._chunkraw(chain[0], chain[-1])
638
648
639 def dodecompress(data, chain):
649 def dodecompress(data, chain):
640 if not cache:
650 if not cache:
641 r.clearcaches()
651 r.clearcaches()
642
652
643 start = r.start
653 start = r.start
644 length = r.length
654 length = r.length
645 inline = r._inline
655 inline = r._inline
646 iosize = r._io.size
656 iosize = r._io.size
647 buffer = util.buffer
657 buffer = util.buffer
648 offset = start(chain[0])
658 offset = start(chain[0])
649
659
650 for rev in chain:
660 for rev in chain:
651 chunkstart = start(rev)
661 chunkstart = start(rev)
652 if inline:
662 if inline:
653 chunkstart += (rev + 1) * iosize
663 chunkstart += (rev + 1) * iosize
654 chunklength = length(rev)
664 chunklength = length(rev)
655 b = buffer(data, chunkstart - offset, chunklength)
665 b = buffer(data, chunkstart - offset, chunklength)
656 revlog.decompress(b)
666 revlog.decompress(b)
657
667
658 def dopatch(text, bins):
668 def dopatch(text, bins):
659 if not cache:
669 if not cache:
660 r.clearcaches()
670 r.clearcaches()
661 mdiff.patches(text, bins)
671 mdiff.patches(text, bins)
662
672
663 def dohash(text):
673 def dohash(text):
664 if not cache:
674 if not cache:
665 r.clearcaches()
675 r.clearcaches()
666 r._checkhash(text, node, rev)
676 r._checkhash(text, node, rev)
667
677
668 def dorevision():
678 def dorevision():
669 if not cache:
679 if not cache:
670 r.clearcaches()
680 r.clearcaches()
671 r.revision(node)
681 r.revision(node)
672
682
673 chain = r._deltachain(rev)[0]
683 chain = r._deltachain(rev)[0]
674 data = r._chunkraw(chain[0], chain[-1])[1]
684 data = r._chunkraw(chain[0], chain[-1])[1]
675 bins = r._chunks(chain)
685 bins = r._chunks(chain)
676 text = str(bins[0])
686 text = str(bins[0])
677 bins = bins[1:]
687 bins = bins[1:]
678 text = mdiff.patches(text, bins)
688 text = mdiff.patches(text, bins)
679
689
680 benches = [
690 benches = [
681 (lambda: dorevision(), 'full'),
691 (lambda: dorevision(), 'full'),
682 (lambda: dodeltachain(rev), 'deltachain'),
692 (lambda: dodeltachain(rev), 'deltachain'),
683 (lambda: doread(chain), 'read'),
693 (lambda: doread(chain), 'read'),
684 (lambda: dodecompress(data, chain), 'decompress'),
694 (lambda: dodecompress(data, chain), 'decompress'),
685 (lambda: dopatch(text, bins), 'patch'),
695 (lambda: dopatch(text, bins), 'patch'),
686 (lambda: dohash(text), 'hash'),
696 (lambda: dohash(text), 'hash'),
687 ]
697 ]
688
698
689 for fn, title in benches:
699 for fn, title in benches:
690 timer, fm = gettimer(ui, opts)
700 timer, fm = gettimer(ui, opts)
691 timer(fn, title=title)
701 timer(fn, title=title)
692 fm.end()
702 fm.end()
693
703
694 @command('perfrevset',
704 @command('perfrevset',
695 [('C', 'clear', False, 'clear volatile cache between each call.'),
705 [('C', 'clear', False, 'clear volatile cache between each call.'),
696 ('', 'contexts', False, 'obtain changectx for each revision')]
706 ('', 'contexts', False, 'obtain changectx for each revision')]
697 + formatteropts, "REVSET")
707 + formatteropts, "REVSET")
698 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
708 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
699 """benchmark the execution time of a revset
709 """benchmark the execution time of a revset
700
710
701 Use the --clean option if need to evaluate the impact of build volatile
711 Use the --clean option if need to evaluate the impact of build volatile
702 revisions set cache on the revset execution. Volatile cache hold filtered
712 revisions set cache on the revset execution. Volatile cache hold filtered
703 and obsolete related cache."""
713 and obsolete related cache."""
704 timer, fm = gettimer(ui, opts)
714 timer, fm = gettimer(ui, opts)
705 def d():
715 def d():
706 if clear:
716 if clear:
707 repo.invalidatevolatilesets()
717 repo.invalidatevolatilesets()
708 if contexts:
718 if contexts:
709 for ctx in repo.set(expr): pass
719 for ctx in repo.set(expr): pass
710 else:
720 else:
711 for r in repo.revs(expr): pass
721 for r in repo.revs(expr): pass
712 timer(d)
722 timer(d)
713 fm.end()
723 fm.end()
714
724
715 @command('perfvolatilesets', formatteropts)
725 @command('perfvolatilesets', formatteropts)
716 def perfvolatilesets(ui, repo, *names, **opts):
726 def perfvolatilesets(ui, repo, *names, **opts):
717 """benchmark the computation of various volatile set
727 """benchmark the computation of various volatile set
718
728
719 Volatile set computes element related to filtering and obsolescence."""
729 Volatile set computes element related to filtering and obsolescence."""
720 timer, fm = gettimer(ui, opts)
730 timer, fm = gettimer(ui, opts)
721 repo = repo.unfiltered()
731 repo = repo.unfiltered()
722
732
723 def getobs(name):
733 def getobs(name):
724 def d():
734 def d():
725 repo.invalidatevolatilesets()
735 repo.invalidatevolatilesets()
726 obsolete.getrevs(repo, name)
736 obsolete.getrevs(repo, name)
727 return d
737 return d
728
738
729 allobs = sorted(obsolete.cachefuncs)
739 allobs = sorted(obsolete.cachefuncs)
730 if names:
740 if names:
731 allobs = [n for n in allobs if n in names]
741 allobs = [n for n in allobs if n in names]
732
742
733 for name in allobs:
743 for name in allobs:
734 timer(getobs(name), title=name)
744 timer(getobs(name), title=name)
735
745
736 def getfiltered(name):
746 def getfiltered(name):
737 def d():
747 def d():
738 repo.invalidatevolatilesets()
748 repo.invalidatevolatilesets()
739 repoview.filterrevs(repo, name)
749 repoview.filterrevs(repo, name)
740 return d
750 return d
741
751
742 allfilter = sorted(repoview.filtertable)
752 allfilter = sorted(repoview.filtertable)
743 if names:
753 if names:
744 allfilter = [n for n in allfilter if n in names]
754 allfilter = [n for n in allfilter if n in names]
745
755
746 for name in allfilter:
756 for name in allfilter:
747 timer(getfiltered(name), title=name)
757 timer(getfiltered(name), title=name)
748 fm.end()
758 fm.end()
749
759
750 @command('perfbranchmap',
760 @command('perfbranchmap',
751 [('f', 'full', False,
761 [('f', 'full', False,
752 'Includes build time of subset'),
762 'Includes build time of subset'),
753 ] + formatteropts)
763 ] + formatteropts)
754 def perfbranchmap(ui, repo, full=False, **opts):
764 def perfbranchmap(ui, repo, full=False, **opts):
755 """benchmark the update of a branchmap
765 """benchmark the update of a branchmap
756
766
757 This benchmarks the full repo.branchmap() call with read and write disabled
767 This benchmarks the full repo.branchmap() call with read and write disabled
758 """
768 """
759 timer, fm = gettimer(ui, opts)
769 timer, fm = gettimer(ui, opts)
760 def getbranchmap(filtername):
770 def getbranchmap(filtername):
761 """generate a benchmark function for the filtername"""
771 """generate a benchmark function for the filtername"""
762 if filtername is None:
772 if filtername is None:
763 view = repo
773 view = repo
764 else:
774 else:
765 view = repo.filtered(filtername)
775 view = repo.filtered(filtername)
766 def d():
776 def d():
767 if full:
777 if full:
768 view._branchcaches.clear()
778 view._branchcaches.clear()
769 else:
779 else:
770 view._branchcaches.pop(filtername, None)
780 view._branchcaches.pop(filtername, None)
771 view.branchmap()
781 view.branchmap()
772 return d
782 return d
773 # add filter in smaller subset to bigger subset
783 # add filter in smaller subset to bigger subset
774 possiblefilters = set(repoview.filtertable)
784 possiblefilters = set(repoview.filtertable)
775 allfilters = []
785 allfilters = []
776 while possiblefilters:
786 while possiblefilters:
777 for name in possiblefilters:
787 for name in possiblefilters:
778 subset = branchmap.subsettable.get(name)
788 subset = branchmap.subsettable.get(name)
779 if subset not in possiblefilters:
789 if subset not in possiblefilters:
780 break
790 break
781 else:
791 else:
782 assert False, 'subset cycle %s!' % possiblefilters
792 assert False, 'subset cycle %s!' % possiblefilters
783 allfilters.append(name)
793 allfilters.append(name)
784 possiblefilters.remove(name)
794 possiblefilters.remove(name)
785
795
786 # warm the cache
796 # warm the cache
787 if not full:
797 if not full:
788 for name in allfilters:
798 for name in allfilters:
789 repo.filtered(name).branchmap()
799 repo.filtered(name).branchmap()
790 # add unfiltered
800 # add unfiltered
791 allfilters.append(None)
801 allfilters.append(None)
792 oldread = branchmap.read
802 oldread = branchmap.read
793 oldwrite = branchmap.branchcache.write
803 oldwrite = branchmap.branchcache.write
794 try:
804 try:
795 branchmap.read = lambda repo: None
805 branchmap.read = lambda repo: None
796 branchmap.write = lambda repo: None
806 branchmap.write = lambda repo: None
797 for name in allfilters:
807 for name in allfilters:
798 timer(getbranchmap(name), title=str(name))
808 timer(getbranchmap(name), title=str(name))
799 finally:
809 finally:
800 branchmap.read = oldread
810 branchmap.read = oldread
801 branchmap.branchcache.write = oldwrite
811 branchmap.branchcache.write = oldwrite
802 fm.end()
812 fm.end()
803
813
804 @command('perfloadmarkers')
814 @command('perfloadmarkers')
805 def perfloadmarkers(ui, repo):
815 def perfloadmarkers(ui, repo):
806 """benchmark the time to parse the on-disk markers for a repo
816 """benchmark the time to parse the on-disk markers for a repo
807
817
808 Result is the number of markers in the repo."""
818 Result is the number of markers in the repo."""
809 timer, fm = gettimer(ui)
819 timer, fm = gettimer(ui)
810 timer(lambda: len(obsolete.obsstore(repo.svfs)))
820 timer(lambda: len(obsolete.obsstore(repo.svfs)))
811 fm.end()
821 fm.end()
812
822
813 @command('perflrucachedict', formatteropts +
823 @command('perflrucachedict', formatteropts +
814 [('', 'size', 4, 'size of cache'),
824 [('', 'size', 4, 'size of cache'),
815 ('', 'gets', 10000, 'number of key lookups'),
825 ('', 'gets', 10000, 'number of key lookups'),
816 ('', 'sets', 10000, 'number of key sets'),
826 ('', 'sets', 10000, 'number of key sets'),
817 ('', 'mixed', 10000, 'number of mixed mode operations'),
827 ('', 'mixed', 10000, 'number of mixed mode operations'),
818 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
828 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
819 norepo=True)
829 norepo=True)
820 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
830 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
821 mixedgetfreq=50, **opts):
831 mixedgetfreq=50, **opts):
822 def doinit():
832 def doinit():
823 for i in xrange(10000):
833 for i in xrange(10000):
824 util.lrucachedict(size)
834 util.lrucachedict(size)
825
835
826 values = []
836 values = []
827 for i in xrange(size):
837 for i in xrange(size):
828 values.append(random.randint(0, sys.maxint))
838 values.append(random.randint(0, sys.maxint))
829
839
830 # Get mode fills the cache and tests raw lookup performance with no
840 # Get mode fills the cache and tests raw lookup performance with no
831 # eviction.
841 # eviction.
832 getseq = []
842 getseq = []
833 for i in xrange(gets):
843 for i in xrange(gets):
834 getseq.append(random.choice(values))
844 getseq.append(random.choice(values))
835
845
836 def dogets():
846 def dogets():
837 d = util.lrucachedict(size)
847 d = util.lrucachedict(size)
838 for v in values:
848 for v in values:
839 d[v] = v
849 d[v] = v
840 for key in getseq:
850 for key in getseq:
841 value = d[key]
851 value = d[key]
842 value # silence pyflakes warning
852 value # silence pyflakes warning
843
853
844 # Set mode tests insertion speed with cache eviction.
854 # Set mode tests insertion speed with cache eviction.
845 setseq = []
855 setseq = []
846 for i in xrange(sets):
856 for i in xrange(sets):
847 setseq.append(random.randint(0, sys.maxint))
857 setseq.append(random.randint(0, sys.maxint))
848
858
849 def dosets():
859 def dosets():
850 d = util.lrucachedict(size)
860 d = util.lrucachedict(size)
851 for v in setseq:
861 for v in setseq:
852 d[v] = v
862 d[v] = v
853
863
854 # Mixed mode randomly performs gets and sets with eviction.
864 # Mixed mode randomly performs gets and sets with eviction.
855 mixedops = []
865 mixedops = []
856 for i in xrange(mixed):
866 for i in xrange(mixed):
857 r = random.randint(0, 100)
867 r = random.randint(0, 100)
858 if r < mixedgetfreq:
868 if r < mixedgetfreq:
859 op = 0
869 op = 0
860 else:
870 else:
861 op = 1
871 op = 1
862
872
863 mixedops.append((op, random.randint(0, size * 2)))
873 mixedops.append((op, random.randint(0, size * 2)))
864
874
865 def domixed():
875 def domixed():
866 d = util.lrucachedict(size)
876 d = util.lrucachedict(size)
867
877
868 for op, v in mixedops:
878 for op, v in mixedops:
869 if op == 0:
879 if op == 0:
870 try:
880 try:
871 d[v]
881 d[v]
872 except KeyError:
882 except KeyError:
873 pass
883 pass
874 else:
884 else:
875 d[v] = v
885 d[v] = v
876
886
877 benches = [
887 benches = [
878 (doinit, 'init'),
888 (doinit, 'init'),
879 (dogets, 'gets'),
889 (dogets, 'gets'),
880 (dosets, 'sets'),
890 (dosets, 'sets'),
881 (domixed, 'mixed')
891 (domixed, 'mixed')
882 ]
892 ]
883
893
884 for fn, title in benches:
894 for fn, title in benches:
885 timer, fm = gettimer(ui, opts)
895 timer, fm = gettimer(ui, opts)
886 timer(fn, title=title)
896 timer(fn, title=title)
887 fm.end()
897 fm.end()
888
898
889 def uisetup(ui):
899 def uisetup(ui):
890 if (util.safehasattr(cmdutil, 'openrevlog') and
900 if (util.safehasattr(cmdutil, 'openrevlog') and
891 not util.safehasattr(commands, 'debugrevlogopts')):
901 not util.safehasattr(commands, 'debugrevlogopts')):
892 # for "historical portability":
902 # for "historical portability":
893 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
903 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
894 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
904 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
895 # openrevlog() should cause failure, because it has been
905 # openrevlog() should cause failure, because it has been
896 # available since 3.5 (or 49c583ca48c4).
906 # available since 3.5 (or 49c583ca48c4).
897 def openrevlog(orig, repo, cmd, file_, opts):
907 def openrevlog(orig, repo, cmd, file_, opts):
898 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
908 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
899 raise error.Abort("This version doesn't support --dir option",
909 raise error.Abort("This version doesn't support --dir option",
900 hint="use 3.5 or later")
910 hint="use 3.5 or later")
901 return orig(repo, cmd, file_, opts)
911 return orig(repo, cmd, file_, opts)
902 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
912 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now