##// END OF EJS Templates
revsetbenchmarks: use a more compact output format with a header...
Pierre-Yves David -
r25534:43e5a681 default
parent child Browse files
Show More
@@ -1,203 +1,216 b''
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 95 def idxwidth(nbidx):
96 96 """return the max width of number used for index
97 97
98 98 This is similar to log10(nbidx), but we use custom code here
99 99 because we start with zero and we'd rather not deal with all the
100 100 extra rounding business that log10 would imply.
101 101 """
102 102 nbidx -= 1 # starts at 0
103 103 idxwidth = 0
104 104 while nbidx:
105 105 idxwidth += 1
106 106 nbidx //= 10
107 107 if not idxwidth:
108 108 idxwidth = 1
109 109 return idxwidth
110 110
111 111 def printresult(idx, data, maxidx):
112 112 """print a line of result to stdout"""
113 113 mask = '%%0%ii) %%s' % idxwidth(maxidx)
114 out = ['%10.6f' % data['wall'],
115 '%10.6f' % data['comb'],
116 '%10.6f' % data['user'],
117 '%10.6f' % data['sys'],
118 '%6d' % data['count'],
119 ]
120 print mask % (idx, ' '.join(out))
114 121
115 out = ("wall %f comb %f user %f sys %f (best of %d)"
116 % (data['wall'], data['comb'], data['user'],
117 data['sys'], data['count']))
118
119 print mask % (idx, out)
122 def printheader(maxidx):
123 header = [' ' * (idxwidth(maxidx) + 1),
124 ' %-8s' % 'wall',
125 ' %-8s' % 'comb',
126 ' %-8s' % 'user',
127 ' %-8s' % 'sys',
128 '%6s' % 'count',
129 ]
130 print ' '.join(header)
120 131
121 132 def getrevs(spec):
122 133 """get the list of rev matched by a revset"""
123 134 try:
124 135 out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec])
125 136 except CalledProcessError, exc:
126 137 print >> sys.stderr, "abort, can't get revision from %s" % spec
127 138 sys.exit(exc.returncode)
128 139 return [r for r in out.split() if r]
129 140
130 141
131 142 parser = OptionParser(usage="usage: %prog [options] <revs>")
132 143 parser.add_option("-f", "--file",
133 144 help="read revset from FILE (stdin if omitted)",
134 145 metavar="FILE")
135 146 parser.add_option("-R", "--repo",
136 147 help="run benchmark on REPO", metavar="REPO")
137 148
138 149 (options, args) = parser.parse_args()
139 150
140 151 if len(sys.argv) < 2:
141 152 parser.print_help()
142 153 sys.exit(255)
143 154
144 155 # the directory where both this script and the perf.py extension live.
145 156 contribdir = os.path.dirname(__file__)
146 157
147 158 target_rev = args[0]
148 159
149 160 revsetsfile = sys.stdin
150 161 if options.file:
151 162 revsetsfile = open(options.file)
152 163
153 164 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')]
154 165
155 166 print "Revsets to benchmark"
156 167 print "----------------------------"
157 168
158 169 for idx, rset in enumerate(revsets):
159 170 print "%i) %s" % (idx, rset)
160 171
161 172 print "----------------------------"
162 173 print
163 174
164 175
165 176 revs = getrevs(target_rev)
166 177
167 178 results = []
168 179 for r in revs:
169 180 print "----------------------------"
170 181 printrevision(r)
171 182 print "----------------------------"
172 183 update(r)
173 184 res = []
174 185 results.append(res)
186 printheader(len(revsets))
175 187 for idx, rset in enumerate(revsets):
176 188 data = perf(rset, target=options.repo)
177 189 res.append(data)
178 190 printresult(idx, data, len(revsets))
179 191 sys.stdout.flush()
180 192 print "----------------------------"
181 193
182 194
183 195 print """
184 196
185 197 Result by revset
186 198 ================
187 199 """
188 200
189 201 print 'Revision:', revs
190 202 for idx, rev in enumerate(revs):
191 203 sys.stdout.write('%i) ' % idx)
192 204 sys.stdout.flush()
193 205 printrevision(rev)
194 206
195 207 print
196 208 print
197 209
198 210 for ridx, rset in enumerate(revsets):
199 211
200 212 print "revset #%i: %s" % (ridx, rset)
213 printheader(len(results))
201 214 for idx, data in enumerate(results):
202 215 printresult(idx, data[ridx], len(results))
203 216 print
General Comments 0
You need to be logged in to leave comments. Login now