llvm-project/libc/benchmarks/libc-benchmark-analysis.py3

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

129 lines
4.7 KiB
Plaintext
Raw Normal View History

"""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()