diff --git a/llvm/utils/llvm-mca-compare.py b/llvm/utils/llvm-mca-compare.py --- a/llvm/utils/llvm-mca-compare.py +++ b/llvm/utils/llvm-mca-compare.py @@ -2,6 +2,7 @@ import argparse import sys +import os from json import loads from subprocess import Popen, PIPE @@ -60,6 +61,27 @@ default=["-"], help="Forward options to lvm-mca tool.", ) + parser.add_argument( + "-plot", + action="store_true", + default=False, + help="Draw plots of statistics for input files.", + ) + parser.add_argument( + "-plot-resource-pressure", + action="store_true", + default=False, + help="Draw plots of resource pressure per iterations for input files.", + ) + parser.add_argument( + "--plot-path", + nargs=1, + type=str, + action="store", + metavar="[=]", + default=["-"], + help="Specify relative path where you want to save the plots.", + ) parser.add_argument( "-v", action="store_true", @@ -69,6 +91,17 @@ return parser.parse_args() +# Verify that the program inputs meet the requirements. +def verify_program_inputs(opts): + if opts.plot_path[0] != "-" and not opts.plot and not opts.plot_resource_pressure: + print( + "error: Please specify --plot-path only with the -plot or -plot-resource-pressure options." + ) + 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("/") @@ -129,7 +162,7 @@ "ResourcePressureInfo" ] - name_target_info_resources = [" "] + json_parsed["TargetInfo"]["Resources"] + name_target_info_resources = json_parsed["TargetInfo"]["Resources"] for s in range(len(resource_pressure_info)): obj_of_resource_pressure_info = resource_pressure_info[s] @@ -243,7 +276,9 @@ for j in range(len(matrix_of_code_regions) + 1) ] - table_values[0] = matrix_of_code_regions[0][0].name_target_info_resources + table_values[0] = [" "] + matrix_of_code_regions[0][ + 0 + ].name_target_info_resources for j in range(len(matrix_of_code_regions)): if len(matrix_of_code_regions[j]) > i: @@ -263,15 +298,289 @@ print("\n") +# Based on the obtained results (summary view) of llvm-mca tool, draws plots for multiple input files. +def draw_plot_files_summary(array_of_summary, opts): + try: + import matplotlib.pyplot as plt + except ImportError: + print("error: matplotlib.pyplot not found.") + sys.exit(1) + try: + from matplotlib.cm import get_cmap + except ImportError: + print("error: get_cmap (matplotlib.cm) not found.") + sys.exit(1) + + names = [ + "Block RThroughput", + "Dispatch Width", + "IPC", + "uOps Per Cycle", + "Instructions", + "Total Cycles", + "Total uOps", + ] + + rows, cols = (len(opts.file_names), 7) + + values = [[0 for x in range(cols)] for y in range(rows)] + + for i in range(len(opts.file_names)): + values[i][0] = array_of_summary[i].block_rthroughput + values[i][1] = array_of_summary[i].dispatch_width + values[i][2] = array_of_summary[i].ipc + values[i][3] = array_of_summary[i].uops_per_cycle + values[i][4] = array_of_summary[i].instructions + values[i][5] = array_of_summary[i].total_cycles + values[i][6] = array_of_summary[i].total_uops + + fig, axs = plt.subplots(4, 2) + fig.suptitle( + "Machine code statistics", fontsize=20, fontweight="bold", color="black" + ) + i = 0 + + for x in range(4): + for y in range(2): + cmap = get_cmap("tab20") + colors = cmap.colors + if not (x == 0 and y == 1) and i < 7: + axs[x][y].grid(True, color="grey", linestyle="--") + maxValue = 0 + if i == 0: + for j in range(len(opts.file_names)): + if maxValue < values[j][i]: + maxValue = values[j][i] + axs[x][y].bar( + 0.3 * j, + values[j][i], + width=0.1, + color=colors[j], + label=get_filename_from_path(opts.file_names[j]), + ) + else: + for j in range(len(opts.file_names)): + if maxValue < values[j][i]: + maxValue = values[j][i] + axs[x][y].bar(0.3 * j, values[j][i], width=0.1, color=colors[j]) + axs[x][y].set_axisbelow(True) + axs[x][y].set_xlim([-0.3, len(opts.file_names) / 3]) + axs[x][y].set_ylim([0, maxValue + (maxValue / 2)]) + axs[x][y].set_title(names[i], fontsize=15, fontweight="bold") + axs[x][y].axes.xaxis.set_visible(False) + for j in range(len(opts.file_names)): + axs[x][y].text( + 0.3 * j, + values[j][i] + (maxValue / 40), + s=str(values[j][i]), + color="black", + fontweight="bold", + fontsize=4, + ) + i = i + 1 + + axs[0][1].set_visible(False) + fig.legend(prop={"size": 15}) + figg = plt.gcf() + figg.set_size_inches((25, 15), forward=False) + if opts.plot_path[0] == "-": + plt.savefig("llvm-mca-plot.png", dpi=500) + print("The plot was saved within llvm-mca-plot.png") + else: + plt.savefig( + os.path.normpath(os.path.join(opts.plot_path[0], "llvm-mca-plot.png")), + dpi=500, + ) + print( + "The plot was saved within {}.".format( + os.path.normpath(os.path.join(opts.plot_path[0], "llvm-mca-plot.png")) + ) + ) + + +# Calculates the average value (summary view) per region. +def summary_average_code_region(array_of_code_regions, file_name): + summary = Summary(file_name, 0, 0, 0, 0, 0, 0, 0, 0, None, None) + for i in range(len(array_of_code_regions)): + summary.block_rthroughput += array_of_code_regions[i].block_rthroughput + summary.dispatch_width += array_of_code_regions[i].dispatch_width + summary.ipc += array_of_code_regions[i].ipc + summary.instructions += array_of_code_regions[i].instructions + summary.iterations += array_of_code_regions[i].iterations + summary.total_cycles += array_of_code_regions[i].total_cycles + summary.total_uops += array_of_code_regions[i].total_uops + summary.uops_per_cycle += array_of_code_regions[i].uops_per_cycle + summary.block_rthroughput = round( + summary.block_rthroughput / len(array_of_code_regions), 2 + ) + summary.dispatch_width = round( + summary.dispatch_width / len(array_of_code_regions), 2 + ) + summary.ipc = round(summary.ipc / len(array_of_code_regions), 2) + summary.instructions = round(summary.instructions / len(array_of_code_regions), 2) + summary.iterations = round(summary.iterations / len(array_of_code_regions), 2) + summary.total_cycles = round(summary.total_cycles / len(array_of_code_regions), 2) + summary.total_uops = round(summary.total_uops / len(array_of_code_regions), 2) + summary.uops_per_cycle = round( + summary.uops_per_cycle / len(array_of_code_regions), 2 + ) + return summary + + +# Based on the obtained results (resource pressure per iter) of llvm-mca tool, draws plots for multiple input files. +def draw_plot_resource_pressure( + array_average_resource_pressure_per_file, opts, name_target_info_resources +): + try: + import matplotlib.pyplot as plt + except ImportError: + print("error: matplotlib.pyplot not found.") + sys.exit(1) + try: + from matplotlib.cm import get_cmap + except ImportError: + print("error: get_cmap (matplotlib.cm) not found.") + sys.exit(1) + + fig, axs = plt.subplots() + fig.suptitle( + "Resource pressure per iterations", + fontsize=20, + fontweight="bold", + color="black", + ) + + maxValue = 0 + for j in range(len(opts.file_names)): + if maxValue < max(array_average_resource_pressure_per_file[j]): + maxValue = max(array_average_resource_pressure_per_file[j]) + + cmap = get_cmap("tab20") + colors = cmap.colors + + xticklabels = [None] * len(opts.file_names) * len(name_target_info_resources) + index = 0 + + for j in range(len(name_target_info_resources)): + for i in range(len(opts.file_names)): + if i == 0: + axs.bar( + j * len(opts.file_names) * 10 + i * 10, + array_average_resource_pressure_per_file[i][j], + width=1, + color=colors[j], + label=name_target_info_resources[j], + ) + else: + axs.bar( + j * len(opts.file_names) * 10 + i * 10, + array_average_resource_pressure_per_file[i][j], + width=1, + color=colors[j], + ) + axs.text( + j * len(opts.file_names) * 10 + i * 10, + array_average_resource_pressure_per_file[i][j] + (maxValue / 40), + s=str(array_average_resource_pressure_per_file[i][j]), + color=colors[j], + fontweight="bold", + fontsize=3, + ) + xticklabels[index] = opts.file_names[i] + index = index + 1 + + axs.set_xticks( + [ + j * len(opts.file_names) * 10 + i * 10 + for j in range(len(name_target_info_resources)) + for i in range(len(opts.file_names)) + ] + ) + axs.set_xticklabels(xticklabels, rotation=65) + + axs.set_axisbelow(True) + axs.set_xlim([-0.5, len(opts.file_names) * len(name_target_info_resources) * 10]) + axs.set_ylim([0, maxValue + maxValue / 10]) + + fig.legend(prop={"size": 15}) + figg = plt.gcf() + figg.set_size_inches((25, 15), forward=False) + if opts.plot_path[0] == "-": + plt.savefig("llvm-mca-plot-resource-pressure.png", dpi=500) + print("The plot was saved within llvm-mca-plot-resource-pressure.png") + else: + plt.savefig( + os.path.normpath( + os.path.join(opts.plot_path[0], "llvm-mca-plot-resource-pressure.png") + ), + dpi=500, + ) + print( + "The plot was saved within {}.".format( + os.path.normpath( + os.path.join( + opts.plot_path[0], "llvm-mca-plot-resource-pressure.png" + ) + ) + ) + ) + + +# Calculates the average value (resource pressure per iter) per region. +def average_code_region_resource_pressure(array_of_code_regions, file_name): + resource_pressure_per_iter_one_file = [0] * len( + array_of_code_regions[0].iteration_resource_pressure + ) + for i in range(len(array_of_code_regions)): + for j in range(len(array_of_code_regions[i].iteration_resource_pressure)): + if array_of_code_regions[i].iteration_resource_pressure[j] != "-": + resource_pressure_per_iter_one_file[j] += float( + array_of_code_regions[i].iteration_resource_pressure[j] + ) + for i in range(len(resource_pressure_per_iter_one_file)): + resource_pressure_per_iter_one_file[i] = round( + resource_pressure_per_iter_one_file[i] / len(array_of_code_regions), 2 + ) + return resource_pressure_per_iter_one_file + + def Main(): parser = argparse.ArgumentParser() opts = parse_program_args(parser) + if not verify_program_inputs(opts): + parser.print_help() + sys.exit(1) + matrix_of_code_regions = [None] * len(opts.file_names) for i in range(len(opts.file_names)): matrix_of_code_regions[i] = run_llvm_mca_tool(opts, opts.file_names[i]) - console_print_results(matrix_of_code_regions, opts) + if not opts.plot and not opts.plot_resource_pressure: + console_print_results(matrix_of_code_regions, opts) + else: + if opts.plot: + array_average_summary_per_file = [None] * len(matrix_of_code_regions) + for j in range(len(matrix_of_code_regions)): + array_average_summary_per_file[j] = summary_average_code_region( + matrix_of_code_regions[j], opts.file_names[j] + ) + draw_plot_files_summary(array_average_summary_per_file, opts) + if opts.plot_resource_pressure: + array_average_resource_pressure_per_file = [None] * len( + matrix_of_code_regions + ) + for j in range(len(matrix_of_code_regions)): + array_average_resource_pressure_per_file[ + j + ] = average_code_region_resource_pressure( + matrix_of_code_regions[j], opts.file_names[j] + ) + draw_plot_resource_pressure( + array_average_resource_pressure_per_file, + opts, + matrix_of_code_regions[0][0].name_target_info_resources, + ) if __name__ == "__main__":