##// END OF EJS Templates
revsetbenchmarks: factor out result output into a function...
Pierre-Yves David -
r25531:371d8afc default
parent child Browse files
Show More
@@ -1,184 +1,187
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 formatresult(data):
95 def printresult(idx, data, maxidx):
96 """format the data dict into a line of text for humans"""
96 """print a line of result to stdout"""
97 return ("wall %f comb %f user %f sys %f (best of %d)"
97 mask = '%i) %s'
98 % (data['wall'], data['comb'], data['user'],
99 data['sys'], data['count']))
100
98
99 out = ("wall %f comb %f user %f sys %f (best of %d)"
100 % (data['wall'], data['comb'], data['user'],
101 data['sys'], data['count']))
102
103 print mask % (idx, out)
101
104
102 def getrevs(spec):
105 def getrevs(spec):
103 """get the list of rev matched by a revset"""
106 """get the list of rev matched by a revset"""
104 try:
107 try:
105 out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec])
108 out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec])
106 except CalledProcessError, exc:
109 except CalledProcessError, exc:
107 print >> sys.stderr, "abort, can't get revision from %s" % spec
110 print >> sys.stderr, "abort, can't get revision from %s" % spec
108 sys.exit(exc.returncode)
111 sys.exit(exc.returncode)
109 return [r for r in out.split() if r]
112 return [r for r in out.split() if r]
110
113
111
114
112 parser = OptionParser(usage="usage: %prog [options] <revs>")
115 parser = OptionParser(usage="usage: %prog [options] <revs>")
113 parser.add_option("-f", "--file",
116 parser.add_option("-f", "--file",
114 help="read revset from FILE (stdin if omitted)",
117 help="read revset from FILE (stdin if omitted)",
115 metavar="FILE")
118 metavar="FILE")
116 parser.add_option("-R", "--repo",
119 parser.add_option("-R", "--repo",
117 help="run benchmark on REPO", metavar="REPO")
120 help="run benchmark on REPO", metavar="REPO")
118
121
119 (options, args) = parser.parse_args()
122 (options, args) = parser.parse_args()
120
123
121 if len(sys.argv) < 2:
124 if len(sys.argv) < 2:
122 parser.print_help()
125 parser.print_help()
123 sys.exit(255)
126 sys.exit(255)
124
127
125 # the directory where both this script and the perf.py extension live.
128 # the directory where both this script and the perf.py extension live.
126 contribdir = os.path.dirname(__file__)
129 contribdir = os.path.dirname(__file__)
127
130
128 target_rev = args[0]
131 target_rev = args[0]
129
132
130 revsetsfile = sys.stdin
133 revsetsfile = sys.stdin
131 if options.file:
134 if options.file:
132 revsetsfile = open(options.file)
135 revsetsfile = open(options.file)
133
136
134 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')]
137 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')]
135
138
136 print "Revsets to benchmark"
139 print "Revsets to benchmark"
137 print "----------------------------"
140 print "----------------------------"
138
141
139 for idx, rset in enumerate(revsets):
142 for idx, rset in enumerate(revsets):
140 print "%i) %s" % (idx, rset)
143 print "%i) %s" % (idx, rset)
141
144
142 print "----------------------------"
145 print "----------------------------"
143 print
146 print
144
147
145
148
146 revs = getrevs(target_rev)
149 revs = getrevs(target_rev)
147
150
148 results = []
151 results = []
149 for r in revs:
152 for r in revs:
150 print "----------------------------"
153 print "----------------------------"
151 printrevision(r)
154 printrevision(r)
152 print "----------------------------"
155 print "----------------------------"
153 update(r)
156 update(r)
154 res = []
157 res = []
155 results.append(res)
158 results.append(res)
156 for idx, rset in enumerate(revsets):
159 for idx, rset in enumerate(revsets):
157 data = perf(rset, target=options.repo)
160 data = perf(rset, target=options.repo)
158 res.append(data)
161 res.append(data)
159 print "%i)" % idx, formatresult(data)
162 printresult(idx, data, len(revsets))
160 sys.stdout.flush()
163 sys.stdout.flush()
161 print "----------------------------"
164 print "----------------------------"
162
165
163
166
164 print """
167 print """
165
168
166 Result by revset
169 Result by revset
167 ================
170 ================
168 """
171 """
169
172
170 print 'Revision:', revs
173 print 'Revision:', revs
171 for idx, rev in enumerate(revs):
174 for idx, rev in enumerate(revs):
172 sys.stdout.write('%i) ' % idx)
175 sys.stdout.write('%i) ' % idx)
173 sys.stdout.flush()
176 sys.stdout.flush()
174 printrevision(rev)
177 printrevision(rev)
175
178
176 print
179 print
177 print
180 print
178
181
179 for ridx, rset in enumerate(revsets):
182 for ridx, rset in enumerate(revsets):
180
183
181 print "revset #%i: %s" % (ridx, rset)
184 print "revset #%i: %s" % (ridx, rset)
182 for idx, data in enumerate(results):
185 for idx, data in enumerate(results):
183 print '%i) %s' % (idx, formatresult(data[ridx]))
186 printresult(idx, data[ridx], len(results))
184 print
187 print
General Comments 0
You need to be logged in to leave comments. Login now