blob: e924baf3d7a131d66f070244d980bb12ac2754d9 [file] [log] [blame]
# 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)