##// END OF EJS Templates
revsetbenchmarks: ensure all indexes have the same width...
Pierre-Yves David -
r25532:1311aee8 default
parent child Browse files
Show More
@@ -1,187 +1,200
1 1 #!/usr/bin/env python
2 2
3 3 # Measure the performance of a list of revsets against multiple revisions
4 4 # defined by parameter. Checkout one by one and run perfrevset with every
5 5 # revset in the list to benchmark its performance.
6 6 #
7 7 # - First argument is a revset of mercurial own repo to runs against.
8 8 # - Second argument is the file from which the revset array will be taken
9 9 # If second argument is omitted read it from standard input
10 10 #
11 11 # You should run this from the root of your mercurial repository.
12 12 #
13 13 # This script also does one run of the current version of mercurial installed
14 14 # to compare performance.
15 15
16 16 import sys
17 17 import os
18 18 import re
19 19 from subprocess import check_call, Popen, CalledProcessError, STDOUT, PIPE
20 20 # cannot use argparse, python 2.7 only
21 21 from optparse import OptionParser
22 22
23 23 def check_output(*args, **kwargs):
24 24 kwargs.setdefault('stderr', PIPE)
25 25 kwargs.setdefault('stdout', PIPE)
26 26 proc = Popen(*args, **kwargs)
27 27 output, error = proc.communicate()
28 28 if proc.returncode != 0:
29 29 raise CalledProcessError(proc.returncode, ' '.join(args[0]))
30 30 return output
31 31
32 32 def update(rev):
33 33 """update the repo to a revision"""
34 34 try:
35 35 check_call(['hg', 'update', '--quiet', '--check', str(rev)])
36 36 except CalledProcessError, exc:
37 37 print >> sys.stderr, 'update to revision %s failed, aborting' % rev
38 38 sys.exit(exc.returncode)
39 39
40 40
41 41 def hg(cmd, repo=None):
42 42 """run a mercurial command
43 43
44 44 <cmd> is the list of command + argument,
45 45 <repo> is an optional repository path to run this command in."""
46 46 fullcmd = ['./hg']
47 47 if repo is not None:
48 48 fullcmd += ['-R', repo]
49 49 fullcmd += ['--config',
50 50 'extensions.perf=' + os.path.join(contribdir, 'perf.py')]
51 51 fullcmd += cmd
52 52 return check_output(fullcmd, stderr=STDOUT)
53 53
54 54 def perf(revset, target=None):
55 55 """run benchmark for this very revset"""
56 56 try:
57 57 output = hg(['perfrevset', revset], repo=target)
58 58 return parseoutput(output)
59 59 except CalledProcessError, exc:
60 60 print >> sys.stderr, 'abort: cannot run revset benchmark: %s' % exc.cmd
61 61 if exc.output is None:
62 62 print >> sys.stderr, '(no ouput)'
63 63 else:
64 64 print >> sys.stderr, exc.output
65 65 sys.exit(exc.returncode)
66 66
67 67 outputre = re.compile(r'! wall (\d+.\d+) comb (\d+.\d+) user (\d+.\d+) '
68 68 'sys (\d+.\d+) \(best of (\d+)\)')
69 69
70 70 def parseoutput(output):
71 71 """parse a textual output into a dict
72 72
73 73 We cannot just use json because we want to compare with old
74 74 versions of Mercurial that may not support json output.
75 75 """
76 76 match = outputre.search(output)
77 77 if not match:
78 78 print >> sys.stderr, 'abort: invalid output:'
79 79 print >> sys.stderr, output
80 80 sys.exit(1)
81 81 return {'comb': float(match.group(2)),
82 82 'count': int(match.group(5)),
83 83 'sys': float(match.group(3)),
84 84 'user': float(match.group(4)),
85 85 'wall': float(match.group(1)),
86 86 }
87 87
88 88 def printrevision(rev):
89 89 """print data about a revision"""
90 90 sys.stdout.write("Revision: ")
91 91 sys.stdout.flush()
92 92 check_call(['hg', 'log', '--rev', str(rev), '--template',
93 93 '{desc|firstline}\n'])
94 94
95 def idxwidth(nbidx):
96 """return the max width of number used for index
97
98 Yes, this is basically a log10."""
99 nbidx -= 1 # starts at 0
100 idxwidth = 0
101 while nbidx:
102 idxwidth += 1
103 nbidx //= 10
104 if not idxwidth:
105 idxwidth = 1
106 return idxwidth
107
95 108 def printresult(idx, data, maxidx):
96 109 """print a line of result to stdout"""
97 mask = '%i) %s'
110 mask = '%%0%ii) %%s' % idxwidth(maxidx)
98 111
99 112 out = ("wall %f comb %f user %f sys %f (best of %d)"
100 113 % (data['wall'], data['comb'], data['user'],
101 114 data['sys'], data['count']))
102 115
103 116 print mask % (idx, out)
104 117
105 118 def getrevs(spec):
106 119 """get the list of rev matched by a revset"""
107 120 try:
108 121 out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec])
109 122 except CalledProcessError, exc:
110 123 print >> sys.stderr, "abort, can't get revision from %s" % spec
111 124 sys.exit(exc.returncode)
112 125 return [r for r in out.split() if r]
113 126
114 127
115 128 parser = OptionParser(usage="usage: %prog [options] <revs>")
116 129 parser.add_option("-f", "--file",
117 130 help="read revset from FILE (stdin if omitted)",
118 131 metavar="FILE")
119 132 parser.add_option("-R", "--repo",
120 133 help="run benchmark on REPO", metavar="REPO")
121 134
122 135 (options, args) = parser.parse_args()
123 136
124 137 if len(sys.argv) < 2:
125 138 parser.print_help()
126 139 sys.exit(255)
127 140
128 141 # the directory where both this script and the perf.py extension live.
129 142 contribdir = os.path.dirname(__file__)
130 143
131 144 target_rev = args[0]
132 145
133 146 revsetsfile = sys.stdin
134 147 if options.file:
135 148 revsetsfile = open(options.file)
136 149
137 150 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')]
138 151
139 152 print "Revsets to benchmark"
140 153 print "----------------------------"
141 154
142 155 for idx, rset in enumerate(revsets):
143 156 print "%i) %s" % (idx, rset)
144 157
145 158 print "----------------------------"
146 159 print
147 160
148 161
149 162 revs = getrevs(target_rev)
150 163
151 164 results = []
152 165 for r in revs:
153 166 print "----------------------------"
154 167 printrevision(r)
155 168 print "----------------------------"
156 169 update(r)
157 170 res = []
158 171 results.append(res)
159 172 for idx, rset in enumerate(revsets):
160 173 data = perf(rset, target=options.repo)
161 174 res.append(data)
162 175 printresult(idx, data, len(revsets))
163 176 sys.stdout.flush()
164 177 print "----------------------------"
165 178
166 179
167 180 print """
168 181
169 182 Result by revset
170 183 ================
171 184 """
172 185
173 186 print 'Revision:', revs
174 187 for idx, rev in enumerate(revs):
175 188 sys.stdout.write('%i) ' % idx)
176 189 sys.stdout.flush()
177 190 printrevision(rev)
178 191
179 192 print
180 193 print
181 194
182 195 for ridx, rset in enumerate(revsets):
183 196
184 197 print "revset #%i: %s" % (ridx, rset)
185 198 for idx, data in enumerate(results):
186 199 printresult(idx, data[ridx], len(results))
187 200 print
General Comments 0
You need to be logged in to leave comments. Login now