589 lines · python
1#!/usr/bin/env python32 3import argparse4import sys5import os6from json import loads7from subprocess import Popen, PIPE8 9# Holds code regions statistics.10class Summary:11 def __init__(12 self,13 name,14 block_rthroughput,15 dispatch_width,16 ipc,17 instructions,18 iterations,19 total_cycles,20 total_uops,21 uops_per_cycle,22 iteration_resource_pressure,23 name_target_info_resources,24 ):25 self.name = name26 self.block_rthroughput = block_rthroughput27 self.dispatch_width = dispatch_width28 self.ipc = ipc29 self.instructions = instructions30 self.iterations = iterations31 self.total_cycles = total_cycles32 self.total_uops = total_uops33 self.uops_per_cycle = uops_per_cycle34 self.iteration_resource_pressure = iteration_resource_pressure35 self.name_target_info_resources = name_target_info_resources36 37 38# Parse the program arguments.39def parse_program_args(parser):40 parser.add_argument(41 "file_names",42 nargs="+",43 type=str,44 help="Names of files which llvm-mca tool process.",45 )46 parser.add_argument(47 "--llvm-mca-binary",48 nargs=1,49 required=True,50 type=str,51 action="store",52 metavar="[=<path to llvm-mca>]",53 help="Specified relative path to binary of llvm-mca.",54 )55 parser.add_argument(56 "--args",57 nargs=1,58 type=str,59 action="store",60 metavar="[='-option1=<arg> -option2=<arg> ...']",61 default=["-"],62 help="Forward options to lvm-mca tool.",63 )64 parser.add_argument(65 "-plot",66 action="store_true",67 default=False,68 help="Draw plots of statistics for input files.",69 )70 parser.add_argument(71 "-plot-resource-pressure",72 action="store_true",73 default=False,74 help="Draw plots of resource pressure per iterations for input files.",75 )76 parser.add_argument(77 "--plot-path",78 nargs=1,79 type=str,80 action="store",81 metavar="[=<path>]",82 default=["-"],83 help="Specify relative path where you want to save the plots.",84 )85 parser.add_argument(86 "-v",87 action="store_true",88 default=False,89 help="More details about the running lvm-mca tool.",90 )91 return parser.parse_args()92 93 94# Verify that the program inputs meet the requirements.95def verify_program_inputs(opts):96 if opts.plot_path[0] != "-" and not opts.plot and not opts.plot_resource_pressure:97 print(98 "error: Please specify --plot-path only with the -plot or -plot-resource-pressure options."99 )100 return False101 102 return True103 104 105# Returns the name of the file to be analyzed from the path it is on.106def get_filename_from_path(path):107 index_of_slash = path.rfind("/")108 return path[(index_of_slash + 1) : len(path)]109 110 111# Returns the results of the running llvm-mca tool for the input file.112def run_llvm_mca_tool(opts, file_name):113 # Get the path of the llvm-mca binary file.114 llvm_mca_cmd = opts.llvm_mca_binary[0]115 116 # The statistics llvm-mca options.117 if opts.args[0] != "-":118 llvm_mca_cmd += " " + opts.args[0]119 llvm_mca_cmd += " -json"120 121 # Set file which llvm-mca tool will process.122 llvm_mca_cmd += " " + file_name123 124 if opts.v:125 print("run: $ " + llvm_mca_cmd + "\n")126 127 # Generate the stats with the llvm-mca.128 subproc = Popen(129 llvm_mca_cmd.split(" "),130 stdin=PIPE,131 stdout=PIPE,132 stderr=PIPE,133 universal_newlines=True,134 )135 136 cmd_stdout, cmd_stderr = subproc.communicate()137 138 try:139 json_parsed = loads(cmd_stdout)140 except:141 print("error: No valid llvm-mca statistics found.")142 print(cmd_stderr)143 sys.exit(1)144 145 if opts.v:146 print("Simulation Parameters: ")147 simulation_parameters = json_parsed["SimulationParameters"]148 for key in simulation_parameters:149 print(key, ":", simulation_parameters[key])150 print("\n")151 152 code_regions_len = len(json_parsed["CodeRegions"])153 array_of_code_regions = [None] * code_regions_len154 155 for i in range(code_regions_len):156 code_region_instructions_len = len(157 json_parsed["CodeRegions"][i]["Instructions"]158 )159 target_info_resources_len = len(json_parsed["TargetInfo"]["Resources"])160 iteration_resource_pressure = ["-" for k in range(target_info_resources_len)]161 resource_pressure_info = json_parsed["CodeRegions"][i]["ResourcePressureView"][162 "ResourcePressureInfo"163 ]164 165 name_target_info_resources = json_parsed["TargetInfo"]["Resources"]166 167 for s in range(len(resource_pressure_info)):168 obj_of_resource_pressure_info = resource_pressure_info[s]169 if (170 obj_of_resource_pressure_info["InstructionIndex"]171 == code_region_instructions_len172 ):173 iteration_resource_pressure[174 obj_of_resource_pressure_info["ResourceIndex"]175 ] = str(round(obj_of_resource_pressure_info["ResourceUsage"], 2))176 177 array_of_code_regions[i] = Summary(178 file_name,179 json_parsed["CodeRegions"][i]["SummaryView"]["BlockRThroughput"],180 json_parsed["CodeRegions"][i]["SummaryView"]["DispatchWidth"],181 json_parsed["CodeRegions"][i]["SummaryView"]["IPC"],182 json_parsed["CodeRegions"][i]["SummaryView"]["Instructions"],183 json_parsed["CodeRegions"][i]["SummaryView"]["Iterations"],184 json_parsed["CodeRegions"][i]["SummaryView"]["TotalCycles"],185 json_parsed["CodeRegions"][i]["SummaryView"]["TotaluOps"],186 json_parsed["CodeRegions"][i]["SummaryView"]["uOpsPerCycle"],187 iteration_resource_pressure,188 name_target_info_resources,189 )190 191 return array_of_code_regions192 193 194# Print statistics in console for single file or for multiple files.195def console_print_results(matrix_of_code_regions, opts):196 try:197 import termtables as tt198 except ImportError:199 print("error: termtables not found.")200 sys.exit(1)201 202 headers_names = [None] * (len(opts.file_names) + 1)203 headers_names[0] = " "204 205 max_code_regions = 0206 207 print("Input files:")208 for i in range(len(matrix_of_code_regions)):209 if max_code_regions < len(matrix_of_code_regions[i]):210 max_code_regions = len(matrix_of_code_regions[i])211 print("[f" + str(i + 1) + "]: " + get_filename_from_path(opts.file_names[i]))212 headers_names[i + 1] = "[f" + str(i + 1) + "]: "213 214 print("\nITERATIONS: " + str(matrix_of_code_regions[0][0].iterations) + "\n")215 216 for i in range(max_code_regions):217 218 print(219 "\n-----------------------------------------\nCode region: "220 + str(i + 1)221 + "\n"222 )223 224 table_values = [225 [[None] for i in range(len(matrix_of_code_regions) + 1)] for j in range(7)226 ]227 228 table_values[0][0] = "Instructions: "229 table_values[1][0] = "Total Cycles: "230 table_values[2][0] = "Total uOps: "231 table_values[3][0] = "Dispatch Width: "232 table_values[4][0] = "uOps Per Cycle: "233 table_values[5][0] = "IPC: "234 table_values[6][0] = "Block RThroughput: "235 236 for j in range(len(matrix_of_code_regions)):237 if len(matrix_of_code_regions[j]) > i:238 table_values[0][j + 1] = str(matrix_of_code_regions[j][i].instructions)239 table_values[1][j + 1] = str(matrix_of_code_regions[j][i].total_cycles)240 table_values[2][j + 1] = str(matrix_of_code_regions[j][i].total_uops)241 table_values[3][j + 1] = str(242 matrix_of_code_regions[j][i].dispatch_width243 )244 table_values[4][j + 1] = str(245 round(matrix_of_code_regions[j][i].uops_per_cycle, 2)246 )247 table_values[5][j + 1] = str(round(matrix_of_code_regions[j][i].ipc, 2))248 table_values[6][j + 1] = str(249 round(matrix_of_code_regions[j][i].block_rthroughput, 2)250 )251 else:252 table_values[0][j + 1] = "-"253 table_values[1][j + 1] = "-"254 table_values[2][j + 1] = "-"255 table_values[3][j + 1] = "-"256 table_values[4][j + 1] = "-"257 table_values[5][j + 1] = "-"258 table_values[6][j + 1] = "-"259 260 tt.print(261 table_values,262 header=headers_names,263 style=tt.styles.ascii_thin_double,264 padding=(0, 1),265 )266 267 print("\nResource pressure per iteration: \n")268 269 table_values = [270 [271 [None]272 for i in range(273 len(matrix_of_code_regions[0][0].iteration_resource_pressure) + 1274 )275 ]276 for j in range(len(matrix_of_code_regions) + 1)277 ]278 279 table_values[0] = [" "] + matrix_of_code_regions[0][280 0281 ].name_target_info_resources282 283 for j in range(len(matrix_of_code_regions)):284 if len(matrix_of_code_regions[j]) > i:285 table_values[j + 1] = [286 "[f" + str(j + 1) + "]: "287 ] + matrix_of_code_regions[j][i].iteration_resource_pressure288 else:289 table_values[j + 1] = ["[f" + str(j + 1) + "]: "] + len(290 matrix_of_code_regions[0][0].iteration_resource_pressure291 ) * ["-"]292 293 tt.print(294 table_values,295 style=tt.styles.ascii_thin_double,296 padding=(0, 1),297 )298 print("\n")299 300 301# Based on the obtained results (summary view) of llvm-mca tool, draws plots for multiple input files.302def draw_plot_files_summary(array_of_summary, opts):303 try:304 import matplotlib.pyplot as plt305 except ImportError:306 print("error: matplotlib.pyplot not found.")307 sys.exit(1)308 try:309 from matplotlib.cm import get_cmap310 except ImportError:311 print("error: get_cmap (matplotlib.cm) not found.")312 sys.exit(1)313 314 names = [315 "Block RThroughput",316 "Dispatch Width",317 "IPC",318 "uOps Per Cycle",319 "Instructions",320 "Total Cycles",321 "Total uOps",322 ]323 324 rows, cols = (len(opts.file_names), 7)325 326 values = [[0 for x in range(cols)] for y in range(rows)]327 328 for i in range(len(opts.file_names)):329 values[i][0] = array_of_summary[i].block_rthroughput330 values[i][1] = array_of_summary[i].dispatch_width331 values[i][2] = array_of_summary[i].ipc332 values[i][3] = array_of_summary[i].uops_per_cycle333 values[i][4] = array_of_summary[i].instructions334 values[i][5] = array_of_summary[i].total_cycles335 values[i][6] = array_of_summary[i].total_uops336 337 fig, axs = plt.subplots(4, 2)338 fig.suptitle(339 "Machine code statistics", fontsize=20, fontweight="bold", color="black"340 )341 i = 0342 343 for x in range(4):344 for y in range(2):345 cmap = get_cmap("tab20")346 colors = cmap.colors347 if not (x == 0 and y == 1) and i < 7:348 axs[x][y].grid(True, color="grey", linestyle="--")349 maxValue = 0350 if i == 0:351 for j in range(len(opts.file_names)):352 if maxValue < values[j][i]:353 maxValue = values[j][i]354 axs[x][y].bar(355 0.3 * j,356 values[j][i],357 width=0.1,358 color=colors[j],359 label=get_filename_from_path(opts.file_names[j]),360 )361 else:362 for j in range(len(opts.file_names)):363 if maxValue < values[j][i]:364 maxValue = values[j][i]365 axs[x][y].bar(0.3 * j, values[j][i], width=0.1, color=colors[j])366 axs[x][y].set_axisbelow(True)367 axs[x][y].set_xlim([-0.3, len(opts.file_names) / 3])368 axs[x][y].set_ylim([0, maxValue + (maxValue / 2)])369 axs[x][y].set_title(names[i], fontsize=15, fontweight="bold")370 axs[x][y].axes.xaxis.set_visible(False)371 for j in range(len(opts.file_names)):372 axs[x][y].text(373 0.3 * j,374 values[j][i] + (maxValue / 40),375 s=str(values[j][i]),376 color="black",377 fontweight="bold",378 fontsize=4,379 )380 i = i + 1381 382 axs[0][1].set_visible(False)383 fig.legend(prop={"size": 15})384 figg = plt.gcf()385 figg.set_size_inches((25, 15), forward=False)386 if opts.plot_path[0] == "-":387 plt.savefig("llvm-mca-plot.png", dpi=500)388 print("The plot was saved within llvm-mca-plot.png")389 else:390 plt.savefig(391 os.path.normpath(os.path.join(opts.plot_path[0], "llvm-mca-plot.png")),392 dpi=500,393 )394 print(395 "The plot was saved within {}.".format(396 os.path.normpath(os.path.join(opts.plot_path[0], "llvm-mca-plot.png"))397 )398 )399 400 401# Calculates the average value (summary view) per region.402def summary_average_code_region(array_of_code_regions, file_name):403 summary = Summary(file_name, 0, 0, 0, 0, 0, 0, 0, 0, None, None)404 for i in range(len(array_of_code_regions)):405 summary.block_rthroughput += array_of_code_regions[i].block_rthroughput406 summary.dispatch_width += array_of_code_regions[i].dispatch_width407 summary.ipc += array_of_code_regions[i].ipc408 summary.instructions += array_of_code_regions[i].instructions409 summary.iterations += array_of_code_regions[i].iterations410 summary.total_cycles += array_of_code_regions[i].total_cycles411 summary.total_uops += array_of_code_regions[i].total_uops412 summary.uops_per_cycle += array_of_code_regions[i].uops_per_cycle413 summary.block_rthroughput = round(414 summary.block_rthroughput / len(array_of_code_regions), 2415 )416 summary.dispatch_width = round(417 summary.dispatch_width / len(array_of_code_regions), 2418 )419 summary.ipc = round(summary.ipc / len(array_of_code_regions), 2)420 summary.instructions = round(summary.instructions / len(array_of_code_regions), 2)421 summary.iterations = round(summary.iterations / len(array_of_code_regions), 2)422 summary.total_cycles = round(summary.total_cycles / len(array_of_code_regions), 2)423 summary.total_uops = round(summary.total_uops / len(array_of_code_regions), 2)424 summary.uops_per_cycle = round(425 summary.uops_per_cycle / len(array_of_code_regions), 2426 )427 return summary428 429 430# Based on the obtained results (resource pressure per iter) of llvm-mca tool, draws plots for multiple input files.431def draw_plot_resource_pressure(432 array_average_resource_pressure_per_file, opts, name_target_info_resources433):434 try:435 import matplotlib.pyplot as plt436 except ImportError:437 print("error: matplotlib.pyplot not found.")438 sys.exit(1)439 try:440 from matplotlib.cm import get_cmap441 except ImportError:442 print("error: get_cmap (matplotlib.cm) not found.")443 sys.exit(1)444 445 fig, axs = plt.subplots()446 fig.suptitle(447 "Resource pressure per iterations",448 fontsize=20,449 fontweight="bold",450 color="black",451 )452 453 maxValue = 0454 for j in range(len(opts.file_names)):455 if maxValue < max(array_average_resource_pressure_per_file[j]):456 maxValue = max(array_average_resource_pressure_per_file[j])457 458 cmap = get_cmap("tab20")459 colors = cmap.colors460 461 xticklabels = [None] * len(opts.file_names) * len(name_target_info_resources)462 index = 0463 464 for j in range(len(name_target_info_resources)):465 for i in range(len(opts.file_names)):466 if i == 0:467 axs.bar(468 j * len(opts.file_names) * 10 + i * 10,469 array_average_resource_pressure_per_file[i][j],470 width=1,471 color=colors[j],472 label=name_target_info_resources[j],473 )474 else:475 axs.bar(476 j * len(opts.file_names) * 10 + i * 10,477 array_average_resource_pressure_per_file[i][j],478 width=1,479 color=colors[j],480 )481 axs.text(482 j * len(opts.file_names) * 10 + i * 10,483 array_average_resource_pressure_per_file[i][j] + (maxValue / 40),484 s=str(array_average_resource_pressure_per_file[i][j]),485 color=colors[j],486 fontweight="bold",487 fontsize=3,488 )489 xticklabels[index] = opts.file_names[i]490 index = index + 1491 492 axs.set_xticks(493 [494 j * len(opts.file_names) * 10 + i * 10495 for j in range(len(name_target_info_resources))496 for i in range(len(opts.file_names))497 ]498 )499 axs.set_xticklabels(xticklabels, rotation=65)500 501 axs.set_axisbelow(True)502 axs.set_xlim([-0.5, len(opts.file_names) * len(name_target_info_resources) * 10])503 axs.set_ylim([0, maxValue + maxValue / 10])504 505 fig.legend(prop={"size": 15})506 figg = plt.gcf()507 figg.set_size_inches((25, 15), forward=False)508 if opts.plot_path[0] == "-":509 plt.savefig("llvm-mca-plot-resource-pressure.png", dpi=500)510 print("The plot was saved within llvm-mca-plot-resource-pressure.png")511 else:512 plt.savefig(513 os.path.normpath(514 os.path.join(opts.plot_path[0], "llvm-mca-plot-resource-pressure.png")515 ),516 dpi=500,517 )518 print(519 "The plot was saved within {}.".format(520 os.path.normpath(521 os.path.join(522 opts.plot_path[0], "llvm-mca-plot-resource-pressure.png"523 )524 )525 )526 )527 528 529# Calculates the average value (resource pressure per iter) per region.530def average_code_region_resource_pressure(array_of_code_regions, file_name):531 resource_pressure_per_iter_one_file = [0] * len(532 array_of_code_regions[0].iteration_resource_pressure533 )534 for i in range(len(array_of_code_regions)):535 for j in range(len(array_of_code_regions[i].iteration_resource_pressure)):536 if array_of_code_regions[i].iteration_resource_pressure[j] != "-":537 resource_pressure_per_iter_one_file[j] += float(538 array_of_code_regions[i].iteration_resource_pressure[j]539 )540 for i in range(len(resource_pressure_per_iter_one_file)):541 resource_pressure_per_iter_one_file[i] = round(542 resource_pressure_per_iter_one_file[i] / len(array_of_code_regions), 2543 )544 return resource_pressure_per_iter_one_file545 546 547def Main():548 parser = argparse.ArgumentParser()549 opts = parse_program_args(parser)550 551 if not verify_program_inputs(opts):552 parser.print_help()553 sys.exit(1)554 555 matrix_of_code_regions = [None] * len(opts.file_names)556 557 for i in range(len(opts.file_names)):558 matrix_of_code_regions[i] = run_llvm_mca_tool(opts, opts.file_names[i])559 if not opts.plot and not opts.plot_resource_pressure:560 console_print_results(matrix_of_code_regions, opts)561 else:562 if opts.plot:563 array_average_summary_per_file = [None] * len(matrix_of_code_regions)564 for j in range(len(matrix_of_code_regions)):565 array_average_summary_per_file[j] = summary_average_code_region(566 matrix_of_code_regions[j], opts.file_names[j]567 )568 draw_plot_files_summary(array_average_summary_per_file, opts)569 if opts.plot_resource_pressure:570 array_average_resource_pressure_per_file = [None] * len(571 matrix_of_code_regions572 )573 for j in range(len(matrix_of_code_regions)):574 array_average_resource_pressure_per_file[575 j576 ] = average_code_region_resource_pressure(577 matrix_of_code_regions[j], opts.file_names[j]578 )579 draw_plot_resource_pressure(580 array_average_resource_pressure_per_file,581 opts,582 matrix_of_code_regions[0][0].name_target_info_resources,583 )584 585 586if __name__ == "__main__":587 Main()588 sys.exit(0)589