| # Copyright 2022 The IREE Authors | 
 | # | 
 | # Licensed under the Apache License v2.0 with LLVM Exceptions. | 
 | # See https://llvm.org/LICENSE.txt for license information. | 
 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | r"""Script to generate a HTML summary of SHARK Tank benchmarks. | 
 |  | 
 | Example usage: | 
 |  | 
 | python parse_shark_benchmarks.py \ | 
 |   --cpu_shark_csv=icelake_shark_bench_results.csv \ | 
 |   --cpu_iree_csv=icelake_iree_bench_results.csv \ | 
 |   --cpu_baseline_csv=cpu_baseline.csv \ | 
 |   --gpu_shark_csv=a100_shark_bench_results.csv \ | 
 |   --gpu_iree_csv=a100_iree_bench_results.csv \ | 
 |   --gpu_baseline_csv=a100_baseline.csv \ | 
 |   --version_info iree=version_info.txt \ | 
 |   --output_path=/tmp/summary.html | 
 | """ | 
 |  | 
 | import argparse | 
 | import pandas as pd | 
 | import pathlib | 
 | import sys | 
 |  | 
 | from datetime import date | 
 |  | 
 | # Add build_tools python dir to the search path. | 
 | sys.path.insert(0, str(pathlib.Path(__file__).parent.parent.parent / "python")) | 
 | from reporting.common import html_utils | 
 |  | 
 | # Column headers. | 
 | _MODEL = "model" | 
 | _DIALECT = "dialect" | 
 | _DATA_TYPE = "data_type" | 
 | _BASELINE = "baseline" | 
 | _DEVICE = "device" | 
 | _BASELINE_LATENCY = "baseline latency (ms)" | 
 | _IREE_LATENCY = "IREE latency (ms)" | 
 | _SHARK_LATENCY = "SHARK latency (ms)" | 
 | _IREE_VS_BASELINE = "IREE vs baseline" | 
 | _SHARK_VS_BASELINE = "SHARK vs baseline" | 
 | _IREE_VS_SHARK = "IREE vs SHARK" | 
 | _BASELINE_MEMORY = "Baseline Peak Device Memory (MB)" | 
 | _IREE_MEMORY = "IREE Peak Device Memory (MB)" | 
 | _SHARK_MEMORY = "SHARK Peak Device Memory (MB)" | 
 |  | 
 | _PERF_COLUMNS = [_IREE_VS_BASELINE, _SHARK_VS_BASELINE, _IREE_VS_SHARK] | 
 | _LATENCY_COLUMNS = [_BASELINE_LATENCY, _IREE_LATENCY, _SHARK_LATENCY] | 
 | _MEMORY_COLUMNS = [_BASELINE_MEMORY, _IREE_MEMORY, _SHARK_MEMORY] | 
 |  | 
 |  | 
 | def _generate_table(df_iree, df_shark, df_baseline, title): | 
 |   """Generates a table comparing latencies between IREE, SHARK and a baseline.""" | 
 |   summary = pd.DataFrame(columns=[ | 
 |       _MODEL, _BASELINE, _DATA_TYPE, _DIALECT, _DEVICE, _BASELINE_LATENCY, | 
 |       _IREE_LATENCY, _SHARK_LATENCY, _IREE_VS_BASELINE, _SHARK_VS_BASELINE, | 
 |       _IREE_VS_SHARK, _BASELINE_MEMORY, _IREE_MEMORY, _SHARK_MEMORY | 
 |   ]) | 
 |  | 
 |   models = df_iree.model.unique() | 
 |   for model in models: | 
 |     iree_results_per_model = df_iree.loc[df_iree.model == model] | 
 |     dialects = iree_results_per_model.dialect.unique() | 
 |     for dialect in dialects: | 
 |       iree_results_per_dialect = iree_results_per_model.loc[ | 
 |           iree_results_per_model.dialect == dialect] | 
 |       data_types = iree_results_per_dialect.data_type.unique() | 
 |       for data_type in data_types: | 
 |         iree_results_per_datatype = iree_results_per_dialect.loc[ | 
 |             iree_results_per_dialect.data_type == data_type] | 
 |         device_types = iree_results_per_datatype.device.unique() | 
 |         for device in device_types: | 
 |           iree_results = iree_results_per_datatype.loc[ | 
 |               iree_results_per_datatype.device == device] | 
 |           if len(iree_results) != 3: | 
 |             print(f"Warning! Expected number of results to be 3. Got" | 
 |                   f" {len(iree_results)}") | 
 |             print(iree_results) | 
 |             continue | 
 |  | 
 |           baseline_results = df_baseline.loc[(df_baseline.model == model) & | 
 |                                              (df_baseline.dialect == dialect) & | 
 |                                              (df_baseline.data_type | 
 |                                               == data_type) & | 
 |                                              (df_baseline.device == device)] | 
 |  | 
 |           if baseline_results.empty: | 
 |             # We use snapshots of latencies for baseline. If it is a new | 
 |             # benchmark that is not included in the snapshot yet, emit a | 
 |             # warning. | 
 |             print( | 
 |                 f"Warning: No baseline results found for {model}, {dialect}," | 
 |                 f" {data_type}, {device}. Using IREE version as baseline. Please" | 
 |                 f" update baseline csv.") | 
 |             engine = iree_results.engine.iloc[0] | 
 |             baseline_df = iree_results.loc[iree_results.engine == engine] | 
 |             baseline_latency = baseline_df.iloc[0]["ms/iter"] | 
 |             baseline_device_mb = baseline_df.iloc[0]["device_memory_mb"] | 
 |           else: | 
 |             engine = baseline_results.engine.iloc[0] | 
 |             baseline_df = baseline_results.loc[baseline_results.engine == | 
 |                                                engine] | 
 |             baseline_latency = baseline_df.iloc[0]["ms/iter"] | 
 |             baseline_device_mb = baseline_df.iloc[0]["device_memory_mb"] | 
 |  | 
 |           iree_df = iree_results.loc[iree_results.engine == "shark_iree_c"] | 
 |           iree_latency = iree_df.iloc[0]["ms/iter"] | 
 |           iree_device_mb = iree_df.iloc[0]["device_memory_mb"] | 
 |           iree_vs_baseline = html_utils.format_latency_comparison( | 
 |               iree_latency, baseline_latency) | 
 |  | 
 |           if df_shark is not None: | 
 |             shark_results = df_shark.loc[(df_shark.model == model) & | 
 |                                          (df_shark.dialect == dialect) & | 
 |                                          (df_shark.data_type == data_type) & | 
 |                                          (df_shark.device == device)] | 
 |             if shark_results.empty: | 
 |               print( | 
 |                   f"Warning: No SHARK results for {model}, {dialect}, {data_type}, {device}." | 
 |               ) | 
 |               continue | 
 |  | 
 |             shark_df = shark_results.loc[shark_results.engine == "shark_iree_c"] | 
 |             shark_latency = shark_df.iloc[0]["ms/iter"] | 
 |             shark_device_mb = shark_df.iloc[0]["device_memory_mb"] | 
 |             shark_vs_baseline = html_utils.format_latency_comparison( | 
 |                 shark_latency, baseline_latency) | 
 |             iree_vs_shark = html_utils.format_latency_comparison( | 
 |                 iree_latency, shark_latency) | 
 |           else: | 
 |             # If there are no SHARK benchmarks available, use default values. | 
 |             # These columns will be hidden later. | 
 |             shark_latency = 0 | 
 |             shark_vs_baseline = "<missing_comparison>" | 
 |             iree_vs_shark = "<missing_comparison>" | 
 |  | 
 |           summary.loc[len(summary)] = [ | 
 |               model, | 
 |               engine, | 
 |               data_type, | 
 |               dialect, | 
 |               device, | 
 |               f"{baseline_latency:.1f}", | 
 |               f"{iree_latency:.1f}", | 
 |               f"{shark_latency:.1f}", | 
 |               iree_vs_baseline, | 
 |               shark_vs_baseline, | 
 |               iree_vs_shark, | 
 |               f"{baseline_device_mb:.3f}", | 
 |               f"{iree_device_mb:.3f}", | 
 |               f"{shark_device_mb:.3f}", | 
 |           ] | 
 |  | 
 |   summary = summary.round(2) | 
 |  | 
 |   st = summary.style.set_table_styles(html_utils.get_table_css()) | 
 |   st = st.hide(axis="index") | 
 |   if df_shark is None: | 
 |     st = st.hide_columns( | 
 |         subset=[_SHARK_LATENCY, _SHARK_VS_BASELINE, _IREE_VS_SHARK]) | 
 |   st = st.set_caption(title) | 
 |   st = st.applymap(html_utils.style_performance, subset=_PERF_COLUMNS) | 
 |   st = st.set_properties(subset=[_MODEL], | 
 |                          **{ | 
 |                              "width": "300px", | 
 |                              "text-align": "left", | 
 |                          }) | 
 |   st = st.set_properties(subset=[_BASELINE], | 
 |                          **{ | 
 |                              "width": "140", | 
 |                              "text-align": "center", | 
 |                          }) | 
 |   st = st.set_properties(subset=[_DIALECT, _DATA_TYPE, _DEVICE], | 
 |                          **{ | 
 |                              "width": "100", | 
 |                              "text-align": "center", | 
 |                          }) | 
 |   st = st.set_properties(subset=_LATENCY_COLUMNS, | 
 |                          **{ | 
 |                              "width": "100", | 
 |                              "text-align": "right", | 
 |                          }) | 
 |   st = st.set_properties(subset=_PERF_COLUMNS, | 
 |                          **{ | 
 |                              "width": "150px", | 
 |                              "text-align": "right", | 
 |                              "color": "#ffffff" | 
 |                          }) | 
 |   st = st.set_properties(subset=_MEMORY_COLUMNS, | 
 |                          **{ | 
 |                              "width": "100", | 
 |                              "text-align": "right", | 
 |                          }) | 
 |  | 
 |   return st.to_html() + "<br/>" | 
 |  | 
 |  | 
 | def generate_table(iree_csv, | 
 |                    baseline_csv, | 
 |                    shark_csv=None, | 
 |                    shape_type="static", | 
 |                    device="cpu", | 
 |                    title="Benchmarks"): | 
 |   """Generates a table comparing latencies between IREE, SHARK and a baseline. | 
 |  | 
 |   Args: | 
 |     iree_csv: Path to the csv file containing IREE latencies. | 
 |     baseline_csv: Path to the csv file containing baseline latencies. | 
 |     shark_csv: Path to the csv file containing SHARK-Runtime latencies. This is optional. | 
 |     shape_type: Currently either `static` or `dynamic`. | 
 |     device: Device used to run the benchmarks. | 
 |     title: The title of the generated table. | 
 |  | 
 |   Returns: | 
 |     An HTML string containing the summarized report. | 
 |   """ | 
 |   shark_df = None | 
 |   if shark_csv is not None: | 
 |     shark_df = pd.read_csv(shark_csv) | 
 |     shark_df = shark_df.loc[(shark_df.shape_type == shape_type) & | 
 |                             (shark_df.device == device)] | 
 |  | 
 |   iree_df = pd.read_csv(iree_csv) | 
 |   iree_df = iree_df.loc[(iree_df.shape_type == shape_type) & | 
 |                         (iree_df.device == device)] | 
 |  | 
 |   baseline_df = pd.read_csv(baseline_csv) | 
 |   baseline_df = baseline_df.loc[(baseline_df.shape_type == shape_type) & | 
 |                                 (baseline_df.device == device)] | 
 |  | 
 |   return _generate_table(iree_df, shark_df, baseline_df, title) | 
 |  | 
 |  | 
 | def main(args): | 
 |   """Summarizes benchmark results generated by the SHARK Tank.""" | 
 |   version_html = f"<i>last updated: {date.today().isoformat()}</i><br/><br/>" | 
 |   version_html += "<i><b>Version Info</b></i><br/>" | 
 |   with open(args.version_info) as f: | 
 |     version_info = dict(l.strip().split("=", 1) for l in f) | 
 |   for key, value in version_info.items(): | 
 |     version_html += f"<i>{key}: {value}</i><br/>" | 
 |   version_html += "<br/>" | 
 |  | 
 |   html = html_utils.generate_header_and_legend(version_html) | 
 |  | 
 |   # Generate Server CPU Static. | 
 |   if args.cpu_iree_csv is not None: | 
 |     html += generate_table(args.cpu_iree_csv, | 
 |                            args.cpu_baseline_csv, | 
 |                            shark_csv=args.cpu_shark_csv, | 
 |                            shape_type="static", | 
 |                            device="cpu", | 
 |                            title="Server Intel Ice Lake CPU (Static Shapes)") | 
 |  | 
 |   # Generate Server GPU Static. | 
 |   if args.gpu_iree_csv is not None: | 
 |     html += generate_table(args.gpu_iree_csv, | 
 |                            args.gpu_baseline_csv, | 
 |                            shark_csv=args.gpu_shark_csv, | 
 |                            shape_type="static", | 
 |                            device="cuda", | 
 |                            title="Server NVIDIA Tesla A100 GPU (Static Shapes)") | 
 |  | 
 |   # Generate Server CPU Dynamic. | 
 |   if args.cpu_iree_csv is not None: | 
 |     html += generate_table(args.cpu_iree_csv, | 
 |                            args.cpu_baseline_csv, | 
 |                            shark_csv=args.cpu_shark_csv, | 
 |                            shape_type="dynamic", | 
 |                            device="cpu", | 
 |                            title="Server Intel Ice Lake CPU (Dynamic Shapes)") | 
 |  | 
 |   # Generate Server GPU Dynamic. | 
 |   if args.gpu_iree_csv is not None: | 
 |     html += generate_table( | 
 |         args.gpu_iree_csv, | 
 |         args.gpu_baseline_csv, | 
 |         shark_csv=args.gpu_shark_csv, | 
 |         shape_type="dynamic", | 
 |         device="cuda", | 
 |         title="Server NVIDIA Tesla A100 GPU (Dynamic Shapes)") | 
 |  | 
 |   args.output_path.write_text(html) | 
 |  | 
 |  | 
 | def parse_args(): | 
 |   parser = argparse.ArgumentParser() | 
 |   parser.add_argument( | 
 |       "--cpu_shark_csv", | 
 |       type=str, | 
 |       default=None, | 
 |       help="The path to the csv file with CPU benchmarking results from the " | 
 |       "SHARK runtime.") | 
 |   parser.add_argument( | 
 |       "--cpu_iree_csv", | 
 |       type=str, | 
 |       default=None, | 
 |       help="The path to the csv file with CPU benchmarking results from IREE.") | 
 |   parser.add_argument( | 
 |       "--cpu_baseline_csv", | 
 |       type=str, | 
 |       default="data/icelake_baseline_2022-09-19.csv", | 
 |       help="The path to the csv file containing baseline CPU results.") | 
 |   parser.add_argument( | 
 |       "--gpu_shark_csv", | 
 |       type=str, | 
 |       default=None, | 
 |       help="The path to the csv file with GPU benchmarking results from the " | 
 |       "SHARK runtime.") | 
 |   parser.add_argument( | 
 |       "--gpu_iree_csv", | 
 |       type=str, | 
 |       default=None, | 
 |       help="The path to the csv file with CPU benchmarking results from IREE.") | 
 |   parser.add_argument( | 
 |       "--gpu_baseline_csv", | 
 |       type=str, | 
 |       default="data/a100_baseline_2022-09-19.csv", | 
 |       help="The path to the csv file containing baseline GPU results.") | 
 |   parser.add_argument( | 
 |       "--version_info", | 
 |       type=pathlib.Path, | 
 |       default=None, | 
 |       help= | 
 |       "The path to a text file containing version information of the frameworks tested." | 
 |   ) | 
 |   parser.add_argument( | 
 |       "--output_path", | 
 |       type=pathlib.Path, | 
 |       default="/tmp/summary.html", | 
 |       help="The path to the output html file that summarizes results.") | 
 |   return parser.parse_args() | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |   main(parse_args()) |