llvm-project
125 строк · 4.7 Кб
1#!/usr/bin/env python3
2import argparse3import csv4import re5import sys6import os7from statistics import geometric_mean8
9TIMING_LOG_RE = re.compile(r"(.*)/(.*).tmp(.*)")10
11
12def main():13parser = argparse.ArgumentParser(14description="BOLT NFC stat parser",15formatter_class=argparse.ArgumentDefaultsHelpFormatter,16)17parser.add_argument(18"input", nargs="+", help="timing.log files produced by llvm-bolt-wrapper"19)20parser.add_argument(21"--check_longer_than",22default=2,23type=float,24help="Only warn on tests longer than X seconds for at least one side",25)26parser.add_argument(27"--threshold_single",28default=10,29type=float,30help="Threshold for a single test result swing, abs percent",31),32parser.add_argument(33"--threshold_agg",34default=5,35type=float,36help="Threshold for geomean test results swing, abs percent",37),38parser.add_argument("--verbose", "-v", action="store_true")39args = parser.parse_args()40
41def fmt_delta(value, exc_threshold, above_bound=True):42formatted_value = format(value, "+.2%")43if not above_bound:44formatted_value += "?"45elif exc_threshold and sys.stdout.isatty(): # terminal supports colors46return f"\033[1m{formatted_value}\033[0m"47return formatted_value48
49# Ratios for geomean computation50time_ratios = []51mem_ratios = []52# Whether any test exceeds the single test threshold (mem or time)53threshold_single = False54# Whether geomean exceeds aggregate test threshold (mem or time)55threshold_agg = False56
57if args.verbose:58print(f"# Individual test threshold: +-{args.threshold_single}%")59print(f"# Aggregate (geomean) test threshold: +-{args.threshold_agg}%")60print(61f"# Checking time swings for tests with runtime >"62f"{args.check_longer_than}s - otherwise marked as ?"63)64print("Test/binary BOLT_wall_time BOLT_max_rss")65
66for input_file in args.input:67input_dir = os.path.dirname(input_file)68with open(input_file) as timing_file:69timing_reader = csv.reader(timing_file, delimiter=";")70for row in timing_reader:71test_name = row[0]72m = TIMING_LOG_RE.match(row[0])73if m:74test_name = f"{input_dir}/{m.groups()[1]}/{m.groups()[2]}"75else:76# Prepend input dir to unparsed test name77test_name = input_dir + "#" + test_name78time_a, time_b = float(row[1]), float(row[3])79mem_a, mem_b = int(row[2]), int(row[4])80# Check if time is above bound for at least one side81time_above_bound = any(82[x > args.check_longer_than for x in [time_a, time_b]]83)84# Compute B/A ratios (for % delta and geomean)85time_ratio = time_b / time_a if time_a else float('nan')86mem_ratio = mem_b / mem_a if mem_a else float('nan')87# Keep ratios for geomean88if time_above_bound and time_ratio > 0: # must be >0 for gmean89time_ratios += [time_ratio]90mem_ratios += [mem_ratio]91# Deltas: (B/A)-1 = (B-A)/A92time_delta = time_ratio - 193mem_delta = mem_ratio - 194# Check individual test results vs single test threshold95time_exc = (96100.0 * abs(time_delta) > args.threshold_single and time_above_bound97)98mem_exc = 100.0 * abs(mem_delta) > args.threshold_single99if time_exc or mem_exc:100threshold_single = True101# Print deltas with formatting in verbose mode102if args.verbose or time_exc or mem_exc:103print(104test_name,105fmt_delta(time_delta, time_exc, time_above_bound),106fmt_delta(mem_delta, mem_exc),107)108
109time_gmean_delta = geometric_mean(time_ratios) - 1110mem_gmean_delta = geometric_mean(mem_ratios) - 1111time_agg_threshold = 100.0 * abs(time_gmean_delta) > args.threshold_agg112mem_agg_threshold = 100.0 * abs(mem_gmean_delta) > args.threshold_agg113if time_agg_threshold or mem_agg_threshold:114threshold_agg = True115if time_agg_threshold or mem_agg_threshold or args.verbose:116print(117"Geomean",118fmt_delta(time_gmean_delta, time_agg_threshold),119fmt_delta(mem_gmean_delta, mem_agg_threshold),120)121exit(threshold_single or threshold_agg)122
123
124if __name__ == "__main__":125main()126