Index: llvm/utils/llvm-mca-compare-multiple-files.py =================================================================== --- /dev/null +++ llvm/utils/llvm-mca-compare-multiple-files.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python + +import argparse +import sys +from json import loads +from subprocess import Popen, PIPE + +# Holds a file statistics. +class Summary: + def __init__( + self, + name, + block_rthroughput, + dispatch_width, + ipc, + instructions, + iterations, + total_cycles, + total_uops, + uops_per_cycle, + ): + self.name = name + self.block_rthroughput = block_rthroughput + self.dispatch_width = dispatch_width + self.ipc = ipc + self.instructions = instructions + self.iterations = iterations + self.total_cycles = total_cycles + self.total_uops = total_uops + self.uops_per_cycle = uops_per_cycle + + +# Parse the program arguments. +def parse_program_args(parser): + parser.add_argument( + "file_names", + nargs="+", + type=str, + help="Names of files which llvm-mca tool process.", + ) + parser.add_argument( + "-mtriple", + nargs=1, + type=str, + action="store", + metavar="[=]", + default=["-"], + help="Target triple.", + ) + parser.add_argument( + "-mcpu", + nargs=1, + type=str, + action="store", + metavar="[=]", + default=["-"], + help="Target a specific cpu type.", + ) + parser.add_argument( + "-march", + nargs=1, + type=str, + action="store", + metavar="[=]", + default=["-"], + help="Target architecture.", + ) + parser.add_argument( + "-dispatch", + nargs=1, + type=str, + action="store", + metavar="[=]", + default=["-"], + help="Override the processor dispatch width.", + ) + parser.add_argument( + "-noalias", + nargs=1, + type=str, + action="store", + metavar="[=]", + default=["-"], + help="If set, assume that loads and stores do not alias.", + ) + parser.add_argument( + "-iterations", + type=str, + nargs=1, + action="store", + metavar="[=]", + default=["-"], + help="Number of iterations to run.", + ) + parser.add_argument( + "--llvm-mca-binary", + nargs=1, + required=True, + type=str, + action="store", + metavar="[=]", + help="Specified relative path to binary of llvm-mca.", + ) + parser.add_argument( + "-v", + action="store_true", + default=False, + help="More details about the running lvm-mca tool.", + ) + return parser.parse_args() + + +# Verify that the program inputs meet the requirements. +def verify_program_inputs(opts): + if opts.iterations[0] != "-" and not opts.iterations[0].isdigit(): + print("error: Please enter valid value for -iterations option.") + return False + if opts.dispatch[0] != "-" and not opts.dispatch[0].isdigit(): + print("error: Please enter valid value for -dispatch option.") + return False + if ( + opts.noalias[0] != "-" + and opts.noalias[0].lower() != "true" + and opts.noalias[0].lower() != "false" + ): + print("error: Please enter valid value for -noalias option.") + return False + return True + + +# Returns the name of the file to be analyzed from the path it is on. +def get_filename_from_path(path): + index_of_slash = path.rfind("/") + return path[(index_of_slash + 1) : len(path)] + + +# Returns the results of the running llvm-mca tool for the input file. +def run_llvm_mca_tool(opts, file_name): + # Get the path of the llvm-mca binary file. + llvm_mca_cmd = opts.llvm_mca_binary[0] + + # The statistics llvm-mca options. + if opts.mtriple[0] != "-": + llvm_mca_cmd += " -mtriple=" + opts.mtriple[0] + if opts.mcpu[0] != "-": + llvm_mca_cmd += " -mcpu=" + opts.mcpu[0] + if opts.iterations[0] != "-": + llvm_mca_cmd += " -iterations=" + opts.iterations[0] + if opts.march[0] != "-": + llvm_mca_cmd += " -march=" + opts.march[0] + if opts.dispatch[0] != "-": + llvm_mca_cmd += " -dispatch=" + opts.dispatch[0] + if opts.noalias[0] != "-": + llvm_mca_cmd += " -noalias=" + opts.noalias[0] + llvm_mca_cmd += " -json" + + # Set file which llvm-mca tool will process. + llvm_mca_cmd += " " + file_name + + if opts.v: + print("run: $ " + llvm_mca_cmd + "\n") + + # Generate the stats with the llvm-mca. + subproc = Popen( + llvm_mca_cmd.split(" "), + stdin=PIPE, + stdout=PIPE, + stderr=PIPE, + universal_newlines=True, + ) + + cmd_stdout, cmd_stderr = subproc.communicate() + + try: + json_parsed = loads(cmd_stdout) + except: + print("error: No valid llvm-mca statistics found.") + print(cmd_stderr) + sys.exit(1) + + return Summary( + opts.file_names[0], + json_parsed["CodeRegions"][0]["SummaryView"]["BlockRThroughput"], + json_parsed["CodeRegions"][0]["SummaryView"]["DispatchWidth"], + json_parsed["CodeRegions"][0]["SummaryView"]["IPC"], + json_parsed["CodeRegions"][0]["SummaryView"]["Instructions"], + json_parsed["CodeRegions"][0]["SummaryView"]["Iterations"], + json_parsed["CodeRegions"][0]["SummaryView"]["TotalCycles"], + json_parsed["CodeRegions"][0]["SummaryView"]["TotaluOps"], + json_parsed["CodeRegions"][0]["SummaryView"]["uOpsPerCycle"], + ) + + +# Print statistics in console for single file or for multiple files. +def console_print_results(array_of_summary, opts): + try: + import termtables as tt + except ImportError: + print("error: termtables not found.") + sys.exit(1) + + headers_names = [None] * (len(opts.file_names) + 1) + headers_names[0] = " " + + print("Input files:") + for i in range(len(array_of_summary)): + print("[f" + str(i + 1) + "]: " + get_filename_from_path(opts.file_names[i])) + headers_names[i + 1] = "[f" + str(i + 1) + "]: " + + print("\nITERATIONS: " + str(array_of_summary[0].iterations) + "\n") + + table_values = [ + [[None] for i in range(len(array_of_summary) + 1)] for j in range(7) + ] + + table_values[0][0] = "Instructions: " + table_values[1][0] = "Total Cycles: " + table_values[2][0] = "Total uOps: " + table_values[3][0] = "Dispatch Width: " + table_values[4][0] = "uOps Per Cycle: " + table_values[5][0] = "IPC: " + table_values[6][0] = "Block RThroughput: " + + for j in range(len(array_of_summary)): + table_values[0][j + 1] = str(array_of_summary[j].instructions) + table_values[1][j + 1] = str(array_of_summary[j].total_cycles) + table_values[2][j + 1] = str(array_of_summary[j].total_uops) + table_values[3][j + 1] = str(array_of_summary[j].dispatch_width) + table_values[4][j + 1] = str(round(array_of_summary[j].uops_per_cycle, 2)) + table_values[5][j + 1] = str(round(array_of_summary[j].ipc, 2)) + table_values[6][j + 1] = str(round(array_of_summary[j].block_rthroughput, 2)) + + tt.print( + table_values, + header=headers_names, + style=tt.styles.ascii_thin_double, + padding=(0, 1), + ) + + +def Main(): + parser = argparse.ArgumentParser() + opts = parse_program_args(parser) + + if not verify_program_inputs(opts): + parser.print_help() + sys.exit(1) + + array_of_summary = [None] * len(opts.file_names) + for i in range(len(opts.file_names)): + array_of_summary[i] = run_llvm_mca_tool(opts, opts.file_names[i]) + console_print_results(array_of_summary, opts) + + +if __name__ == "__main__": + Main() + sys.exit(0)