1#!/usr/bin/env python3 2""" 3This script parses benchmark log files generated by the lowlevel-blt-bench benchmark and 4compares the results. It can also export the parsed results to a CSV file. 5 6Usage: 7 python3 lowlevel_blt_bench_compare <log_file> [<log_file2>] 8 [--export-csv <csv_file>] [--quiet] [--color] [--color-breaks <breaks>] 9 [--disable-columns <columns>] 10 11Author: Marek Pikuła <m.pikula@partner.samsung.com> 12""" 13 14import argparse 15import re 16 17import pandas as pd 18from colorama import Fore, Style 19from tabulate import tabulate 20 21SPEEDUP_COLORS = [ 22 Fore.RED, 23 Fore.LIGHTRED_EX, 24 Fore.YELLOW, 25 Fore.LIGHTYELLOW_EX, 26 Fore.LIGHTGREEN_EX, 27 Fore.GREEN, 28 Fore.BLUE, 29] 30 31 32def colorize_speedup(value: float, breaks: list[float]) -> str: 33 """Colorize the speedup value depending on the range of its value.""" 34 color = SPEEDUP_COLORS[-1] 35 for c, b in zip(SPEEDUP_COLORS, breaks): 36 if value <= b: 37 color = c 38 break 39 40 return f"{color}{value}{Style.RESET_ALL}" 41 42 43def print_df(df: pd.DataFrame, color: bool, breaks: list[float], drop_cols: list[str]): 44 """Print the DataFrame with colorized speedup values.""" 45 df.loc["Average"] = df.mean(axis=0) 46 df.drop(columns=drop_cols, errors="ignore", inplace=True) 47 48 table = tabulate( 49 df.map(colorize_speedup, breaks=breaks) if color else df, 50 floatfmt="2.3f", 51 headers="keys", 52 showindex=True, 53 ) 54 55 # Print the table 56 print(table) 57 58 59def parse_benchmark_log(log_file: str): 60 """Parse a benchmark log file and return a DataFrame with the results.""" 61 62 # Regular expression to match benchmark lines. 63 benchmark_regex = re.compile( 64 r"^\s*(\S+)\s*=\s*L1:\s*([\d.]+)\s*L2:\s*([\d.]+)\s*M:\s*([\d.]+).*" 65 r"HT:\s*([\d.]+)\s*VT:\s*([\d.]+)\s*R:\s*([\d.]+)\s*RT:\s*([\d.]+)\s*" 66 r"\(\s*([\d.]+).*\)" 67 ) 68 69 # Read the log file and parse benchmark results using list comprehension. 70 with open(log_file, "r", encoding="utf-8") as file: 71 parsed_lines = tuple( 72 ( 73 match.group(1), 74 map(float, match.groups()[1:]), 75 ) 76 for line in file 77 if (match := benchmark_regex.match(line)) 78 ) 79 80 # Unpack parsed lines into functions and metrics. 81 functions, metrics = zip(*parsed_lines) if parsed_lines else ([], []) 82 83 # Create a DataFrame from the parsed data. 84 out = pd.DataFrame( 85 metrics, 86 index=functions, 87 columns=("L1", "L2", "M", "HT", "VT", "R", "RT", "Kops/s"), 88 ) 89 out["Avg"] = out.mean(axis=1) 90 return out 91 92 return pd.DataFrame() 93 94 95if __name__ == "__main__": 96 # Set up argument parser. 97 parser = argparse.ArgumentParser( 98 description="Parse and compare lowlevel-blt-bench benchmark results.", 99 ) 100 parser.add_argument( 101 "log_file", 102 help="Path to the first benchmark log file.", 103 ) 104 parser.add_argument( 105 "log_file2", 106 nargs="?", 107 help="Path to the second benchmark log file (optional).", 108 ) 109 parser.add_argument( 110 "--export-csv", 111 "-e", 112 metavar="CSV_FILE", 113 help="Export the parsed results to a CSV file.", 114 ) 115 parser.add_argument( 116 "--quiet", 117 "-q", 118 action="store_true", 119 help="Don't print results (useful with --export-csv).", 120 ) 121 parser.add_argument( 122 "--color", 123 "-c", 124 action="store_true", 125 help="Print table in color.", 126 ) 127 parser.add_argument( 128 "--disable-columns", 129 "-d", 130 metavar="COLUMNS", 131 help="Comma-separated list of columns to disable (e.g., 'L1,L2,M').", 132 ) 133 parser.add_argument( 134 "--color-breaks", 135 "-b", 136 metavar="BREAKS", 137 default="0.8,0.9,1.1,1.5,3.0,5.0", 138 help="Speedup values for color breaks (up to 6).", 139 ) 140 args = parser.parse_args() 141 142 # Don't truncate DataFrame output. 143 pd.set_option("display.max_rows", None) 144 145 # Parse list arguments. 146 disabled_columns: list[str] = ( 147 args.disable_columns.split(",") if args.disable_columns else [] 148 ) 149 color_breaks: list[float] = list(map(float, args.color_breaks.split(","))) 150 151 # Parse the first log file. 152 df1 = parse_benchmark_log(args.log_file) 153 to_export = df1 154 155 if args.log_file2: 156 # Parse the second log file and calculate speedup 157 df2 = parse_benchmark_log(args.log_file2) 158 159 # Align the two DataFrames based on their indices 160 df1, df2 = df1.align(df2, join="inner") 161 162 speedup = (df2 / df1) * 100000 // 100 / 1000 163 164 if not args.quiet: 165 print(f'Speedup between "{args.log_file}" and "{args.log_file2}":\n') 166 print_df(speedup, args.color, color_breaks, disabled_columns) 167 168 to_export = speedup 169 elif not args.quiet: 170 # Print the parsed DataFrame 171 print_df(df1, args.color, color_breaks, disabled_columns) 172 173 # Export parsed results to CSV if requested 174 if args.export_csv: 175 to_export.to_csv(args.export_csv) 176 if not args.quiet: 177 print(f"Parsed results exported to {args.export_csv}") 178