| # Copyright 2023 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 |
| |
| import csv, textwrap |
| import numpy as np |
| from collections import namedtuple |
| from pathlib import Path |
| |
| |
| class PerformanceResult: |
| """Performance result of a single run.""" |
| |
| def __init__(self, operation, configuration, verification_result, runtime): |
| self.operation = operation |
| self.configuration = configuration |
| self.verification_result = verification_result |
| self.runtime = runtime # in milliseconds |
| self.gflops = float(self.operation.flops()) / self.runtime / 1.0e6 |
| |
| def print(self): |
| """Prints the performance result to the console.""" |
| runtime = str(self.runtime) if self.runtime != -1.0 else "Not profiled" |
| gflops = ( |
| str(float(round(self.gflops, 2))) |
| if self.runtime != -1.0 |
| else "Not profiled" |
| ) |
| |
| print("---------------------------------------------------------------- ") |
| print( |
| f'Dispatch : {"_".join([self.operation.name(), self.configuration.name()])}' |
| ) |
| print(f"Provider : IREE Codegen") |
| print(f"OpKind : {self.operation.operation_kind}") |
| print(f"Operation : {self.operation.name()}") |
| print(f"Configuration : {self.configuration.name()}") |
| # Operation specific arguments. |
| arg_str = " ".join( |
| [ |
| f"--{key}={value}" |
| for key, value in self.operation.get_argument_dict().items() |
| ] |
| ) |
| wrapped_arg_str = textwrap.fill( |
| arg_str, width=80, subsequent_indent=" " |
| ) |
| print(f"Arguments : {wrapped_arg_str}") |
| print(f"Verification : {self.verification_result}") |
| print(f"Runtime(ms) : {runtime}") |
| print(f"GFLOPs : {gflops}") |
| |
| def get_dict_entry(self): |
| """Returns a dictionary with the performance result.""" |
| runtime = self.runtime if self.runtime != -1.0 else "" |
| gflops = float(round(self.gflops, 2)) if self.runtime != -1.0 else "Not run" |
| dict_entry = { |
| "Provider": "IREE Codegen", |
| "Verification": self.verification_result, |
| "Runtime(ms)": runtime, |
| "GFLOPs": gflops, |
| } |
| |
| # Add the operation specific arguments. |
| dict_entry.update(self.operation.get_dict_entry()) |
| |
| # Add the configuration specific arguments. |
| dict_entry.update(self.configuration.get_dict_entry()) |
| |
| return dict_entry |
| |
| |
| class PerformanceReport: |
| """Performance report class is used to store the performance results of multiple runs. |
| The report can be written to a csv file.""" |
| |
| def __init__(self, args): |
| self.args = args |
| |
| # Data members extracted from the args. |
| self.output_file_path = None |
| if args.output != "": |
| self.output_file_path = Path(args.output) |
| |
| # List of PerformanceResult. |
| self.perf_result_vector = [] |
| |
| # Additional tags to add to the csv report file. \ |
| # Useful for generating pivot tables. |
| self.tags = [] |
| if args.tags != "": |
| self.tags = args.tags.split(",") |
| |
| # Boolen to check if the header is written to the csv file. |
| self.is_header_written = False |
| |
| # If the args.output set, open the file and write the header. |
| self.open_mode = "a" if self.args.append else "w" |
| if self.output_file_path: |
| self.csv_file = open(self.output_file_path, self.open_mode) |
| |
| def __del__(self): |
| """If the args.output set, close the file.""" |
| if self.output_file_path: |
| print("Writing performance report to %s" % self.output_file_path) |
| self.csv_file.close() |
| |
| def write_csv_header(self, operation, configuration): |
| """Write the header to the csv file.""" |
| |
| # Create and write the header. |
| operation_specific_header = list(operation.get_dict_entry().keys()) |
| configuration_specific_header = list(configuration.get_dict_entry().keys()) |
| performance_header = ["Verification", "Runtime(ms)", "GFLOPs"] |
| csv_header = ( |
| operation_specific_header |
| + configuration_specific_header |
| + performance_header |
| ) |
| csv_header = ["Provider"] + csv_header |
| |
| # If tags are present, add the tags.keys() to the begining of the csv header. |
| if len(self.tags): |
| tag_header = [tag.split(":")[0] for tag in self.tags] |
| csv_header = tag_header + csv_header |
| |
| # Create the csv dictionary writer. |
| self.csv_writer = csv.DictWriter(self.csv_file, fieldnames=csv_header) |
| |
| # Write the header if the file is being created. |
| if self.open_mode == "w": |
| self.csv_writer.writeheader() |
| |
| def append_perf_result(self, performance_result): |
| """Appends a performance result to the report. |
| Additionaly, if args.output set, write the csv_row entry.""" |
| self.perf_result_vector.append(performance_result) |
| |
| if self.output_file_path: |
| # Write the header if not written. |
| if not self.is_header_written: |
| self.write_csv_header( |
| performance_result.operation, performance_result.configuration |
| ) |
| self.is_header_written = True |
| |
| # Create the row entries for performance result. |
| csv_dict_row = performance_result.get_dict_entry() |
| |
| # Create the row entries for tags. |
| for tag in self.tags: |
| tag_key, tag_value = tag.split(":") |
| csv_dict_row[tag_key] = tag_value |
| |
| # Write the row. |
| self.csv_writer.writerow(csv_dict_row) |