##// END OF EJS Templates
contrib/perf: perfparents handle filtered repos
timeless -
r27100:8d5dba93 default
parent child Browse files
Show More
@@ -1,583 +1,584 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, error
6 6 import time, os, sys
7 7 import functools
8 8
9 9 formatteropts = commands.formatteropts
10 10
11 11 cmdtable = {}
12 12 command = cmdutil.command(cmdtable)
13 13
14 14 def gettimer(ui, opts=None):
15 15 """return a timer function and formatter: (timer, formatter)
16 16
17 17 This functions exist to gather the creation of formatter in a single
18 18 place instead of duplicating it in all performance command."""
19 19
20 20 # enforce an idle period before execution to counteract power management
21 21 # experimental config: perf.presleep
22 22 time.sleep(ui.configint("perf", "presleep", 1))
23 23
24 24 if opts is None:
25 25 opts = {}
26 26 # redirect all to stderr
27 27 ui = ui.copy()
28 28 ui.fout = ui.ferr
29 29 # get a formatter
30 30 fm = ui.formatter('perf', opts)
31 31 return functools.partial(_timer, fm), fm
32 32
33 33 def _timer(fm, func, title=None):
34 34 results = []
35 35 begin = time.time()
36 36 count = 0
37 37 while True:
38 38 ostart = os.times()
39 39 cstart = time.time()
40 40 r = func()
41 41 cstop = time.time()
42 42 ostop = os.times()
43 43 count += 1
44 44 a, b = ostart, ostop
45 45 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
46 46 if cstop - begin > 3 and count >= 100:
47 47 break
48 48 if cstop - begin > 10 and count >= 3:
49 49 break
50 50
51 51 fm.startitem()
52 52
53 53 if title:
54 54 fm.write('title', '! %s\n', title)
55 55 if r:
56 56 fm.write('result', '! result: %s\n', r)
57 57 m = min(results)
58 58 fm.plain('!')
59 59 fm.write('wall', ' wall %f', m[0])
60 60 fm.write('comb', ' comb %f', m[1] + m[2])
61 61 fm.write('user', ' user %f', m[1])
62 62 fm.write('sys', ' sys %f', m[2])
63 63 fm.write('count', ' (best of %d)', count)
64 64 fm.plain('\n')
65 65
66 66 @command('perfwalk', formatteropts)
67 67 def perfwalk(ui, repo, *pats, **opts):
68 68 timer, fm = gettimer(ui, opts)
69 69 try:
70 70 m = scmutil.match(repo[None], pats, {})
71 71 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
72 72 except Exception:
73 73 try:
74 74 m = scmutil.match(repo[None], pats, {})
75 75 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
76 76 except Exception:
77 77 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
78 78 fm.end()
79 79
80 80 @command('perfannotate', formatteropts)
81 81 def perfannotate(ui, repo, f, **opts):
82 82 timer, fm = gettimer(ui, opts)
83 83 fc = repo['.'][f]
84 84 timer(lambda: len(fc.annotate(True)))
85 85 fm.end()
86 86
87 87 @command('perfstatus',
88 88 [('u', 'unknown', False,
89 89 'ask status to look for unknown files')] + formatteropts)
90 90 def perfstatus(ui, repo, **opts):
91 91 #m = match.always(repo.root, repo.getcwd())
92 92 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
93 93 # False))))
94 94 timer, fm = gettimer(ui, opts)
95 95 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
96 96 fm.end()
97 97
98 98 @command('perfaddremove', formatteropts)
99 99 def perfaddremove(ui, repo, **opts):
100 100 timer, fm = gettimer(ui, opts)
101 101 try:
102 102 oldquiet = repo.ui.quiet
103 103 repo.ui.quiet = True
104 104 matcher = scmutil.match(repo[None])
105 105 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
106 106 finally:
107 107 repo.ui.quiet = oldquiet
108 108 fm.end()
109 109
110 110 def clearcaches(cl):
111 111 # behave somewhat consistently across internal API changes
112 112 if util.safehasattr(cl, 'clearcaches'):
113 113 cl.clearcaches()
114 114 elif util.safehasattr(cl, '_nodecache'):
115 115 from mercurial.node import nullid, nullrev
116 116 cl._nodecache = {nullid: nullrev}
117 117 cl._nodepos = None
118 118
119 119 @command('perfheads', formatteropts)
120 120 def perfheads(ui, repo, **opts):
121 121 timer, fm = gettimer(ui, opts)
122 122 cl = repo.changelog
123 123 def d():
124 124 len(cl.headrevs())
125 125 clearcaches(cl)
126 126 timer(d)
127 127 fm.end()
128 128
129 129 @command('perftags', formatteropts)
130 130 def perftags(ui, repo, **opts):
131 131 import mercurial.changelog
132 132 import mercurial.manifest
133 133 timer, fm = gettimer(ui, opts)
134 134 def t():
135 135 repo.changelog = mercurial.changelog.changelog(repo.svfs)
136 136 repo.manifest = mercurial.manifest.manifest(repo.svfs)
137 137 repo._tags = None
138 138 return len(repo.tags())
139 139 timer(t)
140 140 fm.end()
141 141
142 142 @command('perfancestors', formatteropts)
143 143 def perfancestors(ui, repo, **opts):
144 144 timer, fm = gettimer(ui, opts)
145 145 heads = repo.changelog.headrevs()
146 146 def d():
147 147 for a in repo.changelog.ancestors(heads):
148 148 pass
149 149 timer(d)
150 150 fm.end()
151 151
152 152 @command('perfancestorset', formatteropts)
153 153 def perfancestorset(ui, repo, revset, **opts):
154 154 timer, fm = gettimer(ui, opts)
155 155 revs = repo.revs(revset)
156 156 heads = repo.changelog.headrevs()
157 157 def d():
158 158 s = repo.changelog.ancestors(heads)
159 159 for rev in revs:
160 160 rev in s
161 161 timer(d)
162 162 fm.end()
163 163
164 164 @command('perfdirs', formatteropts)
165 165 def perfdirs(ui, repo, **opts):
166 166 timer, fm = gettimer(ui, opts)
167 167 dirstate = repo.dirstate
168 168 'a' in dirstate
169 169 def d():
170 170 dirstate.dirs()
171 171 del dirstate._dirs
172 172 timer(d)
173 173 fm.end()
174 174
175 175 @command('perfdirstate', formatteropts)
176 176 def perfdirstate(ui, repo, **opts):
177 177 timer, fm = gettimer(ui, opts)
178 178 "a" in repo.dirstate
179 179 def d():
180 180 repo.dirstate.invalidate()
181 181 "a" in repo.dirstate
182 182 timer(d)
183 183 fm.end()
184 184
185 185 @command('perfdirstatedirs', formatteropts)
186 186 def perfdirstatedirs(ui, repo, **opts):
187 187 timer, fm = gettimer(ui, opts)
188 188 "a" in repo.dirstate
189 189 def d():
190 190 "a" in repo.dirstate._dirs
191 191 del repo.dirstate._dirs
192 192 timer(d)
193 193 fm.end()
194 194
195 195 @command('perfdirstatefoldmap', formatteropts)
196 196 def perfdirstatefoldmap(ui, repo, **opts):
197 197 timer, fm = gettimer(ui, opts)
198 198 dirstate = repo.dirstate
199 199 'a' in dirstate
200 200 def d():
201 201 dirstate._filefoldmap.get('a')
202 202 del dirstate._filefoldmap
203 203 timer(d)
204 204 fm.end()
205 205
206 206 @command('perfdirfoldmap', formatteropts)
207 207 def perfdirfoldmap(ui, repo, **opts):
208 208 timer, fm = gettimer(ui, opts)
209 209 dirstate = repo.dirstate
210 210 'a' in dirstate
211 211 def d():
212 212 dirstate._dirfoldmap.get('a')
213 213 del dirstate._dirfoldmap
214 214 del dirstate._dirs
215 215 timer(d)
216 216 fm.end()
217 217
218 218 @command('perfdirstatewrite', formatteropts)
219 219 def perfdirstatewrite(ui, repo, **opts):
220 220 timer, fm = gettimer(ui, opts)
221 221 ds = repo.dirstate
222 222 "a" in ds
223 223 def d():
224 224 ds._dirty = True
225 225 ds.write(repo.currenttransaction())
226 226 timer(d)
227 227 fm.end()
228 228
229 229 @command('perfmergecalculate',
230 230 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
231 231 def perfmergecalculate(ui, repo, rev, **opts):
232 232 timer, fm = gettimer(ui, opts)
233 233 wctx = repo[None]
234 234 rctx = scmutil.revsingle(repo, rev, rev)
235 235 ancestor = wctx.ancestor(rctx)
236 236 # we don't want working dir files to be stat'd in the benchmark, so prime
237 237 # that cache
238 238 wctx.dirty()
239 239 def d():
240 240 # acceptremote is True because we don't want prompts in the middle of
241 241 # our benchmark
242 242 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
243 243 False, acceptremote=True, followcopies=True)
244 244 timer(d)
245 245 fm.end()
246 246
247 247 @command('perfpathcopies', [], "REV REV")
248 248 def perfpathcopies(ui, repo, rev1, rev2, **opts):
249 249 timer, fm = gettimer(ui, opts)
250 250 ctx1 = scmutil.revsingle(repo, rev1, rev1)
251 251 ctx2 = scmutil.revsingle(repo, rev2, rev2)
252 252 def d():
253 253 copies.pathcopies(ctx1, ctx2)
254 254 timer(d)
255 255 fm.end()
256 256
257 257 @command('perfmanifest', [], 'REV')
258 258 def perfmanifest(ui, repo, rev, **opts):
259 259 timer, fm = gettimer(ui, opts)
260 260 ctx = scmutil.revsingle(repo, rev, rev)
261 261 t = ctx.manifestnode()
262 262 def d():
263 263 repo.manifest._mancache.clear()
264 264 repo.manifest._cache = None
265 265 repo.manifest.read(t)
266 266 timer(d)
267 267 fm.end()
268 268
269 269 @command('perfchangeset', formatteropts)
270 270 def perfchangeset(ui, repo, rev, **opts):
271 271 timer, fm = gettimer(ui, opts)
272 272 n = repo[rev].node()
273 273 def d():
274 274 repo.changelog.read(n)
275 275 #repo.changelog._cache = None
276 276 timer(d)
277 277 fm.end()
278 278
279 279 @command('perfindex', formatteropts)
280 280 def perfindex(ui, repo, **opts):
281 281 import mercurial.revlog
282 282 timer, fm = gettimer(ui, opts)
283 283 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
284 284 n = repo["tip"].node()
285 285 def d():
286 286 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
287 287 cl.rev(n)
288 288 timer(d)
289 289 fm.end()
290 290
291 291 @command('perfstartup', formatteropts)
292 292 def perfstartup(ui, repo, **opts):
293 293 timer, fm = gettimer(ui, opts)
294 294 cmd = sys.argv[0]
295 295 def d():
296 296 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
297 297 timer(d)
298 298 fm.end()
299 299
300 300 @command('perfparents', formatteropts)
301 301 def perfparents(ui, repo, **opts):
302 302 timer, fm = gettimer(ui, opts)
303 303 if len(repo.changelog) < 1000:
304 304 raise error.Abort("repo needs 1000 commits for this test")
305 repo = repo.unfiltered()
305 306 nl = [repo.changelog.node(i) for i in xrange(1000)]
306 307 def d():
307 308 for n in nl:
308 309 repo.changelog.parents(n)
309 310 timer(d)
310 311 fm.end()
311 312
312 313 @command('perfctxfiles', formatteropts)
313 314 def perfctxfiles(ui, repo, x, **opts):
314 315 x = int(x)
315 316 timer, fm = gettimer(ui, opts)
316 317 def d():
317 318 len(repo[x].files())
318 319 timer(d)
319 320 fm.end()
320 321
321 322 @command('perfrawfiles', formatteropts)
322 323 def perfrawfiles(ui, repo, x, **opts):
323 324 x = int(x)
324 325 timer, fm = gettimer(ui, opts)
325 326 cl = repo.changelog
326 327 def d():
327 328 len(cl.read(x)[3])
328 329 timer(d)
329 330 fm.end()
330 331
331 332 @command('perflookup', formatteropts)
332 333 def perflookup(ui, repo, rev, **opts):
333 334 timer, fm = gettimer(ui, opts)
334 335 timer(lambda: len(repo.lookup(rev)))
335 336 fm.end()
336 337
337 338 @command('perfrevrange', formatteropts)
338 339 def perfrevrange(ui, repo, *specs, **opts):
339 340 timer, fm = gettimer(ui, opts)
340 341 revrange = scmutil.revrange
341 342 timer(lambda: len(revrange(repo, specs)))
342 343 fm.end()
343 344
344 345 @command('perfnodelookup', formatteropts)
345 346 def perfnodelookup(ui, repo, rev, **opts):
346 347 timer, fm = gettimer(ui, opts)
347 348 import mercurial.revlog
348 349 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
349 350 n = repo[rev].node()
350 351 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
351 352 def d():
352 353 cl.rev(n)
353 354 clearcaches(cl)
354 355 timer(d)
355 356 fm.end()
356 357
357 358 @command('perflog',
358 359 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
359 360 def perflog(ui, repo, **opts):
360 361 timer, fm = gettimer(ui, opts)
361 362 ui.pushbuffer()
362 363 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
363 364 copies=opts.get('rename')))
364 365 ui.popbuffer()
365 366 fm.end()
366 367
367 368 @command('perfmoonwalk', formatteropts)
368 369 def perfmoonwalk(ui, repo, **opts):
369 370 """benchmark walking the changelog backwards
370 371
371 372 This also loads the changelog data for each revision in the changelog.
372 373 """
373 374 timer, fm = gettimer(ui, opts)
374 375 def moonwalk():
375 376 for i in xrange(len(repo), -1, -1):
376 377 ctx = repo[i]
377 378 ctx.branch() # read changelog data (in addition to the index)
378 379 timer(moonwalk)
379 380 fm.end()
380 381
381 382 @command('perftemplating', formatteropts)
382 383 def perftemplating(ui, repo, **opts):
383 384 timer, fm = gettimer(ui, opts)
384 385 ui.pushbuffer()
385 386 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
386 387 template='{date|shortdate} [{rev}:{node|short}]'
387 388 ' {author|person}: {desc|firstline}\n'))
388 389 ui.popbuffer()
389 390 fm.end()
390 391
391 392 @command('perfcca', formatteropts)
392 393 def perfcca(ui, repo, **opts):
393 394 timer, fm = gettimer(ui, opts)
394 395 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
395 396 fm.end()
396 397
397 398 @command('perffncacheload', formatteropts)
398 399 def perffncacheload(ui, repo, **opts):
399 400 timer, fm = gettimer(ui, opts)
400 401 s = repo.store
401 402 def d():
402 403 s.fncache._load()
403 404 timer(d)
404 405 fm.end()
405 406
406 407 @command('perffncachewrite', formatteropts)
407 408 def perffncachewrite(ui, repo, **opts):
408 409 timer, fm = gettimer(ui, opts)
409 410 s = repo.store
410 411 s.fncache._load()
411 412 lock = repo.lock()
412 413 tr = repo.transaction('perffncachewrite')
413 414 def d():
414 415 s.fncache._dirty = True
415 416 s.fncache.write(tr)
416 417 timer(d)
417 418 lock.release()
418 419 fm.end()
419 420
420 421 @command('perffncacheencode', formatteropts)
421 422 def perffncacheencode(ui, repo, **opts):
422 423 timer, fm = gettimer(ui, opts)
423 424 s = repo.store
424 425 s.fncache._load()
425 426 def d():
426 427 for p in s.fncache.entries:
427 428 s.encode(p)
428 429 timer(d)
429 430 fm.end()
430 431
431 432 @command('perfdiffwd', formatteropts)
432 433 def perfdiffwd(ui, repo, **opts):
433 434 """Profile diff of working directory changes"""
434 435 timer, fm = gettimer(ui, opts)
435 436 options = {
436 437 'w': 'ignore_all_space',
437 438 'b': 'ignore_space_change',
438 439 'B': 'ignore_blank_lines',
439 440 }
440 441
441 442 for diffopt in ('', 'w', 'b', 'B', 'wB'):
442 443 opts = dict((options[c], '1') for c in diffopt)
443 444 def d():
444 445 ui.pushbuffer()
445 446 commands.diff(ui, repo, **opts)
446 447 ui.popbuffer()
447 448 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
448 449 timer(d, title)
449 450 fm.end()
450 451
451 452 @command('perfrevlog',
452 453 [('d', 'dist', 100, 'distance between the revisions')] + formatteropts,
453 454 "[INDEXFILE]")
454 455 def perfrevlog(ui, repo, file_, **opts):
455 456 timer, fm = gettimer(ui, opts)
456 457 from mercurial import revlog
457 458 dist = opts['dist']
458 459 def d():
459 460 r = revlog.revlog(lambda fn: open(fn, 'rb'), file_)
460 461 for x in xrange(0, len(r), dist):
461 462 r.revision(r.node(x))
462 463
463 464 timer(d)
464 465 fm.end()
465 466
466 467 @command('perfrevset',
467 468 [('C', 'clear', False, 'clear volatile cache between each call.'),
468 469 ('', 'contexts', False, 'obtain changectx for each revision')]
469 470 + formatteropts, "REVSET")
470 471 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
471 472 """benchmark the execution time of a revset
472 473
473 474 Use the --clean option if need to evaluate the impact of build volatile
474 475 revisions set cache on the revset execution. Volatile cache hold filtered
475 476 and obsolete related cache."""
476 477 timer, fm = gettimer(ui, opts)
477 478 def d():
478 479 if clear:
479 480 repo.invalidatevolatilesets()
480 481 if contexts:
481 482 for ctx in repo.set(expr): pass
482 483 else:
483 484 for r in repo.revs(expr): pass
484 485 timer(d)
485 486 fm.end()
486 487
487 488 @command('perfvolatilesets', formatteropts)
488 489 def perfvolatilesets(ui, repo, *names, **opts):
489 490 """benchmark the computation of various volatile set
490 491
491 492 Volatile set computes element related to filtering and obsolescence."""
492 493 timer, fm = gettimer(ui, opts)
493 494 repo = repo.unfiltered()
494 495
495 496 def getobs(name):
496 497 def d():
497 498 repo.invalidatevolatilesets()
498 499 obsolete.getrevs(repo, name)
499 500 return d
500 501
501 502 allobs = sorted(obsolete.cachefuncs)
502 503 if names:
503 504 allobs = [n for n in allobs if n in names]
504 505
505 506 for name in allobs:
506 507 timer(getobs(name), title=name)
507 508
508 509 def getfiltered(name):
509 510 def d():
510 511 repo.invalidatevolatilesets()
511 512 repoview.filterrevs(repo, name)
512 513 return d
513 514
514 515 allfilter = sorted(repoview.filtertable)
515 516 if names:
516 517 allfilter = [n for n in allfilter if n in names]
517 518
518 519 for name in allfilter:
519 520 timer(getfiltered(name), title=name)
520 521 fm.end()
521 522
522 523 @command('perfbranchmap',
523 524 [('f', 'full', False,
524 525 'Includes build time of subset'),
525 526 ] + formatteropts)
526 527 def perfbranchmap(ui, repo, full=False, **opts):
527 528 """benchmark the update of a branchmap
528 529
529 530 This benchmarks the full repo.branchmap() call with read and write disabled
530 531 """
531 532 timer, fm = gettimer(ui, opts)
532 533 def getbranchmap(filtername):
533 534 """generate a benchmark function for the filtername"""
534 535 if filtername is None:
535 536 view = repo
536 537 else:
537 538 view = repo.filtered(filtername)
538 539 def d():
539 540 if full:
540 541 view._branchcaches.clear()
541 542 else:
542 543 view._branchcaches.pop(filtername, None)
543 544 view.branchmap()
544 545 return d
545 546 # add filter in smaller subset to bigger subset
546 547 possiblefilters = set(repoview.filtertable)
547 548 allfilters = []
548 549 while possiblefilters:
549 550 for name in possiblefilters:
550 551 subset = branchmap.subsettable.get(name)
551 552 if subset not in possiblefilters:
552 553 break
553 554 else:
554 555 assert False, 'subset cycle %s!' % possiblefilters
555 556 allfilters.append(name)
556 557 possiblefilters.remove(name)
557 558
558 559 # warm the cache
559 560 if not full:
560 561 for name in allfilters:
561 562 repo.filtered(name).branchmap()
562 563 # add unfiltered
563 564 allfilters.append(None)
564 565 oldread = branchmap.read
565 566 oldwrite = branchmap.branchcache.write
566 567 try:
567 568 branchmap.read = lambda repo: None
568 569 branchmap.write = lambda repo: None
569 570 for name in allfilters:
570 571 timer(getbranchmap(name), title=str(name))
571 572 finally:
572 573 branchmap.read = oldread
573 574 branchmap.branchcache.write = oldwrite
574 575 fm.end()
575 576
576 577 @command('perfloadmarkers')
577 578 def perfloadmarkers(ui, repo):
578 579 """benchmark the time to parse the on-disk markers for a repo
579 580
580 581 Result is the number of markers in the repo."""
581 582 timer, fm = gettimer(ui)
582 583 timer(lambda: len(obsolete.obsstore(repo.svfs)))
583 584 fm.end()
General Comments 0
You need to be logged in to leave comments. Login now