Merge remote-tracking branch 'origin/master' into InstrucForm

merge
This commit is contained in:
stefandesouza
2023-08-20 11:39:20 +02:00
8 changed files with 234 additions and 7 deletions

View File

@@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [3.7, 3.8, 3.9, "3.10"] python-version: [3.8, 3.9, "3.10", "3.11"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-python@v2 - uses: actions/setup-python@v4
name: Set up Python ${{ matrix.python-version }} name: Set up Python ${{ matrix.python-version }}
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
@@ -27,7 +27,7 @@ jobs:
- name: Test - name: Test
run: | run: |
coverage run -p tests/all_tests.py coverage run -p tests/all_tests.py
- uses: codecov/codecov-action@v1 - uses: codecov/codecov-action@v3
- name: Build package - name: Build package
run: | run: |
python setup.py build sdist bdist_wheel python setup.py build sdist bdist_wheel

View File

@@ -91,7 +91,7 @@ The usage of OSACA can be listed as:
[--ignore-unknown] [--lcd-timeout SECONDS] [--ignore-unknown] [--lcd-timeout SECONDS]
[--db-check] [--import MICROBENCH] [--insert-marker] [--db-check] [--import MICROBENCH] [--insert-marker]
[--export-graph GRAPHNAME] [--consider-flag-deps] [--export-graph GRAPHNAME] [--consider-flag-deps]
[--out OUT] [--verbose] [--out OUT] [--yaml-out YAML_OUT] [--verbose]
FILEPATH FILEPATH
-h, --help -h, --help
@@ -133,6 +133,8 @@ The usage of OSACA can be listed as:
Increases verbosity level Increases verbosity level
-o OUT, --out OUT -o OUT, --out OUT
Write analysis to this file (default to stdout) Write analysis to this file (default to stdout)
--yaml-out YAML_OUT
Write analysis as YAML representation to this file
The **FILEPATH** describes the filepath to the file to work with and is always necessary, use "-" to read from stdin. The **FILEPATH** describes the filepath to the file to work with and is always necessary, use "-" to read from stdin.

View File

@@ -1,6 +1,6 @@
"""Open Source Architecture Code Analyzer""" """Open Source Architecture Code Analyzer"""
name = "osaca" name = "osaca"
__version__ = "0.5.1" __version__ = "0.5.2"
# To trigger travis deployment to pypi, do the following: # To trigger travis deployment to pypi, do the following:
# 1. Increment __version___ # 1. Increment __version___

View File

@@ -5515,3 +5515,13 @@ instruction_forms:
latency: 3 latency: 3
port_pressure: [[1, '7']] port_pressure: [[1, '7']]
throughput: 1.0 throughput: 1.0
- name: imul # uops.info
operands:
- class: register
name: gpr
- class: register
name: gpr
latency: 3
port_pressure: [[1, '7']]
throughput: 1.0
uops: 1

View File

@@ -8,6 +8,7 @@ import re
from datetime import datetime as dt from datetime import datetime as dt
from osaca.semantics import INSTR_FLAGS, ArchSemantics, KernelDG, MachineModel from osaca.semantics import INSTR_FLAGS, ArchSemantics, KernelDG, MachineModel
from osaca.parser import AttrDict
def _get_version(*file_paths): def _get_version(*file_paths):
@@ -201,6 +202,99 @@ class Frontend(object):
+ self.loopcarried_dependencies(kernel_dg.get_loopcarried_dependencies()) + self.loopcarried_dependencies(kernel_dg.get_loopcarried_dependencies())
) )
def full_analysis_dict(
self,
kernel,
kernel_dg: KernelDG,
arch_warning=False,
length_warning=False,
lcd_warning=False,
):
"""
Create a dictionary of the full analysis for machine-readable output.
:param kernel: kernel to report on
:type kernel: list
:param kernel_dg: directed graph containing CP and LCD
:type kernel_dg: :class:`~osaca.semantics.KernelDG`
:param arch_warning: flag for additional user warning to specify micro-arch
:type arch_warning: boolean, optional
:param length_warning: flag for additional user warning to specify kernel length with
--lines
:type length_warning: boolean, optional
:param lcd_warning: flag for additional user warning due to LCD analysis timed out
:type lcd_warning: boolean, optional
:returns: dict -- a dict of the analysis
"""
warnings = []
if arch_warning:
warnings.append("ArchWarning")
if length_warning:
warnings.append("LengthWarning")
if lcd_warning:
warnings.append("LCDWarning")
if INSTR_FLAGS.TP_UNKWN in [flag for instr in kernel for flag in instr["flags"]]:
warnings.append("UnknownInstrWarning")
tp_sum = ArchSemantics.get_throughput_sum(kernel) or kernel[0]["port_pressure"]
cp_kernel = kernel_dg.get_critical_path()
dep_dict = kernel_dg.get_loopcarried_dependencies()
lcd_sum = 0.0
if dep_dict:
longest_lcd = max(dep_dict, key=lambda ln: dep_dict[ln]["latency"])
lcd_sum = dep_dict[longest_lcd]["latency"]
return {
"Header": self._header_report_dict(),
"Warnings": warnings,
"Kernel": [
{
"Line": re.sub(r"\s+", " ", x["line"].strip()),
"LineNumber": x["line_number"],
"Flags": list(x["flags"]),
"Instruction": x["instruction"],
"Operands": AttrDict.get_dict(x["operands"]),
"SemanticOperands": AttrDict.get_dict(x["semantic_operands"]),
"Label": x["label"],
"Directive": x["directive"],
"Latency": float(x["latency"]),
"LatencyCP": float(x["latency_cp"]),
"LatencyLCD": float(x["latency_lcd"]),
"Throughput": float(x["throughput"]),
"LatencyWithoutLoad": float(x["latency_wo_load"]),
"PortPressure": {
self._machine_model.get_ports()[i]: v
for i, v in enumerate(x["port_pressure"])
},
"PortUops": [
{
"Ports": list(y[1]),
"Cycles": y[0],
}
for y in x["port_uops"]
],
"Comment": x["comment"],
}
for x in kernel
],
"Summary": {
"PortPressure": {
self._machine_model.get_ports()[i]: v for i, v in enumerate(tp_sum)
},
"CriticalPath": sum([x["latency_cp"] for x in cp_kernel]),
"LCD": lcd_sum,
},
"Target": {
"Name": self._arch.upper(),
"Ports": list(self._machine_model.get_ports()),
},
}
def combined_view( def combined_view(
self, self,
kernel, kernel,
@@ -449,6 +543,15 @@ class Frontend(object):
) )
return header + "\n" return header + "\n"
def _header_report_dict(self):
"""Return header information in a dictionary format"""
return {
"Version": _get_version("__init__.py"),
"FileName": self._filename,
"Architecture": self._arch,
"Timestamp": dt.utcnow().strftime("%Y-%m-%d %H:%M:%S"),
}
def _symbol_map(self): def _symbol_map(self):
"""Prints instruction flag map.""" """Prints instruction flag map."""
symbol_dict = { symbol_dict = {

View File

@@ -7,6 +7,8 @@ import re
import sys import sys
from functools import lru_cache from functools import lru_cache
import ruamel.yaml
from osaca.db_interface import import_benchmark_output, sanity_check from osaca.db_interface import import_benchmark_output, sanity_check
from osaca.frontend import Frontend from osaca.frontend import Frontend
from osaca.parser import BaseParser, ParserAArch64, ParserX86ATT from osaca.parser import BaseParser, ParserAArch64, ParserX86ATT
@@ -188,6 +190,13 @@ def create_parser(parser=None):
type=argparse.FileType("w"), type=argparse.FileType("w"),
help="Write analysis to this file (default to stdout).", help="Write analysis to this file (default to stdout).",
) )
parser.add_argument(
"--yaml-out",
default=None,
dest="yaml_out",
type=argparse.FileType("w"),
help="Write analysis as YAML representation to this file",
)
parser.add_argument( parser.add_argument(
"file", "file",
type=argparse.FileType("r"), type=argparse.FileType("r"),
@@ -360,6 +369,17 @@ def inspect(args, output_file=sys.stdout):
), ),
file=output_file, file=output_file,
) )
if args.yaml_out is not None:
ruamel.yaml.dump(
frontend.full_analysis_dict(
kernel,
kernel_graph,
arch_warning=print_arch_warning,
length_warning=print_length_warning,
lcd_warning=kernel_graph.timed_out,
),
args.yaml_out,
)
def run(args, output_file=sys.stdout): def run(args, output_file=sys.stdout):

View File

@@ -27,3 +27,27 @@ class AttrDict(dict):
dictionary[key] = [AttrDict.convert_dict(x) for x in entry] dictionary[key] = [AttrDict.convert_dict(x) for x in entry]
return AttrDict(dictionary) return AttrDict(dictionary)
return dictionary return dictionary
@staticmethod
def get_dict(attrdict):
"""
Convert given `AttrDict` to a standard dictionary.
:param attrdict: `AttrDict` to be converted
:type attrdict: `AttrDict`
:returns: `dict` representation of ``AttrDict``
"""
if isinstance(attrdict, type(list())):
return [AttrDict.get_dict(x) for x in attrdict]
if isinstance(attrdict, type(AttrDict())):
newdict = {}
for key in list(attrdict.keys()):
entry = attrdict[key]
if isinstance(entry, type(dict())) or isinstance(entry, type(AttrDict())):
newdict[key] = AttrDict.get_dict(attrdict[key])
elif isinstance(entry, type(list())):
newdict[key] = [AttrDict.get_dict(x) for x in entry]
else:
newdict[key] = entry
return newdict
return attrdict

View File

@@ -8,7 +8,7 @@ import unittest
from osaca.frontend import Frontend from osaca.frontend import Frontend
from osaca.parser import ParserAArch64, ParserX86ATT from osaca.parser import ParserAArch64, ParserX86ATT
from osaca.semantics import ArchSemantics, KernelDG, MachineModel from osaca.semantics import ArchSemantics, KernelDG, MachineModel, reduce_to_section
class TestFrontend(unittest.TestCase): class TestFrontend(unittest.TestCase):
@@ -81,6 +81,74 @@ class TestFrontend(unittest.TestCase):
fe.full_analysis(self.kernel_AArch64, dg, verbose=True) fe.full_analysis(self.kernel_AArch64, dg, verbose=True)
# TODO compare output with checked string # TODO compare output with checked string
def test_dict_output_x86(self):
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
fe = Frontend(path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "csx.yml"))
analysis_dict = fe.full_analysis_dict(self.kernel_x86, dg)
self.assertEqual(len(self.kernel_x86), len(analysis_dict["Kernel"]))
self.assertEqual("csx", analysis_dict["Header"]["Architecture"])
self.assertEqual(len(analysis_dict["Warnings"]), 0)
for i, line in enumerate(self.kernel_x86):
self.assertEqual(line["throughput"], analysis_dict["Kernel"][i]["Throughput"])
self.assertEqual(line["latency"], analysis_dict["Kernel"][i]["Latency"])
self.assertEqual(
line["latency_wo_load"], analysis_dict["Kernel"][i]["LatencyWithoutLoad"]
)
self.assertEqual(line["latency_cp"], analysis_dict["Kernel"][i]["LatencyCP"])
self.assertEqual(line["instruction"], analysis_dict["Kernel"][i]["Instruction"])
self.assertEqual(len(line["operands"]), len(analysis_dict["Kernel"][i]["Operands"]))
self.assertEqual(
len(line["semantic_operands"]["source"]),
len(analysis_dict["Kernel"][i]["SemanticOperands"]["source"]),
)
self.assertEqual(
len(line["semantic_operands"]["destination"]),
len(analysis_dict["Kernel"][i]["SemanticOperands"]["destination"]),
)
self.assertEqual(
len(line["semantic_operands"]["src_dst"]),
len(analysis_dict["Kernel"][i]["SemanticOperands"]["src_dst"]),
)
self.assertEqual(line["flags"], analysis_dict["Kernel"][i]["Flags"])
self.assertEqual(line["line_number"], analysis_dict["Kernel"][i]["LineNumber"])
def test_dict_output_AArch64(self):
reduced_kernel = reduce_to_section(self.kernel_AArch64, self.semantics_tx2._isa)
dg = KernelDG(
reduced_kernel,
self.parser_AArch64,
self.machine_model_tx2,
self.semantics_tx2,
)
fe = Frontend(path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "tx2.yml"))
analysis_dict = fe.full_analysis_dict(reduced_kernel, dg)
self.assertEqual(len(reduced_kernel), len(analysis_dict["Kernel"]))
self.assertEqual("tx2", analysis_dict["Header"]["Architecture"])
self.assertEqual(len(analysis_dict["Warnings"]), 0)
for i, line in enumerate(reduced_kernel):
self.assertEqual(line["throughput"], analysis_dict["Kernel"][i]["Throughput"])
self.assertEqual(line["latency"], analysis_dict["Kernel"][i]["Latency"])
self.assertEqual(
line["latency_wo_load"], analysis_dict["Kernel"][i]["LatencyWithoutLoad"]
)
self.assertEqual(line["latency_cp"], analysis_dict["Kernel"][i]["LatencyCP"])
self.assertEqual(line["instruction"], analysis_dict["Kernel"][i]["Instruction"])
self.assertEqual(len(line["operands"]), len(analysis_dict["Kernel"][i]["Operands"]))
self.assertEqual(
len(line["semantic_operands"]["source"]),
len(analysis_dict["Kernel"][i]["SemanticOperands"]["source"]),
)
self.assertEqual(
len(line["semantic_operands"]["destination"]),
len(analysis_dict["Kernel"][i]["SemanticOperands"]["destination"]),
)
self.assertEqual(
len(line["semantic_operands"]["src_dst"]),
len(analysis_dict["Kernel"][i]["SemanticOperands"]["src_dst"]),
)
self.assertEqual(line["flags"], analysis_dict["Kernel"][i]["Flags"])
self.assertEqual(line["line_number"], analysis_dict["Kernel"][i]["LineNumber"])
################## ##################
# Helper functions # Helper functions
################## ##################