[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / llvm / utils / llvm-mca-compare.py
blobca69bc945af18287f8c6173a6039c60a5d3aa67f
1 #!/usr/bin/env python3
3 import argparse
4 import sys
5 import os
6 from json import loads
7 from subprocess import Popen, PIPE
9 # Holds code regions statistics.
10 class 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,
25 self.name = name
26 self.block_rthroughput = block_rthroughput
27 self.dispatch_width = dispatch_width
28 self.ipc = ipc
29 self.instructions = instructions
30 self.iterations = iterations
31 self.total_cycles = total_cycles
32 self.total_uops = total_uops
33 self.uops_per_cycle = uops_per_cycle
34 self.iteration_resource_pressure = iteration_resource_pressure
35 self.name_target_info_resources = name_target_info_resources
38 # Parse the program arguments.
39 def 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.",
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.",
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.",
64 parser.add_argument(
65 "-plot",
66 action="store_true",
67 default=False,
68 help="Draw plots of statistics for input files.",
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.",
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.",
85 parser.add_argument(
86 "-v",
87 action="store_true",
88 default=False,
89 help="More details about the running lvm-mca tool.",
91 return parser.parse_args()
94 # Verify that the program inputs meet the requirements.
95 def 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."
100 return False
102 return True
105 # Returns the name of the file to be analyzed from the path it is on.
106 def get_filename_from_path(path):
107 index_of_slash = path.rfind("/")
108 return path[(index_of_slash + 1) : len(path)]
111 # Returns the results of the running llvm-mca tool for the input file.
112 def 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]
116 # The statistics llvm-mca options.
117 if opts.args[0] != "-":
118 llvm_mca_cmd += " " + opts.args[0]
119 llvm_mca_cmd += " -json"
121 # Set file which llvm-mca tool will process.
122 llvm_mca_cmd += " " + file_name
124 if opts.v:
125 print("run: $ " + llvm_mca_cmd + "\n")
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,
136 cmd_stdout, cmd_stderr = subproc.communicate()
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)
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")
152 code_regions_len = len(json_parsed["CodeRegions"])
153 array_of_code_regions = [None] * code_regions_len
155 for i in range(code_regions_len):
156 code_region_instructions_len = len(
157 json_parsed["CodeRegions"][i]["Instructions"]
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"
165 name_target_info_resources = json_parsed["TargetInfo"]["Resources"]
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_len
173 iteration_resource_pressure[
174 obj_of_resource_pressure_info["ResourceIndex"]
175 ] = str(round(obj_of_resource_pressure_info["ResourceUsage"], 2))
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,
191 return array_of_code_regions
194 # Print statistics in console for single file or for multiple files.
195 def console_print_results(matrix_of_code_regions, opts):
196 try:
197 import termtables as tt
198 except ImportError:
199 print("error: termtables not found.")
200 sys.exit(1)
202 headers_names = [None] * (len(opts.file_names) + 1)
203 headers_names[0] = " "
205 max_code_regions = 0
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) + "]: "
214 print("\nITERATIONS: " + str(matrix_of_code_regions[0][0].iterations) + "\n")
216 for i in range(max_code_regions):
218 print(
219 "\n-----------------------------------------\nCode region: "
220 + str(i + 1)
221 + "\n"
224 table_values = [
225 [[None] for i in range(len(matrix_of_code_regions) + 1)] for j in range(7)
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: "
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_width
244 table_values[4][j + 1] = str(
245 round(matrix_of_code_regions[j][i].uops_per_cycle, 2)
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)
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] = "-"
260 tt.print(
261 table_values,
262 header=headers_names,
263 style=tt.styles.ascii_thin_double,
264 padding=(0, 1),
267 print("\nResource pressure per iteration: \n")
269 table_values = [
271 [None]
272 for i in range(
273 len(matrix_of_code_regions[0][0].iteration_resource_pressure) + 1
276 for j in range(len(matrix_of_code_regions) + 1)
279 table_values[0] = [" "] + matrix_of_code_regions[0][
281 ].name_target_info_resources
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_pressure
288 else:
289 table_values[j + 1] = ["[f" + str(j + 1) + "]: "] + len(
290 matrix_of_code_regions[0][0].iteration_resource_pressure
291 ) * ["-"]
293 tt.print(
294 table_values,
295 style=tt.styles.ascii_thin_double,
296 padding=(0, 1),
298 print("\n")
301 # Based on the obtained results (summary view) of llvm-mca tool, draws plots for multiple input files.
302 def draw_plot_files_summary(array_of_summary, opts):
303 try:
304 import matplotlib.pyplot as plt
305 except ImportError:
306 print("error: matplotlib.pyplot not found.")
307 sys.exit(1)
308 try:
309 from matplotlib.cm import get_cmap
310 except ImportError:
311 print("error: get_cmap (matplotlib.cm) not found.")
312 sys.exit(1)
314 names = [
315 "Block RThroughput",
316 "Dispatch Width",
317 "IPC",
318 "uOps Per Cycle",
319 "Instructions",
320 "Total Cycles",
321 "Total uOps",
324 rows, cols = (len(opts.file_names), 7)
326 values = [[0 for x in range(cols)] for y in range(rows)]
328 for i in range(len(opts.file_names)):
329 values[i][0] = array_of_summary[i].block_rthroughput
330 values[i][1] = array_of_summary[i].dispatch_width
331 values[i][2] = array_of_summary[i].ipc
332 values[i][3] = array_of_summary[i].uops_per_cycle
333 values[i][4] = array_of_summary[i].instructions
334 values[i][5] = array_of_summary[i].total_cycles
335 values[i][6] = array_of_summary[i].total_uops
337 fig, axs = plt.subplots(4, 2)
338 fig.suptitle(
339 "Machine code statistics", fontsize=20, fontweight="bold", color="black"
341 i = 0
343 for x in range(4):
344 for y in range(2):
345 cmap = get_cmap("tab20")
346 colors = cmap.colors
347 if not (x == 0 and y == 1) and i < 7:
348 axs[x][y].grid(True, color="grey", linestyle="--")
349 maxValue = 0
350 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]),
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,
380 i = i + 1
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,
394 print(
395 "The plot was saved within {}.".format(
396 os.path.normpath(os.path.join(opts.plot_path[0], "llvm-mca-plot.png"))
401 # Calculates the average value (summary view) per region.
402 def 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_rthroughput
406 summary.dispatch_width += array_of_code_regions[i].dispatch_width
407 summary.ipc += array_of_code_regions[i].ipc
408 summary.instructions += array_of_code_regions[i].instructions
409 summary.iterations += array_of_code_regions[i].iterations
410 summary.total_cycles += array_of_code_regions[i].total_cycles
411 summary.total_uops += array_of_code_regions[i].total_uops
412 summary.uops_per_cycle += array_of_code_regions[i].uops_per_cycle
413 summary.block_rthroughput = round(
414 summary.block_rthroughput / len(array_of_code_regions), 2
416 summary.dispatch_width = round(
417 summary.dispatch_width / len(array_of_code_regions), 2
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), 2
427 return summary
430 # Based on the obtained results (resource pressure per iter) of llvm-mca tool, draws plots for multiple input files.
431 def draw_plot_resource_pressure(
432 array_average_resource_pressure_per_file, opts, name_target_info_resources
434 try:
435 import matplotlib.pyplot as plt
436 except ImportError:
437 print("error: matplotlib.pyplot not found.")
438 sys.exit(1)
439 try:
440 from matplotlib.cm import get_cmap
441 except ImportError:
442 print("error: get_cmap (matplotlib.cm) not found.")
443 sys.exit(1)
445 fig, axs = plt.subplots()
446 fig.suptitle(
447 "Resource pressure per iterations",
448 fontsize=20,
449 fontweight="bold",
450 color="black",
453 maxValue = 0
454 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])
458 cmap = get_cmap("tab20")
459 colors = cmap.colors
461 xticklabels = [None] * len(opts.file_names) * len(name_target_info_resources)
462 index = 0
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],
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],
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,
489 xticklabels[index] = opts.file_names[i]
490 index = index + 1
492 axs.set_xticks(
494 j * len(opts.file_names) * 10 + i * 10
495 for j in range(len(name_target_info_resources))
496 for i in range(len(opts.file_names))
499 axs.set_xticklabels(xticklabels, rotation=65)
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])
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")
516 dpi=500,
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"
529 # Calculates the average value (resource pressure per iter) per region.
530 def 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_pressure
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]
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), 2
544 return resource_pressure_per_iter_one_file
547 def Main():
548 parser = argparse.ArgumentParser()
549 opts = parse_program_args(parser)
551 if not verify_program_inputs(opts):
552 parser.print_help()
553 sys.exit(1)
555 matrix_of_code_regions = [None] * len(opts.file_names)
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]
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_regions
573 for j in range(len(matrix_of_code_regions)):
574 array_average_resource_pressure_per_file[
576 ] = average_code_region_resource_pressure(
577 matrix_of_code_regions[j], opts.file_names[j]
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,
586 if __name__ == "__main__":
587 Main()
588 sys.exit(0)