forked from OSchip/llvm-project
129 lines
4.7 KiB
Python
129 lines
4.7 KiB
Python
"""Reads JSON files produced by the benchmarking framework and renders them.
|
|
|
|
Installation:
|
|
> apt-get install python3-pip
|
|
> pip3 install matplotlib pandas seaborn
|
|
|
|
Run:
|
|
> python3 libc/benchmarks/libc-benchmark-analysis.py3 <files>
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import pandas as pd
|
|
import seaborn as sns
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.ticker import EngFormatter
|
|
|
|
def formatUnit(value, unit):
|
|
return EngFormatter(unit, sep="").format_data(value)
|
|
|
|
def formatCache(cache):
|
|
letter = cache["Type"][0].lower()
|
|
level = cache["Level"]
|
|
size = formatUnit(cache["Size"], "B")
|
|
ways = cache["NumSharing"]
|
|
return F'{letter}L{level}:{size}/{ways}'
|
|
|
|
def getCpuFrequency(study):
|
|
return study["Runtime"]["Host"]["CpuFrequency"]
|
|
|
|
def getId(study):
|
|
CpuName = study["Runtime"]["Host"]["CpuName"]
|
|
CpuFrequency = formatUnit(getCpuFrequency(study), "Hz")
|
|
Mode = " (Sweep)" if study["Configuration"]["IsSweepMode"] else ""
|
|
CpuCaches = ", ".join(formatCache(c) for c in study["Runtime"]["Host"]["Caches"])
|
|
return F'{CpuName} {CpuFrequency}{Mode}\n{CpuCaches}'
|
|
|
|
def getFunction(study):
|
|
return study["Configuration"]["Function"]
|
|
|
|
def getLabel(study):
|
|
return F'{getFunction(study)} {study["StudyName"]}'
|
|
|
|
def displaySweepData(id, studies, mode):
|
|
df = None
|
|
for study in studies:
|
|
Measurements = study["Measurements"]
|
|
SweepModeMaxSize = study["Configuration"]["SweepModeMaxSize"]
|
|
NumSizes = SweepModeMaxSize + 1
|
|
NumTrials = study["Configuration"]["NumTrials"]
|
|
assert NumTrials * NumSizes == len(Measurements), 'not a multiple of NumSizes'
|
|
Index = pd.MultiIndex.from_product([range(NumSizes), range(NumTrials)], names=['size', 'trial'])
|
|
if df is None:
|
|
df = pd.DataFrame(Measurements, index=Index, columns=[getLabel(study)])
|
|
else:
|
|
df[getLabel(study)] = pd.Series(Measurements, index=Index)
|
|
df = df.reset_index(level='trial', drop=True)
|
|
if mode == "cycles":
|
|
df *= getCpuFrequency(study)
|
|
if mode == "bytespercycle":
|
|
df *= getCpuFrequency(study)
|
|
for col in df.columns:
|
|
df[col] = pd.Series(data=df.index, index=df.index).divide(df[col])
|
|
FormatterUnit = {"time":"s","cycles":"","bytespercycle":"B/cycle"}[mode]
|
|
Label = {"time":"Time","cycles":"Cycles","bytespercycle":"Byte/cycle"}[mode]
|
|
graph = sns.lineplot(data=df, palette="muted", ci=95)
|
|
graph.set_title(id)
|
|
graph.yaxis.set_major_formatter(EngFormatter(unit=FormatterUnit))
|
|
graph.yaxis.set_label_text(Label)
|
|
graph.xaxis.set_major_formatter(EngFormatter(unit="B"))
|
|
graph.xaxis.set_label_text("Copy Size")
|
|
_ = plt.xticks(rotation=90)
|
|
plt.show()
|
|
|
|
def displayDistributionData(id, studies, mode):
|
|
distributions = set()
|
|
df = None
|
|
for study in studies:
|
|
distribution = study["Configuration"]["SizeDistributionName"]
|
|
distributions.add(distribution)
|
|
local = pd.DataFrame(study["Measurements"], columns=["time"])
|
|
local["distribution"] = distribution
|
|
local["label"] = getLabel(study)
|
|
local["cycles"] = local["time"] * getCpuFrequency(study)
|
|
if df is None:
|
|
df = local
|
|
else:
|
|
df = df.append(local)
|
|
if mode == "bytespercycle":
|
|
mode = "time"
|
|
print("`--mode=bytespercycle` is ignored for distribution mode reports")
|
|
FormatterUnit = {"time":"s","cycles":""}[mode]
|
|
Label = {"time":"Time","cycles":"Cycles"}[mode]
|
|
graph = sns.violinplot(data=df, x="distribution", y=mode, palette="muted", hue="label", order=sorted(distributions))
|
|
graph.set_title(id)
|
|
graph.yaxis.set_major_formatter(EngFormatter(unit=FormatterUnit))
|
|
graph.yaxis.set_label_text(Label)
|
|
_ = plt.xticks(rotation=90)
|
|
plt.show()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Process benchmark json files.")
|
|
parser.add_argument("--mode", choices=["time", "cycles", "bytespercycle"], default="time", help="Use to display either 'time', 'cycles' or 'bytes/cycle'.")
|
|
parser.add_argument("files", nargs="+", help="The json files to read from.")
|
|
|
|
args = parser.parse_args()
|
|
study_groups = dict()
|
|
for file in args.files:
|
|
with open(file) as json_file:
|
|
json_obj = json.load(json_file)
|
|
Id = getId(json_obj)
|
|
if Id in study_groups:
|
|
study_groups[Id].append(json_obj)
|
|
else:
|
|
study_groups[Id] = [json_obj]
|
|
|
|
plt.tight_layout()
|
|
sns.set_theme(style="ticks")
|
|
for id, study_collection in study_groups.items():
|
|
if "(Sweep)" in id:
|
|
displaySweepData(id, study_collection, args.mode)
|
|
else:
|
|
displayDistributionData(id, study_collection, args.mode)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|