##// END OF EJS Templates
contrib/perf: perfparents handle tiny repos...
timeless -
r27099:e94bfebb default
parent child Browse files
Show More
@@ -1,581 +1,583
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 from mercurial import repoview, branchmap, merge, copies
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 if len(repo.changelog) < 1000:
304 raise error.Abort("repo needs 1000 commits for this test")
303 305 nl = [repo.changelog.node(i) for i in xrange(1000)]
304 306 def d():
305 307 for n in nl:
306 308 repo.changelog.parents(n)
307 309 timer(d)
308 310 fm.end()
309 311
310 312 @command('perfctxfiles', formatteropts)
311 313 def perfctxfiles(ui, repo, x, **opts):
312 314 x = int(x)
313 315 timer, fm = gettimer(ui, opts)
314 316 def d():
315 317 len(repo[x].files())
316 318 timer(d)
317 319 fm.end()
318 320
319 321 @command('perfrawfiles', formatteropts)
320 322 def perfrawfiles(ui, repo, x, **opts):
321 323 x = int(x)
322 324 timer, fm = gettimer(ui, opts)
323 325 cl = repo.changelog
324 326 def d():
325 327 len(cl.read(x)[3])
326 328 timer(d)
327 329 fm.end()
328 330
329 331 @command('perflookup', formatteropts)
330 332 def perflookup(ui, repo, rev, **opts):
331 333 timer, fm = gettimer(ui, opts)
332 334 timer(lambda: len(repo.lookup(rev)))
333 335 fm.end()
334 336
335 337 @command('perfrevrange', formatteropts)
336 338 def perfrevrange(ui, repo, *specs, **opts):
337 339 timer, fm = gettimer(ui, opts)
338 340 revrange = scmutil.revrange
339 341 timer(lambda: len(revrange(repo, specs)))
340 342 fm.end()
341 343
342 344 @command('perfnodelookup', formatteropts)
343 345 def perfnodelookup(ui, repo, rev, **opts):
344 346 timer, fm = gettimer(ui, opts)
345 347 import mercurial.revlog
346 348 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
347 349 n = repo[rev].node()
348 350 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
349 351 def d():
350 352 cl.rev(n)
351 353 clearcaches(cl)
352 354 timer(d)
353 355 fm.end()
354 356
355 357 @command('perflog',
356 358 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
357 359 def perflog(ui, repo, **opts):
358 360 timer, fm = gettimer(ui, opts)
359 361 ui.pushbuffer()
360 362 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
361 363 copies=opts.get('rename')))
362 364 ui.popbuffer()
363 365 fm.end()
364 366
365 367 @command('perfmoonwalk', formatteropts)
366 368 def perfmoonwalk(ui, repo, **opts):
367 369 """benchmark walking the changelog backwards
368 370
369 371 This also loads the changelog data for each revision in the changelog.
370 372 """
371 373 timer, fm = gettimer(ui, opts)
372 374 def moonwalk():
373 375 for i in xrange(len(repo), -1, -1):
374 376 ctx = repo[i]
375 377 ctx.branch() # read changelog data (in addition to the index)
376 378 timer(moonwalk)
377 379 fm.end()
378 380
379 381 @command('perftemplating', formatteropts)
380 382 def perftemplating(ui, repo, **opts):
381 383 timer, fm = gettimer(ui, opts)
382 384 ui.pushbuffer()
383 385 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
384 386 template='{date|shortdate} [{rev}:{node|short}]'
385 387 ' {author|person}: {desc|firstline}\n'))
386 388 ui.popbuffer()
387 389 fm.end()
388 390
389 391 @command('perfcca', formatteropts)
390 392 def perfcca(ui, repo, **opts):
391 393 timer, fm = gettimer(ui, opts)
392 394 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
393 395 fm.end()
394 396
395 397 @command('perffncacheload', formatteropts)
396 398 def perffncacheload(ui, repo, **opts):
397 399 timer, fm = gettimer(ui, opts)
398 400 s = repo.store
399 401 def d():
400 402 s.fncache._load()
401 403 timer(d)
402 404 fm.end()
403 405
404 406 @command('perffncachewrite', formatteropts)
405 407 def perffncachewrite(ui, repo, **opts):
406 408 timer, fm = gettimer(ui, opts)
407 409 s = repo.store
408 410 s.fncache._load()
409 411 lock = repo.lock()
410 412 tr = repo.transaction('perffncachewrite')
411 413 def d():
412 414 s.fncache._dirty = True
413 415 s.fncache.write(tr)
414 416 timer(d)
415 417 lock.release()
416 418 fm.end()
417 419
418 420 @command('perffncacheencode', formatteropts)
419 421 def perffncacheencode(ui, repo, **opts):
420 422 timer, fm = gettimer(ui, opts)
421 423 s = repo.store
422 424 s.fncache._load()
423 425 def d():
424 426 for p in s.fncache.entries:
425 427 s.encode(p)
426 428 timer(d)
427 429 fm.end()
428 430
429 431 @command('perfdiffwd', formatteropts)
430 432 def perfdiffwd(ui, repo, **opts):
431 433 """Profile diff of working directory changes"""
432 434 timer, fm = gettimer(ui, opts)
433 435 options = {
434 436 'w': 'ignore_all_space',
435 437 'b': 'ignore_space_change',
436 438 'B': 'ignore_blank_lines',
437 439 }
438 440
439 441 for diffopt in ('', 'w', 'b', 'B', 'wB'):
440 442 opts = dict((options[c], '1') for c in diffopt)
441 443 def d():
442 444 ui.pushbuffer()
443 445 commands.diff(ui, repo, **opts)
444 446 ui.popbuffer()
445 447 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
446 448 timer(d, title)
447 449 fm.end()
448 450
449 451 @command('perfrevlog',
450 452 [('d', 'dist', 100, 'distance between the revisions')] + formatteropts,
451 453 "[INDEXFILE]")
452 454 def perfrevlog(ui, repo, file_, **opts):
453 455 timer, fm = gettimer(ui, opts)
454 456 from mercurial import revlog
455 457 dist = opts['dist']
456 458 def d():
457 459 r = revlog.revlog(lambda fn: open(fn, 'rb'), file_)
458 460 for x in xrange(0, len(r), dist):
459 461 r.revision(r.node(x))
460 462
461 463 timer(d)
462 464 fm.end()
463 465
464 466 @command('perfrevset',
465 467 [('C', 'clear', False, 'clear volatile cache between each call.'),
466 468 ('', 'contexts', False, 'obtain changectx for each revision')]
467 469 + formatteropts, "REVSET")
468 470 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
469 471 """benchmark the execution time of a revset
470 472
471 473 Use the --clean option if need to evaluate the impact of build volatile
472 474 revisions set cache on the revset execution. Volatile cache hold filtered
473 475 and obsolete related cache."""
474 476 timer, fm = gettimer(ui, opts)
475 477 def d():
476 478 if clear:
477 479 repo.invalidatevolatilesets()
478 480 if contexts:
479 481 for ctx in repo.set(expr): pass
480 482 else:
481 483 for r in repo.revs(expr): pass
482 484 timer(d)
483 485 fm.end()
484 486
485 487 @command('perfvolatilesets', formatteropts)
486 488 def perfvolatilesets(ui, repo, *names, **opts):
487 489 """benchmark the computation of various volatile set
488 490
489 491 Volatile set computes element related to filtering and obsolescence."""
490 492 timer, fm = gettimer(ui, opts)
491 493 repo = repo.unfiltered()
492 494
493 495 def getobs(name):
494 496 def d():
495 497 repo.invalidatevolatilesets()
496 498 obsolete.getrevs(repo, name)
497 499 return d
498 500
499 501 allobs = sorted(obsolete.cachefuncs)
500 502 if names:
501 503 allobs = [n for n in allobs if n in names]
502 504
503 505 for name in allobs:
504 506 timer(getobs(name), title=name)
505 507
506 508 def getfiltered(name):
507 509 def d():
508 510 repo.invalidatevolatilesets()
509 511 repoview.filterrevs(repo, name)
510 512 return d
511 513
512 514 allfilter = sorted(repoview.filtertable)
513 515 if names:
514 516 allfilter = [n for n in allfilter if n in names]
515 517
516 518 for name in allfilter:
517 519 timer(getfiltered(name), title=name)
518 520 fm.end()
519 521
520 522 @command('perfbranchmap',
521 523 [('f', 'full', False,
522 524 'Includes build time of subset'),
523 525 ] + formatteropts)
524 526 def perfbranchmap(ui, repo, full=False, **opts):
525 527 """benchmark the update of a branchmap
526 528
527 529 This benchmarks the full repo.branchmap() call with read and write disabled
528 530 """
529 531 timer, fm = gettimer(ui, opts)
530 532 def getbranchmap(filtername):
531 533 """generate a benchmark function for the filtername"""
532 534 if filtername is None:
533 535 view = repo
534 536 else:
535 537 view = repo.filtered(filtername)
536 538 def d():
537 539 if full:
538 540 view._branchcaches.clear()
539 541 else:
540 542 view._branchcaches.pop(filtername, None)
541 543 view.branchmap()
542 544 return d
543 545 # add filter in smaller subset to bigger subset
544 546 possiblefilters = set(repoview.filtertable)
545 547 allfilters = []
546 548 while possiblefilters:
547 549 for name in possiblefilters:
548 550 subset = branchmap.subsettable.get(name)
549 551 if subset not in possiblefilters:
550 552 break
551 553 else:
552 554 assert False, 'subset cycle %s!' % possiblefilters
553 555 allfilters.append(name)
554 556 possiblefilters.remove(name)
555 557
556 558 # warm the cache
557 559 if not full:
558 560 for name in allfilters:
559 561 repo.filtered(name).branchmap()
560 562 # add unfiltered
561 563 allfilters.append(None)
562 564 oldread = branchmap.read
563 565 oldwrite = branchmap.branchcache.write
564 566 try:
565 567 branchmap.read = lambda repo: None
566 568 branchmap.write = lambda repo: None
567 569 for name in allfilters:
568 570 timer(getbranchmap(name), title=str(name))
569 571 finally:
570 572 branchmap.read = oldread
571 573 branchmap.branchcache.write = oldwrite
572 574 fm.end()
573 575
574 576 @command('perfloadmarkers')
575 577 def perfloadmarkers(ui, repo):
576 578 """benchmark the time to parse the on-disk markers for a repo
577 579
578 580 Result is the number of markers in the repo."""
579 581 timer, fm = gettimer(ui)
580 582 timer(lambda: len(obsolete.obsstore(repo.svfs)))
581 583 fm.end()
General Comments 0
You need to be logged in to leave comments. Login now