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