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