CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
339 lines
11 KiB
Python
339 lines
11 KiB
Python
from __future__ import annotations
|
|
|
|
import csv
|
|
import json
|
|
from collections.abc import Iterable
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
from .records import Pc, Wave
|
|
|
|
CODE_HEADER = "ISA, _, LineNumber, Source, Codeobj, Vaddr, Hit, Latency, Stall, Idle"
|
|
CODE_ROW_INST = 0
|
|
CODE_ROW_LINE = 2
|
|
CODE_ROW_SOURCE = 3
|
|
CODE_ROW_CODE_OBJECT = 4
|
|
CODE_ROW_VADDR = 5
|
|
CODE_ROW_HIT = 6
|
|
CODE_ROW_LATENCY = 7
|
|
CODE_ROW_STALL = 8
|
|
CODE_ROW_IDLE = 9
|
|
CODE_ROW_COUNTERS = (CODE_ROW_HIT, CODE_ROW_LATENCY, CODE_ROW_STALL, CODE_ROW_IDLE)
|
|
CODE_ROW_WIDTH = CODE_ROW_IDLE + 1
|
|
|
|
STATS_COL_CODE_OBJECT = "CodeObj"
|
|
STATS_COL_VADDR = "Vaddr"
|
|
STATS_COL_INSTRUCTION = "Instruction"
|
|
STATS_COL_HITCOUNT = "Hitcount"
|
|
STATS_COL_LATENCY = "Latency"
|
|
STATS_COL_STALL = "Stall"
|
|
STATS_COL_IDLE = "Idle"
|
|
STATS_COL_SOURCE = "Source"
|
|
STATS_CSV_HEADER = [
|
|
STATS_COL_CODE_OBJECT,
|
|
STATS_COL_VADDR,
|
|
STATS_COL_INSTRUCTION,
|
|
STATS_COL_HITCOUNT,
|
|
STATS_COL_LATENCY,
|
|
STATS_COL_STALL,
|
|
STATS_COL_IDLE,
|
|
STATS_COL_SOURCE,
|
|
]
|
|
|
|
|
|
@dataclass
|
|
class CodeEntry:
|
|
pc: Pc
|
|
inst: str
|
|
line_number: int
|
|
source: str = ""
|
|
hitcount: int = 0
|
|
latency: int = 0
|
|
stall: int = 0
|
|
idle: int = 0
|
|
expected_hitcount: int = 0
|
|
expected_latency: int = 0
|
|
expected_stall: int = 0
|
|
expected_idle: int = 0
|
|
memory_size: int = 4
|
|
|
|
|
|
class CodeIndex:
|
|
def __init__(self, entries: Iterable[CodeEntry], document: dict | None = None):
|
|
self.document = document or {"code": [], "header": CODE_HEADER}
|
|
self.entries: dict[Pc, CodeEntry] = {}
|
|
self.line_numbers: dict[Pc, int] = {}
|
|
self._memory_sizes_dirty = False
|
|
|
|
for entry in entries:
|
|
self.entries[entry.pc] = entry
|
|
self.line_numbers[entry.pc] = entry.line_number
|
|
|
|
self._next_line_number = max(self.line_numbers.values(), default=0) + 1
|
|
self._mark_memory_sizes_dirty()
|
|
|
|
@classmethod
|
|
def from_code_json(cls, path: str | Path, *, load_counts: bool = True) -> "CodeIndex":
|
|
return cls.from_document(json.loads(Path(path).read_text()), load_counts=load_counts)
|
|
|
|
@classmethod
|
|
def from_document(cls, doc: dict, *, load_counts: bool = True) -> "CodeIndex":
|
|
document = json.loads(json.dumps(doc))
|
|
if not load_counts:
|
|
_clear_document_counts(document)
|
|
|
|
entries: list[CodeEntry] = []
|
|
|
|
for row in document.get("code", []):
|
|
if len(row) < CODE_ROW_WIDTH:
|
|
continue
|
|
inst = str(row[CODE_ROW_INST])
|
|
if _is_code_label(inst):
|
|
continue
|
|
pc = Pc(address=int(row[CODE_ROW_VADDR]), code_object_id=int(row[CODE_ROW_CODE_OBJECT]))
|
|
entries.append(
|
|
CodeEntry(
|
|
pc=pc,
|
|
inst=inst,
|
|
line_number=int(row[CODE_ROW_LINE]),
|
|
source=str(row[CODE_ROW_SOURCE]),
|
|
hitcount=int(row[CODE_ROW_HIT] or 0) if load_counts else 0,
|
|
latency=int(row[CODE_ROW_LATENCY] or 0) if load_counts else 0,
|
|
stall=int(row[CODE_ROW_STALL] or 0) if load_counts else 0,
|
|
idle=int(row[CODE_ROW_IDLE] or 0) if load_counts else 0,
|
|
)
|
|
)
|
|
|
|
return cls(entries, document)
|
|
|
|
@classmethod
|
|
def from_stats_csv(cls, paths: Iterable[str | Path]) -> "CodeIndex":
|
|
entries: dict[Pc, CodeEntry] = {}
|
|
for path in paths:
|
|
with Path(path).open("r", newline="") as fh:
|
|
reader = csv.DictReader(fh)
|
|
for row in reader:
|
|
if not row:
|
|
continue
|
|
pc = Pc(
|
|
address=int(row[STATS_COL_VADDR].strip(), 0),
|
|
code_object_id=int(row[STATS_COL_CODE_OBJECT].strip(), 0),
|
|
)
|
|
inst = row[STATS_COL_INSTRUCTION].strip()
|
|
if _is_code_label(inst):
|
|
continue
|
|
|
|
entry = entries.setdefault(
|
|
pc,
|
|
CodeEntry(
|
|
pc=pc,
|
|
inst=inst,
|
|
line_number=len(entries) + 1,
|
|
source=row.get(STATS_COL_SOURCE, ""),
|
|
),
|
|
)
|
|
entry.expected_hitcount += _int_cell(row.get(STATS_COL_HITCOUNT))
|
|
entry.expected_latency += _int_cell(row.get(STATS_COL_LATENCY))
|
|
entry.expected_stall += _int_cell(row.get(STATS_COL_STALL))
|
|
entry.expected_idle += _int_cell(row.get(STATS_COL_IDLE))
|
|
return cls(entries.values())
|
|
|
|
def isa_for_pc(self, pc: Pc) -> tuple[str, int] | None:
|
|
entry = self.entries.get(pc)
|
|
if entry is None:
|
|
return None
|
|
self._ensure_memory_sizes()
|
|
return entry.inst, entry.memory_size
|
|
|
|
def get_or_create(self, pc: Pc) -> CodeEntry:
|
|
entry = self.entries.get(pc)
|
|
if entry is not None:
|
|
return entry
|
|
entry = CodeEntry(
|
|
pc=pc,
|
|
inst="",
|
|
line_number=self._next_line_number,
|
|
)
|
|
self._next_line_number += 1
|
|
self.entries[pc] = entry
|
|
self.line_numbers[pc] = entry.line_number
|
|
self._mark_memory_sizes_dirty()
|
|
return entry
|
|
|
|
def line_number(self, pc: Pc) -> int:
|
|
"""Return the source line number for *pc*.
|
|
|
|
Raises KeyError if *pc* is not present in the index.
|
|
"""
|
|
return self.entries[pc].line_number
|
|
|
|
def accumulate_wave(self, wave: Wave) -> None:
|
|
prev_inst_time = wave.begin_time
|
|
for inst in wave.instructions:
|
|
if inst.pc.code_object_id == 0 and inst.pc.address == 0:
|
|
continue
|
|
entry = self.get_or_create(inst.pc)
|
|
entry.hitcount += 1
|
|
entry.latency += inst.duration
|
|
entry.stall += inst.stall
|
|
entry.idle += max(inst.time - prev_inst_time, 0)
|
|
prev_inst_time = max(prev_inst_time, inst.time + inst.duration)
|
|
|
|
def write_code_json(self, path: str | Path) -> None:
|
|
out_doc = dict(self.document)
|
|
rows = []
|
|
seen: set[Pc] = set()
|
|
for row in self.document.get("code", []):
|
|
if len(row) < CODE_ROW_WIDTH or _is_code_label(str(row[CODE_ROW_INST])):
|
|
rows.append(row)
|
|
continue
|
|
pc = _pc_from_code_row(row)
|
|
seen.add(pc)
|
|
entry = self.entries.get(pc)
|
|
if entry is None:
|
|
rows.append(row)
|
|
continue
|
|
updated = list(row)
|
|
_set_code_row_counts(updated, entry)
|
|
rows.append(updated)
|
|
for entry in sorted(
|
|
(entry for pc, entry in self.entries.items() if pc not in seen),
|
|
key=lambda e: e.line_number,
|
|
):
|
|
rows.append(_code_row(entry))
|
|
out_doc["code"] = rows
|
|
out_doc.setdefault("header", CODE_HEADER)
|
|
Path(path).write_text(json.dumps(out_doc, indent=2))
|
|
|
|
def write_stats_csv(self, path: str | Path) -> None:
|
|
with Path(path).open("w", newline="") as fh:
|
|
writer = csv.writer(fh)
|
|
writer.writerow(STATS_CSV_HEADER)
|
|
seen: set[Pc] = set()
|
|
for row in self.document.get("code", []):
|
|
if len(row) < CODE_ROW_WIDTH:
|
|
continue
|
|
pc = _pc_from_code_row(row)
|
|
if _is_code_label(str(row[CODE_ROW_INST])):
|
|
writer.writerow(
|
|
[
|
|
pc.code_object_id,
|
|
pc.address,
|
|
row[CODE_ROW_INST],
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
row[CODE_ROW_SOURCE],
|
|
]
|
|
)
|
|
continue
|
|
entry = self.entries.get(pc)
|
|
if entry is not None:
|
|
_write_stats_row(writer, entry)
|
|
seen.add(pc)
|
|
for entry in sorted(
|
|
(entry for pc, entry in self.entries.items() if pc not in seen),
|
|
key=lambda e: (e.pc.code_object_id, e.pc.address),
|
|
):
|
|
_write_stats_row(writer, entry)
|
|
|
|
def validate_expected(self) -> list[str]:
|
|
errors = []
|
|
for entry in sorted(
|
|
self.entries.values(),
|
|
key=lambda e: (e.pc.code_object_id, e.pc.address),
|
|
):
|
|
if (
|
|
entry.hitcount == entry.expected_hitcount
|
|
and entry.latency == entry.expected_latency
|
|
and entry.stall == entry.expected_stall
|
|
and entry.idle == entry.expected_idle
|
|
):
|
|
continue
|
|
errors.append(
|
|
f"{entry.inst} - PC: {entry.pc.code_object_id},{entry.pc.address}\n"
|
|
f"Hitcount: {entry.hitcount}/{entry.expected_hitcount}, "
|
|
f"Stall: {entry.stall}/{entry.expected_stall}, "
|
|
f"Latency: {entry.latency}/{entry.expected_latency}, "
|
|
f"Idle: {entry.idle}/{entry.expected_idle}"
|
|
)
|
|
return errors
|
|
|
|
def _mark_memory_sizes_dirty(self) -> None:
|
|
self._memory_sizes_dirty = True
|
|
|
|
def _ensure_memory_sizes(self) -> None:
|
|
if self._memory_sizes_dirty:
|
|
self._set_memory_sizes()
|
|
self._memory_sizes_dirty = False
|
|
|
|
def _set_memory_sizes(self) -> None:
|
|
by_codeobj: dict[int, list[CodeEntry]] = {}
|
|
for entry in self.entries.values():
|
|
by_codeobj.setdefault(entry.pc.code_object_id, []).append(entry)
|
|
for entries in by_codeobj.values():
|
|
entries.sort(key=lambda e: e.pc.address)
|
|
for idx, entry in enumerate(entries):
|
|
if idx + 1 < len(entries):
|
|
delta = entries[idx + 1].pc.address - entry.pc.address
|
|
entry.memory_size = max(delta, 4)
|
|
else:
|
|
entry.memory_size = 4
|
|
|
|
|
|
def _int_cell(value: str | None) -> int:
|
|
value = (value or "").strip()
|
|
return int(value, 0) if value else 0
|
|
|
|
|
|
def _is_code_label(inst: str) -> bool:
|
|
return inst.startswith(";") or inst.startswith("label")
|
|
|
|
|
|
def _pc_from_code_row(row: list) -> Pc:
|
|
return Pc(
|
|
address=int(row[CODE_ROW_VADDR]),
|
|
code_object_id=int(row[CODE_ROW_CODE_OBJECT]),
|
|
)
|
|
|
|
|
|
def _set_code_row_counts(row: list, entry: CodeEntry) -> None:
|
|
row[CODE_ROW_HIT] = entry.hitcount
|
|
row[CODE_ROW_LATENCY] = entry.latency
|
|
row[CODE_ROW_STALL] = entry.stall
|
|
row[CODE_ROW_IDLE] = entry.idle
|
|
|
|
|
|
def _code_row(entry: CodeEntry) -> list[object]:
|
|
row: list[object] = [0] * CODE_ROW_WIDTH
|
|
row[CODE_ROW_INST] = entry.inst
|
|
row[CODE_ROW_LINE] = entry.line_number
|
|
row[CODE_ROW_SOURCE] = entry.source
|
|
row[CODE_ROW_CODE_OBJECT] = entry.pc.code_object_id
|
|
row[CODE_ROW_VADDR] = entry.pc.address
|
|
_set_code_row_counts(row, entry)
|
|
return row
|
|
|
|
|
|
def _clear_document_counts(document: dict) -> None:
|
|
for row in document.get("code", []):
|
|
if len(row) >= CODE_ROW_WIDTH:
|
|
for column in CODE_ROW_COUNTERS:
|
|
row[column] = 0
|
|
|
|
|
|
def _write_stats_row(writer: object, entry: CodeEntry) -> None:
|
|
writer.writerow(
|
|
[
|
|
entry.pc.code_object_id,
|
|
entry.pc.address,
|
|
entry.inst,
|
|
entry.hitcount,
|
|
entry.latency,
|
|
entry.stall,
|
|
entry.idle,
|
|
entry.source,
|
|
]
|
|
)
|