7 from statistics
import geometric_mean
9 TIMING_LOG_RE
= re
.compile(r
"(.*)/(.*).tmp(.*)")
13 parser
= argparse
.ArgumentParser(
14 description
="BOLT NFC stat parser",
15 formatter_class
=argparse
.ArgumentDefaultsHelpFormatter
,
18 "input", nargs
="+", help="timing.log files produced by llvm-bolt-wrapper"
21 "--check_longer_than",
24 help="Only warn on tests longer than X seconds for at least one side",
30 help="Threshold for a single test result swing, abs percent",
36 help="Threshold for geomean test results swing, abs percent",
38 parser
.add_argument("--verbose", "-v", action
="store_true")
39 args
= parser
.parse_args()
41 def fmt_delta(value
, exc_threshold
, above_bound
=True):
42 formatted_value
= format(value
, "+.2%")
44 formatted_value
+= "?"
45 elif exc_threshold
and sys
.stdout
.isatty(): # terminal supports colors
46 return f
"\033[1m{formatted_value}\033[0m"
47 return formatted_value
49 # Ratios for geomean computation
52 # Whether any test exceeds the single test threshold (mem or time)
53 threshold_single
= False
54 # Whether geomean exceeds aggregate test threshold (mem or time)
58 print(f
"# Individual test threshold: +-{args.threshold_single}%")
59 print(f
"# Aggregate (geomean) test threshold: +-{args.threshold_agg}%")
61 f
"# Checking time swings for tests with runtime >"
62 f
"{args.check_longer_than}s - otherwise marked as ?"
64 print("Test/binary BOLT_wall_time BOLT_max_rss")
66 for input_file
in args
.input:
67 input_dir
= os
.path
.dirname(input_file
)
68 with
open(input_file
) as timing_file
:
69 timing_reader
= csv
.reader(timing_file
, delimiter
=";")
70 for row
in timing_reader
:
72 m
= TIMING_LOG_RE
.match(row
[0])
74 test_name
= f
"{input_dir}/{m.groups()[1]}/{m.groups()[2]}"
76 # Prepend input dir to unparsed test name
77 test_name
= input_dir
+ "#" + test_name
78 time_a
, time_b
= float(row
[1]), float(row
[3])
79 mem_a
, mem_b
= int(row
[2]), int(row
[4])
80 # Check if time is above bound for at least one side
81 time_above_bound
= any(
82 [x
> args
.check_longer_than
for x
in [time_a
, time_b
]]
84 # Compute B/A ratios (for % delta and geomean)
85 time_ratio
= time_b
/ time_a
if time_a
else float('nan')
86 mem_ratio
= mem_b
/ mem_a
if mem_a
else float('nan')
87 # Keep ratios for geomean
88 if time_above_bound
and time_ratio
> 0: # must be >0 for gmean
89 time_ratios
+= [time_ratio
]
90 mem_ratios
+= [mem_ratio
]
91 # Deltas: (B/A)-1 = (B-A)/A
92 time_delta
= time_ratio
- 1
93 mem_delta
= mem_ratio
- 1
94 # Check individual test results vs single test threshold
96 100.0 * abs(time_delta
) > args
.threshold_single
and time_above_bound
98 mem_exc
= 100.0 * abs(mem_delta
) > args
.threshold_single
99 if time_exc
or mem_exc
:
100 threshold_single
= True
101 # Print deltas with formatting in verbose mode
102 if args
.verbose
or time_exc
or mem_exc
:
105 fmt_delta(time_delta
, time_exc
, time_above_bound
),
106 fmt_delta(mem_delta
, mem_exc
),
109 time_gmean_delta
= geometric_mean(time_ratios
) - 1
110 mem_gmean_delta
= geometric_mean(mem_ratios
) - 1
111 time_agg_threshold
= 100.0 * abs(time_gmean_delta
) > args
.threshold_agg
112 mem_agg_threshold
= 100.0 * abs(mem_gmean_delta
) > args
.threshold_agg
113 if time_agg_threshold
or mem_agg_threshold
:
115 if time_agg_threshold
or mem_agg_threshold
or args
.verbose
:
118 fmt_delta(time_gmean_delta
, time_agg_threshold
),
119 fmt_delta(mem_gmean_delta
, mem_agg_threshold
),
121 exit(threshold_single
or threshold_agg
)
124 if __name__
== "__main__":