##// END OF EJS Templates
perf: add perfmoonwalk command to walk the changelog backwards...
Brodie Rao -
r20178:74aea4be default
parent child Browse files
Show More
@@ -1,412 +1,424 b''
1 1 # perf.py - performance test routines
2 2 '''helper extension to measure performance'''
3 3
4 4 from mercurial import cmdutil, scmutil, util, commands, obsolete
5 5 from mercurial import repoview, branchmap, merge, copies
6 6 import time, os, sys
7 7
8 8 cmdtable = {}
9 9 command = cmdutil.command(cmdtable)
10 10
11 11 def timer(func, title=None):
12 12 results = []
13 13 begin = time.time()
14 14 count = 0
15 15 while True:
16 16 ostart = os.times()
17 17 cstart = time.time()
18 18 r = func()
19 19 cstop = time.time()
20 20 ostop = os.times()
21 21 count += 1
22 22 a, b = ostart, ostop
23 23 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
24 24 if cstop - begin > 3 and count >= 100:
25 25 break
26 26 if cstop - begin > 10 and count >= 3:
27 27 break
28 28 if title:
29 29 sys.stderr.write("! %s\n" % title)
30 30 if r:
31 31 sys.stderr.write("! result: %s\n" % r)
32 32 m = min(results)
33 33 sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n"
34 34 % (m[0], m[1] + m[2], m[1], m[2], count))
35 35
36 36 @command('perfwalk')
37 37 def perfwalk(ui, repo, *pats):
38 38 try:
39 39 m = scmutil.match(repo[None], pats, {})
40 40 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
41 41 except Exception:
42 42 try:
43 43 m = scmutil.match(repo[None], pats, {})
44 44 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
45 45 except Exception:
46 46 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
47 47
48 48 @command('perfannotate')
49 49 def perfannotate(ui, repo, f):
50 50 fc = repo['.'][f]
51 51 timer(lambda: len(fc.annotate(True)))
52 52
53 53 @command('perfstatus',
54 54 [('u', 'unknown', False,
55 55 'ask status to look for unknown files')])
56 56 def perfstatus(ui, repo, **opts):
57 57 #m = match.always(repo.root, repo.getcwd())
58 58 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
59 59 # False))))
60 60 timer(lambda: sum(map(len, repo.status(**opts))))
61 61
62 62 @command('perfaddremove')
63 63 def perfaddremove(ui, repo):
64 64 try:
65 65 oldquiet = repo.ui.quiet
66 66 repo.ui.quiet = True
67 67 timer(lambda: scmutil.addremove(repo, dry_run=True))
68 68 finally:
69 69 repo.ui.quiet = oldquiet
70 70
71 71 def clearcaches(cl):
72 72 # behave somewhat consistently across internal API changes
73 73 if util.safehasattr(cl, 'clearcaches'):
74 74 cl.clearcaches()
75 75 elif util.safehasattr(cl, '_nodecache'):
76 76 from mercurial.node import nullid, nullrev
77 77 cl._nodecache = {nullid: nullrev}
78 78 cl._nodepos = None
79 79
80 80 @command('perfheads')
81 81 def perfheads(ui, repo):
82 82 cl = repo.changelog
83 83 def d():
84 84 len(cl.headrevs())
85 85 clearcaches(cl)
86 86 timer(d)
87 87
88 88 @command('perftags')
89 89 def perftags(ui, repo):
90 90 import mercurial.changelog
91 91 import mercurial.manifest
92 92 def t():
93 93 repo.changelog = mercurial.changelog.changelog(repo.sopener)
94 94 repo.manifest = mercurial.manifest.manifest(repo.sopener)
95 95 repo._tags = None
96 96 return len(repo.tags())
97 97 timer(t)
98 98
99 99 @command('perfancestors')
100 100 def perfancestors(ui, repo):
101 101 heads = repo.changelog.headrevs()
102 102 def d():
103 103 for a in repo.changelog.ancestors(heads):
104 104 pass
105 105 timer(d)
106 106
107 107 @command('perfancestorset')
108 108 def perfancestorset(ui, repo, revset):
109 109 revs = repo.revs(revset)
110 110 heads = repo.changelog.headrevs()
111 111 def d():
112 112 s = repo.changelog.ancestors(heads)
113 113 for rev in revs:
114 114 rev in s
115 115 timer(d)
116 116
117 117 @command('perfdirs')
118 118 def perfdirs(ui, repo):
119 119 dirstate = repo.dirstate
120 120 'a' in dirstate
121 121 def d():
122 122 dirstate.dirs()
123 123 del dirstate._dirs
124 124 timer(d)
125 125
126 126 @command('perfdirstate')
127 127 def perfdirstate(ui, repo):
128 128 "a" in repo.dirstate
129 129 def d():
130 130 repo.dirstate.invalidate()
131 131 "a" in repo.dirstate
132 132 timer(d)
133 133
134 134 @command('perfdirstatedirs')
135 135 def perfdirstatedirs(ui, repo):
136 136 "a" in repo.dirstate
137 137 def d():
138 138 "a" in repo.dirstate._dirs
139 139 del repo.dirstate._dirs
140 140 timer(d)
141 141
142 142 @command('perfdirstatewrite')
143 143 def perfdirstatewrite(ui, repo):
144 144 ds = repo.dirstate
145 145 "a" in ds
146 146 def d():
147 147 ds._dirty = True
148 148 ds.write()
149 149 timer(d)
150 150
151 151 @command('perfmergecalculate',
152 152 [('r', 'rev', '.', 'rev to merge against')])
153 153 def perfmergecalculate(ui, repo, rev):
154 154 wctx = repo[None]
155 155 rctx = scmutil.revsingle(repo, rev, rev)
156 156 ancestor = wctx.ancestor(rctx)
157 157 # we don't want working dir files to be stat'd in the benchmark, so prime
158 158 # that cache
159 159 wctx.dirty()
160 160 def d():
161 161 # acceptremote is True because we don't want prompts in the middle of
162 162 # our benchmark
163 163 merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False,
164 164 acceptremote=True)
165 165 timer(d)
166 166
167 167 @command('perfpathcopies', [], "REV REV")
168 168 def perfpathcopies(ui, repo, rev1, rev2):
169 169 ctx1 = scmutil.revsingle(repo, rev1, rev1)
170 170 ctx2 = scmutil.revsingle(repo, rev2, rev2)
171 171 def d():
172 172 copies.pathcopies(ctx1, ctx2)
173 173 timer(d)
174 174
175 175 @command('perfmanifest', [], 'REV')
176 176 def perfmanifest(ui, repo, rev):
177 177 ctx = scmutil.revsingle(repo, rev, rev)
178 178 t = ctx.manifestnode()
179 179 def d():
180 180 repo.manifest._mancache.clear()
181 181 repo.manifest._cache = None
182 182 repo.manifest.read(t)
183 183 timer(d)
184 184
185 185 @command('perfchangeset')
186 186 def perfchangeset(ui, repo, rev):
187 187 n = repo[rev].node()
188 188 def d():
189 189 repo.changelog.read(n)
190 190 #repo.changelog._cache = None
191 191 timer(d)
192 192
193 193 @command('perfindex')
194 194 def perfindex(ui, repo):
195 195 import mercurial.revlog
196 196 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
197 197 n = repo["tip"].node()
198 198 def d():
199 199 cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i")
200 200 cl.rev(n)
201 201 timer(d)
202 202
203 203 @command('perfstartup')
204 204 def perfstartup(ui, repo):
205 205 cmd = sys.argv[0]
206 206 def d():
207 207 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
208 208 timer(d)
209 209
210 210 @command('perfparents')
211 211 def perfparents(ui, repo):
212 212 nl = [repo.changelog.node(i) for i in xrange(1000)]
213 213 def d():
214 214 for n in nl:
215 215 repo.changelog.parents(n)
216 216 timer(d)
217 217
218 218 @command('perflookup')
219 219 def perflookup(ui, repo, rev):
220 220 timer(lambda: len(repo.lookup(rev)))
221 221
222 222 @command('perfrevrange')
223 223 def perfrevrange(ui, repo, *specs):
224 224 revrange = scmutil.revrange
225 225 timer(lambda: len(revrange(repo, specs)))
226 226
227 227 @command('perfnodelookup')
228 228 def perfnodelookup(ui, repo, rev):
229 229 import mercurial.revlog
230 230 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
231 231 n = repo[rev].node()
232 232 cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i")
233 233 def d():
234 234 cl.rev(n)
235 235 clearcaches(cl)
236 236 timer(d)
237 237
238 238 @command('perflog',
239 239 [('', 'rename', False, 'ask log to follow renames')])
240 240 def perflog(ui, repo, **opts):
241 241 ui.pushbuffer()
242 242 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
243 243 copies=opts.get('rename')))
244 244 ui.popbuffer()
245 245
246 @command('perfmoonwalk')
247 def perfmoonwalk(ui, repo):
248 """benchmark walking the changelog backwards
249
250 This also loads the changelog data for each revision in the changelog.
251 """
252 def moonwalk():
253 for i in xrange(len(repo), -1, -1):
254 ctx = repo[i]
255 ctx.branch() # read changelog data (in addition to the index)
256 timer(moonwalk)
257
246 258 @command('perftemplating')
247 259 def perftemplating(ui, repo):
248 260 ui.pushbuffer()
249 261 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
250 262 template='{date|shortdate} [{rev}:{node|short}]'
251 263 ' {author|person}: {desc|firstline}\n'))
252 264 ui.popbuffer()
253 265
254 266 @command('perfcca')
255 267 def perfcca(ui, repo):
256 268 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
257 269
258 270 @command('perffncacheload')
259 271 def perffncacheload(ui, repo):
260 272 s = repo.store
261 273 def d():
262 274 s.fncache._load()
263 275 timer(d)
264 276
265 277 @command('perffncachewrite')
266 278 def perffncachewrite(ui, repo):
267 279 s = repo.store
268 280 s.fncache._load()
269 281 def d():
270 282 s.fncache._dirty = True
271 283 s.fncache.write()
272 284 timer(d)
273 285
274 286 @command('perffncacheencode')
275 287 def perffncacheencode(ui, repo):
276 288 s = repo.store
277 289 s.fncache._load()
278 290 def d():
279 291 for p in s.fncache.entries:
280 292 s.encode(p)
281 293 timer(d)
282 294
283 295 @command('perfdiffwd')
284 296 def perfdiffwd(ui, repo):
285 297 """Profile diff of working directory changes"""
286 298 options = {
287 299 'w': 'ignore_all_space',
288 300 'b': 'ignore_space_change',
289 301 'B': 'ignore_blank_lines',
290 302 }
291 303
292 304 for diffopt in ('', 'w', 'b', 'B', 'wB'):
293 305 opts = dict((options[c], '1') for c in diffopt)
294 306 def d():
295 307 ui.pushbuffer()
296 308 commands.diff(ui, repo, **opts)
297 309 ui.popbuffer()
298 310 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
299 311 timer(d, title)
300 312
301 313 @command('perfrevlog',
302 314 [('d', 'dist', 100, 'distance between the revisions')],
303 315 "[INDEXFILE]")
304 316 def perfrevlog(ui, repo, file_, **opts):
305 317 from mercurial import revlog
306 318 dist = opts['dist']
307 319 def d():
308 320 r = revlog.revlog(lambda fn: open(fn, 'rb'), file_)
309 321 for x in xrange(0, len(r), dist):
310 322 r.revision(r.node(x))
311 323
312 324 timer(d)
313 325
314 326 @command('perfrevset',
315 327 [('C', 'clear', False, 'clear volatile cache between each call.')],
316 328 "REVSET")
317 329 def perfrevset(ui, repo, expr, clear=False):
318 330 """benchmark the execution time of a revset
319 331
320 332 Use the --clean option if need to evaluate the impact of build volatile
321 333 revisions set cache on the revset execution. Volatile cache hold filtered
322 334 and obsolete related cache."""
323 335 def d():
324 336 if clear:
325 337 repo.invalidatevolatilesets()
326 338 repo.revs(expr)
327 339 timer(d)
328 340
329 341 @command('perfvolatilesets')
330 342 def perfvolatilesets(ui, repo, *names):
331 343 """benchmark the computation of various volatile set
332 344
333 345 Volatile set computes element related to filtering and obsolescence."""
334 346 repo = repo.unfiltered()
335 347
336 348 def getobs(name):
337 349 def d():
338 350 repo.invalidatevolatilesets()
339 351 obsolete.getrevs(repo, name)
340 352 return d
341 353
342 354 allobs = sorted(obsolete.cachefuncs)
343 355 if names:
344 356 allobs = [n for n in allobs if n in names]
345 357
346 358 for name in allobs:
347 359 timer(getobs(name), title=name)
348 360
349 361 def getfiltered(name):
350 362 def d():
351 363 repo.invalidatevolatilesets()
352 364 repoview.filteredrevs(repo, name)
353 365 return d
354 366
355 367 allfilter = sorted(repoview.filtertable)
356 368 if names:
357 369 allfilter = [n for n in allfilter if n in names]
358 370
359 371 for name in allfilter:
360 372 timer(getfiltered(name), title=name)
361 373
362 374 @command('perfbranchmap',
363 375 [('f', 'full', False,
364 376 'Includes build time of subset'),
365 377 ])
366 378 def perfbranchmap(ui, repo, full=False):
367 379 """benchmark the update of a branchmap
368 380
369 381 This benchmarks the full repo.branchmap() call with read and write disabled
370 382 """
371 383 def getbranchmap(filtername):
372 384 """generate a benchmark function for the filtername"""
373 385 if filtername is None:
374 386 view = repo
375 387 else:
376 388 view = repo.filtered(filtername)
377 389 def d():
378 390 if full:
379 391 view._branchcaches.clear()
380 392 else:
381 393 view._branchcaches.pop(filtername, None)
382 394 view.branchmap()
383 395 return d
384 396 # add filter in smaller subset to bigger subset
385 397 possiblefilters = set(repoview.filtertable)
386 398 allfilters = []
387 399 while possiblefilters:
388 400 for name in possiblefilters:
389 401 subset = branchmap.subsettable.get(name)
390 402 if subset not in possiblefilters:
391 403 break
392 404 else:
393 405 assert False, 'subset cycle %s!' % possiblefilters
394 406 allfilters.append(name)
395 407 possiblefilters.remove(name)
396 408
397 409 # warm the cache
398 410 if not full:
399 411 for name in allfilters:
400 412 repo.filtered(name).branchmap()
401 413 # add unfiltered
402 414 allfilters.append(None)
403 415 oldread = branchmap.read
404 416 oldwrite = branchmap.branchcache.write
405 417 try:
406 418 branchmap.read = lambda repo: None
407 419 branchmap.write = lambda repo: None
408 420 for name in allfilters:
409 421 timer(getbranchmap(name), title=str(name))
410 422 finally:
411 423 branchmap.read = oldread
412 424 branchmap.branchcache.write = oldwrite
General Comments 0
You need to be logged in to leave comments. Login now