mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2026-01-05 02:30:08 +01:00
added documentation
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Frontend interface for OSACA. Does everything necessary for printing analysis to the terminal.
|
||||||
|
"""
|
||||||
import re
|
import re
|
||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
|
|
||||||
@@ -8,6 +10,16 @@ from osaca.semantics import INSTR_FLAGS, ArchSemantics, KernelDG, MachineModel
|
|||||||
|
|
||||||
class Frontend(object):
|
class Frontend(object):
|
||||||
def __init__(self, filename='', arch=None, path_to_yaml=None):
|
def __init__(self, filename='', arch=None, path_to_yaml=None):
|
||||||
|
"""
|
||||||
|
Constructor method.
|
||||||
|
|
||||||
|
:param filename: path to the analyzed kernel file for documentation, defaults to ''
|
||||||
|
:type filename: str, optional
|
||||||
|
:param arch: micro-arch code for getting the machine model, defaults to None
|
||||||
|
:type arch: str, optional
|
||||||
|
:param path_to_yaml: path to the YAML file for getting the machine model, defaults to None
|
||||||
|
:type path_to_yaml: str, optional
|
||||||
|
"""
|
||||||
self._filename = filename
|
self._filename = filename
|
||||||
if not arch and not path_to_yaml:
|
if not arch and not path_to_yaml:
|
||||||
raise ValueError('Either arch or path_to_yaml required.')
|
raise ValueError('Either arch or path_to_yaml required.')
|
||||||
@@ -22,9 +34,25 @@ class Frontend(object):
|
|||||||
self._arch = self._machine_model.get_arch()
|
self._arch = self._machine_model.get_arch()
|
||||||
|
|
||||||
def _is_comment(self, instruction_form):
|
def _is_comment(self, instruction_form):
|
||||||
|
"""
|
||||||
|
Checks if instruction form is a comment-only line.
|
||||||
|
|
||||||
|
:param instruction_form: instruction form as dict
|
||||||
|
:returns: `True` if comment line, `False` otherwise
|
||||||
|
"""
|
||||||
return instruction_form['comment'] is not None and instruction_form['instruction'] is None
|
return instruction_form['comment'] is not None and instruction_form['instruction'] is None
|
||||||
|
|
||||||
def print_throughput_analysis(self, kernel, show_lineno=False, show_cmnts=True):
|
def print_throughput_analysis(self, kernel, show_lineno=False, show_cmnts=True):
|
||||||
|
"""
|
||||||
|
Print throughput analysis only.
|
||||||
|
|
||||||
|
:param kernel: Kernel to print throughput analysis for.
|
||||||
|
:type kernel: list
|
||||||
|
:param show_lineno: flag for showing the line number of instructions, defaults to `False`
|
||||||
|
:type show_lineno: bool, optional
|
||||||
|
:param show_cmnts: flag for showing comment-only lines in kernel, defaults to `True`
|
||||||
|
:type show_cmnts: bool, optional
|
||||||
|
"""
|
||||||
lineno_filler = ' ' if show_lineno else ''
|
lineno_filler = ' ' if show_lineno else ''
|
||||||
port_len = self._get_max_port_len(kernel)
|
port_len = self._get_max_port_len(kernel)
|
||||||
separator = '-' * sum([x + 3 for x in port_len]) + '-'
|
separator = '-' * sum([x + 3 for x in port_len]) + '-'
|
||||||
@@ -58,6 +86,14 @@ class Frontend(object):
|
|||||||
print(lineno_filler + self._get_port_pressure(tp_sum, port_len, separator=' '))
|
print(lineno_filler + self._get_port_pressure(tp_sum, port_len, separator=' '))
|
||||||
|
|
||||||
def print_latency_analysis(self, cp_kernel, separator='|'):
|
def print_latency_analysis(self, cp_kernel, separator='|'):
|
||||||
|
"""
|
||||||
|
Print a list-based CP analysis to the terminal.
|
||||||
|
|
||||||
|
:param cp_kernel: loop kernel containing the CP information for each instruction form
|
||||||
|
:type cp_kernel: list
|
||||||
|
:separator: separator symbol for the columns, defaults to '|'
|
||||||
|
:type separator: str, optional
|
||||||
|
"""
|
||||||
print('\n\nLatency Analysis Report\n' + '-----------------------')
|
print('\n\nLatency Analysis Report\n' + '-----------------------')
|
||||||
for instruction_form in cp_kernel:
|
for instruction_form in cp_kernel:
|
||||||
print(
|
print(
|
||||||
@@ -80,6 +116,14 @@ class Frontend(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def print_loopcarried_dependencies(self, dep_dict, separator='|'):
|
def print_loopcarried_dependencies(self, dep_dict, separator='|'):
|
||||||
|
"""
|
||||||
|
Print a list-based LCD analysis to the terminal.
|
||||||
|
|
||||||
|
:param dep_dict: dictionary with first instruction in LCD as key and the deps as value
|
||||||
|
:type dep_dict: dict
|
||||||
|
:separator: separator symbol for the columns, defaults to '|'
|
||||||
|
:type separator: str, optional
|
||||||
|
"""
|
||||||
print(
|
print(
|
||||||
'\n\nLoop-Carried Dependencies Analysis Report\n'
|
'\n\nLoop-Carried Dependencies Analysis Report\n'
|
||||||
+ '-----------------------------------------'
|
+ '-----------------------------------------'
|
||||||
@@ -101,6 +145,17 @@ class Frontend(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def print_full_analysis(self, kernel, kernel_dg: KernelDG, verbose=False):
|
def print_full_analysis(self, kernel, kernel_dg: KernelDG, verbose=False):
|
||||||
|
"""
|
||||||
|
Prints the full analysis report including header, the symbol map, the combined TP/CP/LCD
|
||||||
|
view and the list based LCD view.
|
||||||
|
|
||||||
|
:param kernel: kernel to print
|
||||||
|
:type kernel: list
|
||||||
|
:param kernel_dg: directed graph containing CP and LCD
|
||||||
|
:type kernel_dg: :class:`~osaca.semantics.KernelDG`
|
||||||
|
:param verbose: verbose output flag, defaults to `False`
|
||||||
|
:type verbose: bool, optional
|
||||||
|
"""
|
||||||
self._print_header_report()
|
self._print_header_report()
|
||||||
self._print_symbol_map()
|
self._print_symbol_map()
|
||||||
self.print_combined_view(
|
self.print_combined_view(
|
||||||
@@ -109,6 +164,19 @@ class Frontend(object):
|
|||||||
self.print_loopcarried_dependencies(kernel_dg.get_loopcarried_dependencies())
|
self.print_loopcarried_dependencies(kernel_dg.get_loopcarried_dependencies())
|
||||||
|
|
||||||
def print_combined_view(self, kernel, cp_kernel: KernelDG, dep_dict, show_cmnts=True):
|
def print_combined_view(self, kernel, cp_kernel: KernelDG, dep_dict, show_cmnts=True):
|
||||||
|
"""
|
||||||
|
Prints the combined view of the kernel including the port pressure (TP), a CP column and a
|
||||||
|
LCD column.
|
||||||
|
|
||||||
|
:param kernel: kernel to print
|
||||||
|
:type kernel: list
|
||||||
|
:param kernel_dg: directed graph containing CP and LCD
|
||||||
|
:type kernel_dg: :class:`~osaca.semantics.KernelDG`
|
||||||
|
:param dep_dict: dictionary with first instruction in LCD as key and the deps as value
|
||||||
|
:type dep_dict: dict
|
||||||
|
:param show_cmnts: flag for showing comment-only lines in kernel, defaults to `True`
|
||||||
|
:type show_cmnts: bool, optional
|
||||||
|
"""
|
||||||
print('\n\nCombined Analysis Report\n' + '------------------------')
|
print('\n\nCombined Analysis Report\n' + '------------------------')
|
||||||
lineno_filler = ' '
|
lineno_filler = ' '
|
||||||
port_len = self._get_max_port_len(kernel)
|
port_len = self._get_max_port_len(kernel)
|
||||||
@@ -178,6 +246,7 @@ class Frontend(object):
|
|||||||
####################
|
####################
|
||||||
|
|
||||||
def _get_separator_list(self, separator, separator_2=' '):
|
def _get_separator_list(self, separator, separator_2=' '):
|
||||||
|
"""Creates column view for seperators in the TP/combined view."""
|
||||||
separator_list = []
|
separator_list = []
|
||||||
for i in range(len(self._machine_model.get_ports()) - 1):
|
for i in range(len(self._machine_model.get_ports()) - 1):
|
||||||
match_1 = re.search(r'\d+', self._machine_model.get_ports()[i])
|
match_1 = re.search(r'\d+', self._machine_model.get_ports()[i])
|
||||||
@@ -190,6 +259,7 @@ class Frontend(object):
|
|||||||
return separator_list
|
return separator_list
|
||||||
|
|
||||||
def _get_flag_symbols(self, flag_obj):
|
def _get_flag_symbols(self, flag_obj):
|
||||||
|
"""Returns flags for a flag object of an instruction"""
|
||||||
string_result = ''
|
string_result = ''
|
||||||
string_result += '*' if INSTR_FLAGS.NOT_BOUND in flag_obj else ''
|
string_result += '*' if INSTR_FLAGS.NOT_BOUND in flag_obj else ''
|
||||||
string_result += 'X' if INSTR_FLAGS.TP_UNKWN in flag_obj else ''
|
string_result += 'X' if INSTR_FLAGS.TP_UNKWN in flag_obj else ''
|
||||||
@@ -199,6 +269,7 @@ class Frontend(object):
|
|||||||
return string_result
|
return string_result
|
||||||
|
|
||||||
def _get_port_pressure(self, ports, port_len, used_ports=[], separator='|'):
|
def _get_port_pressure(self, ports, port_len, used_ports=[], separator='|'):
|
||||||
|
"""Returns line of port pressure for an instruction."""
|
||||||
if not isinstance(separator, list):
|
if not isinstance(separator, list):
|
||||||
separator = [separator for x in ports]
|
separator = [separator for x in ports]
|
||||||
string_result = '{} '.format(separator[-1])
|
string_result = '{} '.format(separator[-1])
|
||||||
@@ -212,20 +283,23 @@ class Frontend(object):
|
|||||||
return string_result[:-1]
|
return string_result[:-1]
|
||||||
|
|
||||||
def _get_node_by_lineno(self, lineno, kernel):
|
def _get_node_by_lineno(self, lineno, kernel):
|
||||||
|
"""Returns instruction form from kernel by its line number."""
|
||||||
nodes = [instr for instr in kernel if instr['line_number'] == lineno]
|
nodes = [instr for instr in kernel if instr['line_number'] == lineno]
|
||||||
return nodes[0] if len(nodes) > 0 else None
|
return nodes[0] if len(nodes) > 0 else None
|
||||||
|
|
||||||
def _get_lcd_cp_ports(self, line_number, cp_dg, dependency, separator='|'):
|
def _get_lcd_cp_ports(self, line_number, cp_dg, dependency, separator='|'):
|
||||||
|
"""Returns the CP and LCD line for one instruction."""
|
||||||
lat_cp = lat_lcd = ''
|
lat_cp = lat_lcd = ''
|
||||||
if cp_dg:
|
if cp_dg:
|
||||||
lat_cp = float(self._get_node_by_lineno(line_number, cp_dg)['latency_cp'])
|
lat_cp = float(self._get_node_by_lineno(line_number, cp_dg)['latency_cp'])
|
||||||
if dependency:
|
if dependency:
|
||||||
lat_lcd = float(self._get_node_by_lineno(line_number, dependency['dependencies'])[
|
lat_lcd = float(
|
||||||
'latency_lcd'
|
self._get_node_by_lineno(line_number, dependency['dependencies'])['latency_lcd']
|
||||||
])
|
)
|
||||||
return '{} {:>4} {} {:>4} {}'.format(separator, lat_cp, separator, lat_lcd, separator)
|
return '{} {:>4} {} {:>4} {}'.format(separator, lat_cp, separator, lat_lcd, separator)
|
||||||
|
|
||||||
def _get_max_port_len(self, kernel):
|
def _get_max_port_len(self, kernel):
|
||||||
|
"""Returns the maximal length needed to print all throughputs of the kernel."""
|
||||||
port_len = [4 for x in self._machine_model.get_ports()]
|
port_len = [4 for x in self._machine_model.get_ports()]
|
||||||
for instruction_form in kernel:
|
for instruction_form in kernel:
|
||||||
for i, port in enumerate(instruction_form['port_pressure']):
|
for i, port in enumerate(instruction_form['port_pressure']):
|
||||||
@@ -234,6 +308,7 @@ class Frontend(object):
|
|||||||
return port_len
|
return port_len
|
||||||
|
|
||||||
def _get_port_number_line(self, port_len, separator='|'):
|
def _get_port_number_line(self, port_len, separator='|'):
|
||||||
|
"""Returns column view of port identificators of machine_model."""
|
||||||
string_result = separator
|
string_result = separator
|
||||||
separator_list = self._get_separator_list(separator, '-')
|
separator_list = self._get_separator_list(separator, '-')
|
||||||
for i, length in enumerate(port_len):
|
for i, length in enumerate(port_len):
|
||||||
@@ -242,6 +317,7 @@ class Frontend(object):
|
|||||||
return string_result
|
return string_result
|
||||||
|
|
||||||
def _print_header_report(self):
|
def _print_header_report(self):
|
||||||
|
"""Prints header information"""
|
||||||
version = 'v0.3'
|
version = 'v0.3'
|
||||||
adjust = 20
|
adjust = 20
|
||||||
header = ''
|
header = ''
|
||||||
@@ -254,6 +330,7 @@ class Frontend(object):
|
|||||||
print(header)
|
print(header)
|
||||||
|
|
||||||
def _print_symbol_map(self):
|
def _print_symbol_map(self):
|
||||||
|
"""Prints instruction flag map."""
|
||||||
symbol_dict = {
|
symbol_dict = {
|
||||||
INSTR_FLAGS.NOT_BOUND: 'Instruction micro-ops not bound to a port',
|
INSTR_FLAGS.NOT_BOUND: 'Instruction micro-ops not bound to a port',
|
||||||
INSTR_FLAGS.TP_UNKWN: 'No throughput/latency information for this instruction in '
|
INSTR_FLAGS.TP_UNKWN: 'No throughput/latency information for this instruction in '
|
||||||
|
|||||||
Reference in New Issue
Block a user