1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#!@PYTHON@
""" Timing comparisons for crm_simulate profiling output
"""
__copyright__ = "Copyright 2019-2023 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import io
import re
import sys
import errno
import argparse
import os
# These imports allow running from a source checkout after running `make`.
# Note that while this doesn't necessarily mean it will successfully run tests,
# but being able to see --help output can be useful.
if os.path.exists("@abs_top_srcdir@/python"):
sys.path.insert(0, "@abs_top_srcdir@/python")
if os.path.exists("@abs_top_builddir@/python") and "@abs_top_builddir@" != "@abs_top_srcdir@":
sys.path.insert(0, "@abs_top_builddir@/python")
from pacemaker.exitstatus import ExitStatus
DESC = """Compare timings from crm_simulate profiling output"""
BEFORE_HELP = """Output of "crm_simulate --profile cts/scheduler --repeat <N>" from earlier Pacemaker build"""
# line like: * Testing cts/scheduler/xml/1360.xml ... 0.07 secs
PATTERN = r"""^\s*\*\s+Testing\s+.*/([^/]+)\.xml\s+\.+\s+([.0-9]+)\s+secs\s*$"""
def parse_args(argv=sys.argv):
""" Parse command-line arguments """
parser = argparse.ArgumentParser(description=DESC)
parser.add_argument('-V', '--verbose', action='count',
help='Increase verbosity')
parser.add_argument('-p', '--threshold-percent', type=float, default=0,
help="Don't show tests with less than this percentage difference in times")
parser.add_argument('-s', '--threshold-seconds', type=float, default=0,
help="Don't show tests with less than this seconds difference in times")
parser.add_argument('-S', '--sort', choices=['test', 'before', 'after', 'diff', 'percent'],
default='test', help="Sort results by this column")
parser.add_argument('-r', '--reverse', action='store_true',
help="Sort results in descending order")
parser.add_argument('before_file', metavar='BEFORE',
type=argparse.FileType('r'),
help=BEFORE_HELP)
parser.add_argument('after_file', metavar='AFTER',
type=argparse.FileType('r'),
help='Output of same command from later Pacemaker build')
return parser.parse_args(argv[1:])
def extract_times(infile):
""" Extract test names and times into hash table from file """
result = {}
for line in infile:
match = re.search(PATTERN, line)
if match is not None:
result[match.group(1)] = match.group(2)
return result
def compare_test(test, before, after, args):
""" Compare one test's timings """
try:
before_time = float(before[test])
except KeyError:
if args.verbose > 0:
print("No previous test " + test + " to compare")
return None
after_time = float(after[test])
time_diff = after_time - before_time
time_diff_percent = (time_diff / before_time) * 100
if ((abs(time_diff) >= args.threshold_seconds)
and (abs(time_diff_percent) >= args.threshold_percent)):
return { 'test': test,
'before': before_time,
'after': after_time,
'diff': time_diff,
'percent': time_diff_percent
}
return None
def sort_diff(result):
""" Sort two test results by time difference """
global sort_field
return result[sort_field]
def print_results(results, sort_reverse):
""" Output the comparison results """
if results == []:
return
# Sort and print test differences
results.sort(reverse=sort_reverse, key=sort_diff)
for result in results:
print("%-40s %6.2fs vs %6.2fs (%+.2fs = %+6.2f%%)" % (result['test'],
result['before'], result['after'], result['diff'],
result['percent']))
# Print average differences
diff_total = sum(d['diff'] for d in results)
percent_total = sum(d['percent'] for d in results)
nresults = len(results)
print("\nAverages: %+.2fs %+6.2f%%" % ((diff_total / nresults),
(percent_total / nresults)))
if __name__ == "__main__":
global sort_field
try:
args = parse_args()
if args.verbose is None:
args.verbose = 0
before = extract_times(args.before_file)
after = extract_times(args.after_file)
sort_field = args.sort
# Build a list of test differences
results = []
for test in after.keys():
result = compare_test(test, before, after, args)
if result is not None:
results = results + [ result ]
print_results(results, sort_reverse=args.reverse)
except KeyboardInterrupt:
pass
except IOError as e:
if e.errno != errno.EPIPE:
raise
sys.exit(ExitStatus.OK)
# vim: set filetype=python expandtab tabstop=4 softtabstop=4 shiftwidth=4 textwidth=120:
|