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