| # 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) |