import plotly.graph_objects as go
from plotly.subplots import make_subplots
import hashlib
import math
TRIM_PERCENT = 0.10
def get_color(name):
hash_object = hashlib.md5(name.encode())
color_index = int(hash_object.hexdigest(), 16) % 360
return f'hsl({color_index}, 90%, 30%)'
def plot_results(results, output_file, input_file):
# determine the number of loops we have data for
total_loops = set()
for x_values in results.values():
for xx_values in x_values.values():
total_loops.update(xx_values.keys())
total_loops = sorted(total_loops)
num_loops = len(total_loops)
loop_to_col = {loop: idx + 1 for idx, loop in enumerate(total_loops)}
fig = make_subplots(
rows=4,
cols=num_loops,
subplot_titles=[f"{loop}" for loop in total_loops] * 4,
vertical_spacing=0.1,
horizontal_spacing=0.05,
shared_xaxes=True,
shared_yaxes=False,
)
for coro_name, x_values in results.items():
for speedup, xx_values in x_values.items():
for loop, experiments in xx_values.items():
col = loop_to_col[loop]
x_list = []
y_latency_list = []
y_throughput_list = []
y_max_latency_list = []
y_num_callbacks_list = []
latency_hover_text = []
max_latency_hover_text = []
for experiment in experiments:
completed_callbacks = experiment[0]
virtual_run_time = experiment[1]
x_val = speedup * 100
x_list.append(x_val)
num_callbacks = len(completed_callbacks)
y_num_callbacks_list.append(num_callbacks)
# handle average latency graph
if num_callbacks > 0:
trim_count = math.floor(num_callbacks * TRIM_PERCENT / 2)
sorted_callbacks = sorted(completed_callbacks, key=lambda cb: cb[1])
trimmed_callbacks = (
sorted_callbacks[trim_count: len(sorted_callbacks) - trim_count]
if trim_count > 0 else sorted_callbacks
)
trimmed_latencies = [cb[1] for cb in trimmed_callbacks]
latency = sum(trimmed_latencies) / len(trimmed_latencies)
y_latency_list.append(latency)
max_cb = max(completed_callbacks, key=lambda cb: cb[1])
y_max_latency_list.append(max_cb[1])
breakdown = "
".join([f" {cb[0]}: {round(cb[1], 4)}" for cb in trimmed_callbacks])
latency_hover_text.append(
f"{coro_name}
Speedup: {speedup}
Trimmed Average Wait ({int(TRIM_PERCENT*100)}%): {round(latency, 4)}
Breakdown:
{breakdown}"
)
max_latency_hover_text.append(
f"{coro_name}
Speedup: {speedup}
Max Wait: {round(max_cb[1], 4)}
Handle: {max_cb[0]}"
)
else:
latency = 0
y_latency_list.append(latency)
latency_hover_text.append(f"{coro_name}
Speedup: {speedup}
No callbacks")
max_latency_hover_text.append(f"{coro_name}
Speedup: {speedup}
No callbacks")
# handle throughput graph
throughput = num_callbacks / virtual_run_time if virtual_run_time else 0
y_throughput_list.append(throughput)
fig.add_trace(go.Scatter(
x=x_list,
y=y_latency_list,
mode='markers',
name=coro_name,
marker=dict(color=get_color(coro_name)),
hovertext=latency_hover_text,
showlegend=True,
), row=1, col=col)
fig.add_trace(go.Scatter(
x=x_list,
y=y_throughput_list,
mode='markers',
name=coro_name,
marker=dict(color=get_color(coro_name)),
showlegend=False,
), row=2, col=col)
fig.add_trace(go.Scatter(
x=x_list,
y=y_max_latency_list,
mode='markers',
name=coro_name,
marker=dict(color=get_color(coro_name)),
hovertext=max_latency_hover_text,
showlegend=False,
), row=3, col=col)
fig.add_trace(go.Scatter(
x=x_list,
y=y_num_callbacks_list,
mode='markers',
name=coro_name,
marker=dict(color=get_color(coro_name)),
showlegend=False,
), row=4, col=col)
fig.update_layout(
height=1080,
width=1920 * num_loops,
title_text=f"Coroutine Performance Metrics: {input_file}",
showlegend=False,
)
for col in range(1, num_loops + 1):
fig.update_xaxes(title_text="speedup (% optimized away)", row=4, col=col)
fig.update_xaxes(showticklabels=True, col=col)
fig.update_xaxes(showticklabels=True, row=2, col=col)
fig.update_xaxes(showticklabels=True, row=3, col=col)
fig.update_yaxes(title_text="average latency (seconds)", row=1, col=1)
fig.update_yaxes(title_text="throughput (handles per second)", row=2, col=1)
fig.update_yaxes(title_text="maximum latency (seconds)", row=3, col=1)
fig.update_yaxes(title_text="# of callbacks", row=4, col=1)
fig.write_html(output_file)