comparison contrib/perf.py @ 35109:e96613048bdd

perf: add command to benchmark bundle reading Upcoming commits will be refactoring bundle2 I/O code. This commit establishes a `hg perfbundleread` command that measures how long it takes to read a bundle using various mechanisms. As a baseline, here's output from an uncompressed bundle1 bundle of my Firefox repo (7,098,622,890 bytes): ! read(8k) ! wall 0.763481 comb 0.760000 user 0.160000 sys 0.600000 (best of 6) ! read(16k) ! wall 0.644512 comb 0.640000 user 0.110000 sys 0.530000 (best of 16) ! read(32k) ! wall 0.581172 comb 0.590000 user 0.060000 sys 0.530000 (best of 18) ! read(128k) ! wall 0.535183 comb 0.530000 user 0.010000 sys 0.520000 (best of 19) ! cg1 deltaiter() ! wall 0.873500 comb 0.880000 user 0.840000 sys 0.040000 (best of 12) ! cg1 getchunks() ! wall 6.283797 comb 6.270000 user 5.570000 sys 0.700000 (best of 3) ! cg1 read(8k) ! wall 1.097173 comb 1.100000 user 0.400000 sys 0.700000 (best of 10) ! cg1 read(16k) ! wall 0.810750 comb 0.800000 user 0.200000 sys 0.600000 (best of 13) ! cg1 read(32k) ! wall 0.671215 comb 0.670000 user 0.110000 sys 0.560000 (best of 15) ! cg1 read(128k) ! wall 0.597857 comb 0.600000 user 0.020000 sys 0.580000 (best of 15) And from an uncompressed bundle2 bundle (6,070,036,163 bytes): ! read(8k) ! wall 0.676997 comb 0.680000 user 0.160000 sys 0.520000 (best of 15) ! read(16k) ! wall 0.592706 comb 0.590000 user 0.080000 sys 0.510000 (best of 17) ! read(32k) ! wall 0.529395 comb 0.530000 user 0.050000 sys 0.480000 (best of 16) ! read(128k) ! wall 0.491270 comb 0.490000 user 0.010000 sys 0.480000 (best of 19) ! bundle2 forwardchunks() ! wall 2.997131 comb 2.990000 user 2.270000 sys 0.720000 (best of 4) ! bundle2 iterparts() ! wall 12.247197 comb 10.670000 user 8.170000 sys 2.500000 (best of 3) ! bundle2 part seek() ! wall 11.761675 comb 10.500000 user 8.240000 sys 2.260000 (best of 3) ! bundle2 part read(8k) ! wall 9.116163 comb 9.110000 user 8.240000 sys 0.870000 (best of 3) ! bundle2 part read(16k) ! wall 8.984362 comb 8.970000 user 8.110000 sys 0.860000 (best of 3) ! bundle2 part read(32k) ! wall 8.758364 comb 8.740000 user 7.860000 sys 0.880000 (best of 3) ! bundle2 part read(128k) ! wall 8.749040 comb 8.730000 user 7.830000 sys 0.900000 (best of 3) We already see some interesting data. Notably that bundle2 has significant overhead compared to bundle1. This matters for e.g. stream clone bundles, which can be applied at >1Gbps. Differential Revision: https://phab.mercurial-scm.org/D1385
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 13 Nov 2017 19:20:34 -0800
parents 61888bd0b300
children da91e7309daf
comparison
equal deleted inserted replaced
35108:8b1c887d52e7 35109:e96613048bdd
486 clearfilecache(repo, '_bookmarks') 486 clearfilecache(repo, '_bookmarks')
487 repo._bookmarks 487 repo._bookmarks
488 timer(d) 488 timer(d)
489 fm.end() 489 fm.end()
490 490
491 @command('perfbundleread', formatteropts, 'BUNDLE')
492 def perfbundleread(ui, repo, bundlepath, **opts):
493 """Benchmark reading of bundle files.
494
495 This command is meant to isolate the I/O part of bundle reading as
496 much as possible.
497 """
498 from mercurial import (
499 bundle2,
500 exchange,
501 streamclone,
502 )
503
504 def makebench(fn):
505 def run():
506 with open(bundlepath, 'rb') as fh:
507 bundle = exchange.readbundle(ui, fh, bundlepath)
508 fn(bundle)
509
510 return run
511
512 def makereadnbytes(size):
513 def run():
514 with open(bundlepath, 'rb') as fh:
515 bundle = exchange.readbundle(ui, fh, bundlepath)
516 while bundle.read(size):
517 pass
518
519 return run
520
521 def makestdioread(size):
522 def run():
523 with open(bundlepath, 'rb') as fh:
524 while fh.read(size):
525 pass
526
527 return run
528
529 # bundle1
530
531 def deltaiter(bundle):
532 for delta in bundle.deltaiter():
533 pass
534
535 def iterchunks(bundle):
536 for chunk in bundle.getchunks():
537 pass
538
539 # bundle2
540
541 def forwardchunks(bundle):
542 for chunk in bundle._forwardchunks():
543 pass
544
545 def iterparts(bundle):
546 for part in bundle.iterparts():
547 pass
548
549 def seek(bundle):
550 for part in bundle.iterparts():
551 part.seek(0, os.SEEK_END)
552
553 def makepartreadnbytes(size):
554 def run():
555 with open(bundlepath, 'rb') as fh:
556 bundle = exchange.readbundle(ui, fh, bundlepath)
557 for part in bundle.iterparts():
558 while part.read(size):
559 pass
560
561 return run
562
563 benches = [
564 (makestdioread(8192), 'read(8k)'),
565 (makestdioread(16384), 'read(16k)'),
566 (makestdioread(32768), 'read(32k)'),
567 (makestdioread(131072), 'read(128k)'),
568 ]
569
570 with open(bundlepath, 'rb') as fh:
571 bundle = exchange.readbundle(ui, fh, bundlepath)
572
573 if isinstance(bundle, changegroup.cg1unpacker):
574 benches.extend([
575 (makebench(deltaiter), 'cg1 deltaiter()'),
576 (makebench(iterchunks), 'cg1 getchunks()'),
577 (makereadnbytes(8192), 'cg1 read(8k)'),
578 (makereadnbytes(16384), 'cg1 read(16k)'),
579 (makereadnbytes(32768), 'cg1 read(32k)'),
580 (makereadnbytes(131072), 'cg1 read(128k)'),
581 ])
582 elif isinstance(bundle, bundle2.unbundle20):
583 benches.extend([
584 (makebench(forwardchunks), 'bundle2 forwardchunks()'),
585 (makebench(iterparts), 'bundle2 iterparts()'),
586 (makebench(seek), 'bundle2 part seek()'),
587 (makepartreadnbytes(8192), 'bundle2 part read(8k)'),
588 (makepartreadnbytes(16384), 'bundle2 part read(16k)'),
589 (makepartreadnbytes(32768), 'bundle2 part read(32k)'),
590 (makepartreadnbytes(131072), 'bundle2 part read(128k)'),
591 ])
592 elif isinstance(bundle, streamclone.streamcloneapplier):
593 raise error.Abort('stream clone bundles not supported')
594 else:
595 raise error.Abort('unhandled bundle type: %s' % type(bundle))
596
597 for fn, title in benches:
598 timer, fm = gettimer(ui, opts)
599 timer(fn, title=title)
600 fm.end()
601
491 @command('perfchangegroupchangelog', formatteropts + 602 @command('perfchangegroupchangelog', formatteropts +
492 [('', 'version', '02', 'changegroup version'), 603 [('', 'version', '02', 'changegroup version'),
493 ('r', 'rev', '', 'revisions to add to changegroup')]) 604 ('r', 'rev', '', 'revisions to add to changegroup')])
494 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts): 605 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
495 """Benchmark producing a changelog group for a changegroup. 606 """Benchmark producing a changelog group for a changegroup.