import SATestUtils as utils
from SATestBuild import ProjectTester, stdout, TestInfo
from ProjectMap import ProjectInfo
import pandas as pd
from typing import List, Tuple
INDEX_COLUMN = "index"
def _save(data: pd.DataFrame, file_path: str):
data.to_csv(file_path, index_label=INDEX_COLUMN)
def _load(file_path: str) -> pd.DataFrame:
return pd.read_csv(file_path, index_col=INDEX_COLUMN)
class Benchmark:
def __init__(self, projects: List[ProjectInfo], iterations: int,
output_path: str):
self.projects = projects
self.iterations = iterations
self.out = output_path
def run(self):
results = [self._benchmark_project(project)
for project in self.projects]
data = pd.concat(results, ignore_index=True)
_save(data, self.out)
def _benchmark_project(self, project: ProjectInfo) -> pd.DataFrame:
if not project.enabled:
stdout(f" \n\n--- Skipping disabled project {project.name}\n")
return
stdout(f" \n\n--- Benchmarking project {project.name}\n")
test_info = TestInfo(project)
tester = ProjectTester(test_info, silent=True)
project_dir = tester.get_project_dir()
output_dir = tester.get_output_dir()
raw_data = []
for i in range(self.iterations):
stdout(f"Iteration #{i + 1}")
time, mem = tester.build(project_dir, output_dir)
raw_data.append({"time": time, "memory": mem,
"iteration": i, "project": project.name})
stdout(f"time: {utils.time_to_str(time)}, "
f"peak memory: {utils.memory_to_str(mem)}")
return pd.DataFrame(raw_data)
def compare(old_path: str, new_path: str, plot_file: str):
old = _load(old_path)
new = _load(new_path)
old_projects = set(old["project"])
new_projects = set(new["project"])
common_projects = old_projects & new_projects
old = old[old["project"].isin(common_projects)]
new = new[new["project"].isin(common_projects)]
old, new = _normalize(old, new)
old["kind"] = "old"
new["kind"] = "new"
data = pd.concat([old, new], ignore_index=True)
_plot(data, plot_file)
def _normalize(old: pd.DataFrame,
new: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]:
means = old.groupby("project").mean()
return _normalize_impl(old, means), _normalize_impl(new, means)
def _normalize_impl(data: pd.DataFrame, means: pd.DataFrame):
joined_data = data.merge(means, on="project", suffixes=("", "_mean"))
_normalize_key(joined_data, "time")
_normalize_key(joined_data, "memory")
return joined_data
def _normalize_key(data: pd.DataFrame, key: str):
norm_key = _normalized_name(key)
mean_key = f"{key}_mean"
data[norm_key] = data[key] / data[mean_key]
def _normalized_name(name: str) -> str:
return f"normalized {name}"
def _plot(data: pd.DataFrame, plot_file: str):
import matplotlib
import seaborn as sns
from matplotlib import pyplot as plt
sns.set_style("whitegrid")
figure, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6))
def _subplot(key: str, ax: matplotlib.axes.Axes):
sns.boxplot(x="project", y=_normalized_name(key), hue="kind",
data=data, palette=sns.color_palette("BrBG", 2), ax=ax)
_subplot("time", ax1)
ax1.set_xlabel("")
_subplot("memory", ax2)
ax2.get_legend().remove()
figure.savefig(plot_file)