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 |
10 |
11 import sys |
11 from __future__ import absolute_import, print_function |
|
12 import math |
12 import os |
13 import os |
13 import re |
14 import re |
14 import math |
15 import sys |
15 from subprocess import check_call, Popen, CalledProcessError, STDOUT, PIPE |
16 from subprocess import ( |
|
17 CalledProcessError, |
|
18 check_call, |
|
19 PIPE, |
|
20 Popen, |
|
21 STDOUT, |
|
22 ) |
16 # cannot use argparse, python 2.7 only |
23 # cannot use argparse, python 2.7 only |
17 from optparse import OptionParser |
24 from optparse import ( |
|
25 OptionParser, |
|
26 ) |
18 |
27 |
19 DEFAULTVARIANTS = ['plain', 'min', 'max', 'first', 'last', |
28 DEFAULTVARIANTS = ['plain', 'min', 'max', 'first', 'last', |
20 'reverse', 'reverse+first', 'reverse+last', |
29 'reverse', 'reverse+first', 'reverse+last', |
21 'sort', 'sort+first', 'sort+last'] |
30 'sort', 'sort+first', 'sort+last'] |
22 |
31 |
34 try: |
43 try: |
35 check_call(['hg', 'update', '--quiet', '--check', str(rev)]) |
44 check_call(['hg', 'update', '--quiet', '--check', str(rev)]) |
36 check_output(['make', 'local'], |
45 check_output(['make', 'local'], |
37 stderr=None) # suppress output except for error/warning |
46 stderr=None) # suppress output except for error/warning |
38 except CalledProcessError as exc: |
47 except CalledProcessError as exc: |
39 print >> sys.stderr, 'update to revision %s failed, aborting' % rev |
48 print('update to revision %s failed, aborting'%rev, file=sys.stderr) |
40 sys.exit(exc.returncode) |
49 sys.exit(exc.returncode) |
41 |
50 |
42 |
51 |
43 def hg(cmd, repo=None): |
52 def hg(cmd, repo=None): |
44 """run a mercurial command |
53 """run a mercurial command |
60 if contexts: |
69 if contexts: |
61 args.append('--contexts') |
70 args.append('--contexts') |
62 output = hg(args, repo=target) |
71 output = hg(args, repo=target) |
63 return parseoutput(output) |
72 return parseoutput(output) |
64 except CalledProcessError as exc: |
73 except CalledProcessError as exc: |
65 print >> sys.stderr, 'abort: cannot run revset benchmark: %s' % exc.cmd |
74 print('abort: cannot run revset benchmark: %s'%exc.cmd, file=sys.stderr) |
66 if getattr(exc, 'output', None) is None: # no output before 2.7 |
75 if getattr(exc, 'output', None) is None: # no output before 2.7 |
67 print >> sys.stderr, '(no output)' |
76 print('(no output)', file=sys.stderr) |
68 else: |
77 else: |
69 print >> sys.stderr, exc.output |
78 print(exc.output, file=sys.stderr) |
70 return None |
79 return None |
71 |
80 |
72 outputre = re.compile(r'! wall (\d+.\d+) comb (\d+.\d+) user (\d+.\d+) ' |
81 outputre = re.compile(r'! wall (\d+.\d+) comb (\d+.\d+) user (\d+.\d+) ' |
73 'sys (\d+.\d+) \(best of (\d+)\)') |
82 'sys (\d+.\d+) \(best of (\d+)\)') |
74 |
83 |
78 We cannot just use json because we want to compare with old |
87 We cannot just use json because we want to compare with old |
79 versions of Mercurial that may not support json output. |
88 versions of Mercurial that may not support json output. |
80 """ |
89 """ |
81 match = outputre.search(output) |
90 match = outputre.search(output) |
82 if not match: |
91 if not match: |
83 print >> sys.stderr, 'abort: invalid output:' |
92 print('abort: invalid output:', file=sys.stderr) |
84 print >> sys.stderr, output |
93 print(output, file=sys.stderr) |
85 sys.exit(1) |
94 sys.exit(1) |
86 return {'comb': float(match.group(2)), |
95 return {'comb': float(match.group(2)), |
87 'count': int(match.group(5)), |
96 'count': int(match.group(5)), |
88 'sys': float(match.group(3)), |
97 'sys': float(match.group(3)), |
89 'user': float(match.group(4)), |
98 'user': float(match.group(4)), |
181 if verbose: |
190 if verbose: |
182 out.append(formattiming(data[var]['comb'])) |
191 out.append(formattiming(data[var]['comb'])) |
183 out.append(formattiming(data[var]['user'])) |
192 out.append(formattiming(data[var]['user'])) |
184 out.append(formattiming(data[var]['sys'])) |
193 out.append(formattiming(data[var]['sys'])) |
185 out.append('%6d' % data[var]['count']) |
194 out.append('%6d' % data[var]['count']) |
186 print mask % (idx, ' '.join(out)) |
195 print(mask % (idx, ' '.join(out))) |
187 |
196 |
188 def printheader(variants, maxidx, verbose=False, relative=False): |
197 def printheader(variants, maxidx, verbose=False, relative=False): |
189 header = [' ' * (idxwidth(maxidx) + 1)] |
198 header = [' ' * (idxwidth(maxidx) + 1)] |
190 for var in variants: |
199 for var in variants: |
191 if not var: |
200 if not var: |
198 if verbose: |
207 if verbose: |
199 header.append('%-8s' % 'comb') |
208 header.append('%-8s' % 'comb') |
200 header.append('%-8s' % 'user') |
209 header.append('%-8s' % 'user') |
201 header.append('%-8s' % 'sys') |
210 header.append('%-8s' % 'sys') |
202 header.append('%6s' % 'count') |
211 header.append('%6s' % 'count') |
203 print ' '.join(header) |
212 print(' '.join(header)) |
204 |
213 |
205 def getrevs(spec): |
214 def getrevs(spec): |
206 """get the list of rev matched by a revset""" |
215 """get the list of rev matched by a revset""" |
207 try: |
216 try: |
208 out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec]) |
217 out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec]) |
209 except CalledProcessError as exc: |
218 except CalledProcessError as exc: |
210 print >> sys.stderr, "abort, can't get revision from %s" % spec |
219 print("abort, can't get revision from %s"%spec, file=sys.stderr) |
211 sys.exit(exc.returncode) |
220 sys.exit(exc.returncode) |
212 return [r for r in out.split() if r] |
221 return [r for r in out.split() if r] |
213 |
222 |
214 |
223 |
215 def applyvariants(revset, variant): |
224 def applyvariants(revset, variant): |
259 revsetsfile = open(options.file) |
268 revsetsfile = open(options.file) |
260 |
269 |
261 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')] |
270 revsets = [l.strip() for l in revsetsfile if not l.startswith('#')] |
262 revsets = [l for l in revsets if l] |
271 revsets = [l for l in revsets if l] |
263 |
272 |
264 print "Revsets to benchmark" |
273 print("Revsets to benchmark") |
265 print "----------------------------" |
274 print("----------------------------") |
266 |
275 |
267 for idx, rset in enumerate(revsets): |
276 for idx, rset in enumerate(revsets): |
268 print "%i) %s" % (idx, rset) |
277 print("%i) %s" % (idx, rset)) |
269 |
278 |
270 print "----------------------------" |
279 print("----------------------------") |
271 print |
280 print() |
272 |
281 |
273 revs = [] |
282 revs = [] |
274 for a in args: |
283 for a in args: |
275 revs.extend(getrevs(a)) |
284 revs.extend(getrevs(a)) |
276 |
285 |
277 variants = options.variants.split(',') |
286 variants = options.variants.split(',') |
278 |
287 |
279 results = [] |
288 results = [] |
280 for r in revs: |
289 for r in revs: |
281 print "----------------------------" |
290 print("----------------------------") |
282 printrevision(r) |
291 printrevision(r) |
283 print "----------------------------" |
292 print("----------------------------") |
284 update(r) |
293 update(r) |
285 res = [] |
294 res = [] |
286 results.append(res) |
295 results.append(res) |
287 printheader(variants, len(revsets), verbose=options.verbose) |
296 printheader(variants, len(revsets), verbose=options.verbose) |
288 for idx, rset in enumerate(revsets): |
297 for idx, rset in enumerate(revsets): |
293 varres[var] = data |
302 varres[var] = data |
294 res.append(varres) |
303 res.append(varres) |
295 printresult(variants, idx, varres, len(revsets), |
304 printresult(variants, idx, varres, len(revsets), |
296 verbose=options.verbose) |
305 verbose=options.verbose) |
297 sys.stdout.flush() |
306 sys.stdout.flush() |
298 print "----------------------------" |
307 print("----------------------------") |
299 |
308 |
300 |
309 |
301 print """ |
310 print(""" |
302 |
311 |
303 Result by revset |
312 Result by revset |
304 ================ |
313 ================ |
305 """ |
314 """) |
306 |
315 |
307 print 'Revision:' |
316 print('Revision:') |
308 for idx, rev in enumerate(revs): |
317 for idx, rev in enumerate(revs): |
309 sys.stdout.write('%i) ' % idx) |
318 sys.stdout.write('%i) ' % idx) |
310 sys.stdout.flush() |
319 sys.stdout.flush() |
311 printrevision(rev) |
320 printrevision(rev) |
312 |
321 |
313 print |
322 print() |
314 print |
323 print() |
315 |
324 |
316 for ridx, rset in enumerate(revsets): |
325 for ridx, rset in enumerate(revsets): |
317 |
326 |
318 print "revset #%i: %s" % (ridx, rset) |
327 print("revset #%i: %s" % (ridx, rset)) |
319 printheader(variants, len(results), verbose=options.verbose, relative=True) |
328 printheader(variants, len(results), verbose=options.verbose, relative=True) |
320 ref = None |
329 ref = None |
321 for idx, data in enumerate(results): |
330 for idx, data in enumerate(results): |
322 printresult(variants, idx, data[ridx], len(results), |
331 printresult(variants, idx, data[ridx], len(results), |
323 verbose=options.verbose, reference=ref) |
332 verbose=options.verbose, reference=ref) |
324 ref = data[ridx] |
333 ref = data[ridx] |
325 print |
334 print() |