mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-12 01:14:28 -05:00
d612ed2cae
- Get the previous benchmark results from artifact store S3 bucket - Compare the current benchmark to the previous results - Attach markdown table comparing results to the workflow output - Update benchmarks in bucket if running on main - Generate graphs from comparison - Document continous benchmarking
195 lines
6.2 KiB
Python
195 lines
6.2 KiB
Python
"""Generate graphs comparing K-Bench benchmarks across cloud providers and Constellation."""
|
|
import json
|
|
import os
|
|
from collections import defaultdict
|
|
|
|
import numpy as np
|
|
from matplotlib import pyplot as plt
|
|
|
|
SUBJECTS = [
|
|
'constellation-azure',
|
|
'AKS',
|
|
'constellation-gcp',
|
|
'GKE',
|
|
]
|
|
|
|
LEGEND_NAMES = [
|
|
'Constellation on Azure',
|
|
'AKS',
|
|
'Constellation on GCP',
|
|
'GKE',
|
|
]
|
|
|
|
BAR_COLORS = ['#90FF99', '#929292', '#8B04DD', '#000000']
|
|
|
|
# Rotate bar labels by X degrees
|
|
LABEL_ROTATE_BY = 30
|
|
LABEL_FONTSIZE = 9
|
|
|
|
# Some lookup dictionaries for x axis
|
|
api_suffix = 'ms'
|
|
pod_key2header = {
|
|
'pod_create': 'Pod Create',
|
|
'pod_list': 'Pod List',
|
|
'pod_get': 'Pod Get',
|
|
'pod_update': 'Pod Update',
|
|
'pod_delete': 'Pod Delete',
|
|
}
|
|
svc_key2header = {
|
|
'svc_create': 'Service Create',
|
|
'svc_list': 'Service List',
|
|
'svc_update': 'Service Update',
|
|
'svc_delete': 'Service Delete',
|
|
'svc_get': 'Service Get',
|
|
}
|
|
depl_key2header = {
|
|
'depl_create': 'Deployment Create',
|
|
'depl_list': 'Deployment List',
|
|
'depl_update': 'Deployment Update',
|
|
'depl_scale': 'Deployment Scale',
|
|
'depl_delete': 'Deployment Delete',
|
|
}
|
|
|
|
fio_suffix = 'MiB/s'
|
|
fio_key2header = {
|
|
'fio_root_async_R70W30_R': 'async_R70W30 mix,\n seq. reads',
|
|
'fio_root_async_R70W30_W': 'async_R70W30 mix,\n seq. writes',
|
|
'fio_root_async_R100W0_R': 'async_R100W0 mix,\n seq. reads',
|
|
'fio_root_async_R0W100_W': 'async_R0W100 mix,\n seq. writes',
|
|
}
|
|
|
|
net_suffix = 'Mbit/s'
|
|
net_key2header = {
|
|
'net_internode_snd': 'iperf internode \n send ({net_suffix})'.format(net_suffix=net_suffix),
|
|
'net_intranode_snd': 'iperf intranode \n send ({net_suffix})'.format(net_suffix=net_suffix),
|
|
}
|
|
|
|
|
|
def configure() -> str:
|
|
"""Read the benchmark data paths.
|
|
|
|
Expects ENV vars (required):
|
|
- BDIR=benchmarks
|
|
|
|
Raises TypeError if at least one of them is missing.
|
|
|
|
Returns: out_dir
|
|
"""
|
|
out_dir = os.environ.get('BDIR', None)
|
|
if not out_dir:
|
|
raise TypeError(
|
|
'ENV variables BDIR is required.')
|
|
return out_dir
|
|
|
|
|
|
def bar_chart(data, headers, title='', suffix='', val_label=True, y_log=False):
|
|
"""Draws a bar chart with multiple bars per data point.
|
|
|
|
Args:
|
|
data (dict[str, list]): Benchmark data dictionary: subject -> lists of value points
|
|
headers (list): List of headers (x-axis).
|
|
title (str, optional): The title for the chart. Defaults to "".
|
|
suffix (str, optional): The suffix for values e.g. "MiB/s". Defaults to "".
|
|
val_label (bool, optional): Put a label of the value over the bar chart. Defaults to True.
|
|
y_log (bool, optional): Set the y-axis to a logarithmic scale. Defaults to False.
|
|
Returns:
|
|
fig (matplotlib.pyplot.figure): The pyplot figure
|
|
"""
|
|
fig, ax = plt.subplots(figsize=(10, 5))
|
|
fig.patch.set_facecolor('white')
|
|
|
|
# Number of bars per group
|
|
n_bars = len(data)
|
|
|
|
# The width of a single bar
|
|
bar_width = 0.8 / n_bars
|
|
|
|
# List containing handles for the drawn bars, used for the legend
|
|
bars = []
|
|
|
|
# Iterate over all data
|
|
for i, values in enumerate(data.values()):
|
|
# The offset in x direction of that bar
|
|
x_offset = (i - n_bars / 2) * bar_width + bar_width / 2
|
|
|
|
# Draw a bar for every value of that type
|
|
for x, y in enumerate(values):
|
|
bar = ax.bar(x + x_offset, y, width=bar_width * 0.9,
|
|
color=BAR_COLORS[i % len(BAR_COLORS)], edgecolor='black')
|
|
if val_label:
|
|
ax.bar_label(bar, padding=1,
|
|
fmt='%g {suffix}'.format(suffix=suffix))
|
|
# Add a handle to the last drawn bar, which we'll need for the legend
|
|
bars.append(bar[0])
|
|
# Draw legend
|
|
ax.legend(bars, LEGEND_NAMES)
|
|
if y_log:
|
|
ax.set_yscale('log')
|
|
ax.set_xticks(np.arange(len(headers)))
|
|
ax.set_xticklabels(headers)
|
|
|
|
plt.setp(ax.get_xticklabels(), fontsize=LABEL_FONTSIZE,
|
|
rotation=LABEL_ROTATE_BY)
|
|
plt.title('{title} ({suffix})'.format(title=title, suffix=suffix))
|
|
plt.tight_layout()
|
|
return fig
|
|
|
|
|
|
def main():
|
|
"""Read the files and create diagrams."""
|
|
out_dir = configure()
|
|
combined_results = defaultdict(dict)
|
|
|
|
for test in SUBJECTS:
|
|
# Read the previous results
|
|
read_path = os.path.join(
|
|
out_dir, '{subject}.json'.format(subject=test))
|
|
try:
|
|
with open(read_path, 'r') as res_file:
|
|
combined_results[test].update(json.load(res_file))
|
|
except OSError as e:
|
|
raise ValueError(
|
|
'Failed reading {subject} benchmark records: {e}'.format(subject=test, e=e))
|
|
|
|
# Combine the evaluation of the Kubernetes API benchmarks
|
|
for i, api in enumerate([pod_key2header, svc_key2header, depl_key2header]):
|
|
api_data = {}
|
|
for s in SUBJECTS:
|
|
points = combined_results[s]['kbench']
|
|
subject_data = [points[h] for h in api]
|
|
api_data[s] = subject_data
|
|
hdrs = list(api.values())
|
|
bar_chart(data=api_data, headers=hdrs,
|
|
title='API Latency', suffix=api_suffix, y_log=True)
|
|
|
|
save_name = os.path.join(out_dir, 'api_{i}_perf.png'.format(i=i))
|
|
plt.savefig(save_name, bbox_inches='tight')
|
|
|
|
# Network chart
|
|
net_data = {}
|
|
for s in SUBJECTS:
|
|
points = combined_results[s]['kbench']
|
|
subject_data = [points[h] for h in net_key2header]
|
|
net_data[s] = subject_data
|
|
hdrs = list(net_key2header.values())
|
|
bar_chart(data=net_data, headers=hdrs,
|
|
title='Network Throughput', suffix=net_suffix, y_log=True)
|
|
save_name = os.path.join(out_dir, 'net_perf.png')
|
|
plt.savefig(save_name, bbox_inches='tight')
|
|
|
|
# fio chart
|
|
fio_data = {}
|
|
for s in SUBJECTS:
|
|
points = combined_results[s]['kbench']
|
|
subject_data = [points[h] for h in fio_key2header]
|
|
fio_data[s] = subject_data
|
|
hdrs = list(fio_key2header.values())
|
|
bar_chart(data=fio_data, headers=hdrs,
|
|
title='Storage Throughput', suffix=fio_suffix, y_log=True)
|
|
save_name = os.path.join(out_dir, 'storage_perf.png')
|
|
plt.savefig(save_name, bbox_inches='tight')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|