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