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