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