verilator/ci/ci-rtlmeter-report.py

104 lines
3.1 KiB
Python

# DESCRIPTION: Verilator: CI script for 'rtlmeter.yml' PR results
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
###############################################################################
# This script runs with the venv of **RTLMeter**
###############################################################################
import sys
import json
import tabulate
from typing import Final, List
tabulate.PRESERVE_WHITESPACE = True
tabulate.MIN_PADDING = 0
_ASCII_TABLE_FORMAT: Final = tabulate.TableFormat(
lineabove=tabulate.Line("╒═", "", "═╤═", "═╕"),
linebelowheader=tabulate.Line("╞═", "", "═╪═", "═╡"),
linebelow=tabulate.Line("╘═", "", "═╧═", "═╛"),
headerrow=tabulate.DataRow("", "", ""),
datarow=tabulate.DataRow("", "", ""),
linebetweenrows=None,
padding=0,
with_header_hide=None,
)
def printTable(table: List[List[str]], **kwargs) -> None:
print(tabulate.tabulate(table, tablefmt=_ASCII_TABLE_FORMAT, **kwargs))
# fmt: off
stepMetric = (
("verilate", "elapsed"),
("verilate", "memory"),
("verilate", "cpu"),
("cppbuild", "elapsed"),
("cppbuild", "memory"),
("cppbuild", "cpu"),
("cppbuild", "codeSize"),
("execute", "speed"),
("execute", "clocks"),
("execute", "memory"),
("execute", "cpu"),
)
# fmt: on
table = []
for ref, cmp in zip(sys.argv[1::2], sys.argv[2::2]):
with open(ref, "r", encoding="utf-8") as f:
ref_json = json.load(f)[0]
with open(cmp, "r", encoding="utf-8") as f:
cmp_json = json.load(f)
if table:
table.append(tabulate.SEPARATING_LINE)
runName = ref_json["runName"]
for step, metric in stepMetric:
if step not in cmp_json:
continue
data = cmp_json[step]
if metric not in data:
continue
data = data[metric]
minGain = float("inf")
maxGain = float("-inf")
meanGain = 1
count = 0
for _, _, _, _, _, g, _ in data["table"]:
minGain = min(minGain, g)
maxGain = max(maxGain, g)
meanGain *= g
count += 1
meanGain = meanGain**(1 / count)
if metric == "clocks":
# Clock cycles should match exactly
status = "" if minGain != 1 or maxGain != 1 else ""
else:
# Otherwise use some arbitrary brackets
status = ""
if (meanGain > 0.95):
status = "⚠️"
if (meanGain > 0.98):
status = ""
if (meanGain > 1.02):
status = "💡"
if (meanGain > 1.05):
status = ""
table.append([
runName, step, ref_json["metrics"][metric]["header"], f"{meanGain:.2f}x {status} ",
f"{minGain:.2f}x", f"{maxGain:.2f}x"
])
printTable(
table,
headers=("Run", "Step", "Metric", "Improvement", "Min", "Max"),
colalign=("left", "left", "left", "right", "right", "right"),
disable_numparse=True,
)