In [4]:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
%matplotlib inline
In [5]:
# %load network-benchmark.py
#!/usr/bin/env python3
import configparser
from functools import lru_cache
import glob
import io
import os
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import pandas as pd
# import numpy as np
TEST_RESULT_ROOT = "/Users/esteele/tmp/wifi-test/root/wifi-test-results"
METADATA_FILENAME = "metadata.ini"
TEST_RESULT_GLOB = "*.csv"
TEST_RESULT_DIR_PATTERN = os.path.join(TEST_RESULT_ROOT, "test-run-{0}")
TEST_RESULT_METADATA_FILE = os.path.join(TEST_RESULT_DIR_PATTERN,
METADATA_FILENAME)
TEST_RESULT_FILES_PATTERN = os.path.join(TEST_RESULT_DIR_PATTERN,
TEST_RESULT_GLOB)
DEFAULT_REFERENCE_LINE_Y_VALUE = 250000
BW_DESC = {
125000: "360p bitrate",
250000: "480p bitrate",
500000: "720p bitrate",
}
def get_test_run_results(test_run_id):
test_result_files_glob = TEST_RESULT_FILES_PATTERN.format(test_run_id)
result_sio = io.StringIO()
for csv in glob.iglob(test_result_files_glob):
with open(csv) as f:
result_sio.write(f.read())
# Prime for reading
result_sio.seek(0)
return result_sio
def get_dataframe_from_test_run(test_run_id, downscale_factor=1):
raw_run_data = get_test_run_results(test_run_id)
summary_data = pd.read_csv(raw_run_data,
comment="#",
names=["client_id",
"timestamp",
"bytes_per_sec"])
# Create a time_offset column
summary_data["time_offset"] = summary_data['timestamp'] - \
min(summary_data['timestamp'])
summary_data["time_offset"] = \
(summary_data["time_offset"] // downscale_factor) * downscale_factor
summary_data["time_offset"] = \
pd.to_timedelta(summary_data['time_offset'], unit="s")
summary_data.set_index("time_offset", inplace=True)
return summary_data
def get_graph_title_for_run(test_run_id):
config = configparser.ConfigParser()
config.read(TEST_RESULT_METADATA_FILE.format(test_run_id))
# Don't count the global section
client_count = len(config.sections()) - 1
stream_count = sum([int(config[s]["parallel_run_count"])
for s in config.sections()
if s != "global"])
bandwidth_desc = "{0} bytes/sec".format(
config["global"]["test_bandwidth_bps"]
)
title = "Run {0} against {1} {2}.\n" \
"{3} streams between {4} clients. " \
"Each stream attempting {5}" \
.format(
test_run_id,
config["global"]["test_server_hostname"],
config["global"]["extra_run_description"],
stream_count,
client_count,
bandwidth_desc
)
return title
def get_graph_title_for_group(test_group_id):
run_ids = get_test_run_ids_for_group_id(test_group_id)
config = configparser.ConfigParser()
config.read(TEST_RESULT_METADATA_FILE.format(run_ids[0]))
# Don't count the global section
client_count = len(config.sections()) - 1
stream_count = sum([int(config[s]["parallel_run_count"])
for s in config.sections()
if s != "global"])
bandwidth_desc = "{0} bytes/sec".format(
config["global"]["test_bandwidth_bps"]
)
title = "Group run {0} against {1} {2} with {3} repeat runs.\n" \
"{4} streams between {5} clients. " \
"Each stream attempting {6}" \
.format(
test_group_id,
config["global"]["test_server_hostname"],
config["global"]["extra_run_description"],
len(run_ids),
stream_count,
client_count,
bandwidth_desc,
)
return title
def get_bw_annotation_detail(run_id):
config = configparser.ConfigParser()
config.read(TEST_RESULT_METADATA_FILE.format(run_id))
bandwidth_bps = int(config["global"]["test_bandwidth_bps"])
return bandwidth_bps, BW_DESC.get(bandwidth_bps, "")
def show_run_df_as_line_graph(run_id, ax):
config = configparser.ConfigParser()
config.read(TEST_RESULT_METADATA_FILE.format(run_id))
test_bandwidth_bps = int(config["global"]["test_bandwidth_bps"])
if test_bandwidth_bps in BW_DESC:
reference_line_y = test_bandwidth_bps
else:
reference_line_y = DEFAULT_REFERENCE_LINE_Y_VALUE
df = get_dataframe_from_test_run(run_id)
pivot_df = df.pivot(columns="client_id",
values="bytes_per_sec")
ax = pivot_df.plot(figsize=(20, 10), ax=ax)
ax.set_xlabel("Elapsed time (sec)")
ax.set_xbound(lower=0)
ax.set_ylabel("Throughput (bytes/sec)")
ax.axhline(y=reference_line_y,
color='0.75',
linestyle="--")
# Could also do va=bottom, ha=right to put the annotation in the graph
ax.annotate(BW_DESC[reference_line_y],
xy=(1.0, reference_line_y),
xycoords=("axes fraction", "data"),
va="center", ha="left")
ax.set_title(get_graph_title_for_run(run_id))
def show_multiple_run_ids_as_line_graph(run_ids):
_, axes = plt.subplots(1, len(run_ids),
sharex=True,
sharey=True,
squeeze=False)
for idx, run_id in enumerate(run_ids):
show_run_df_as_line_graph(
run_id,
ax=axes[0][idx]
)
def show_run_df_as_boxplot(df, title, annotation_xpoint, annotation_str):
ax = df.boxplot(column="bytes_per_sec",
by="time_offset",
figsize=(10, 5),
whis=[5, 95],
showfliers=False)
ax.set_xlabel("Elapsed time (sec)")
major_loc = ticker.AutoLocator()
major_fmt = ticker.FormatStrFormatter('%d')
ax.xaxis.set_major_locator(major_loc)
ax.xaxis.set_major_formatter(major_fmt)
ax.grid()
ax.set_ylabel("Throughput (bytes/sec)")
# An empty annotation string means that it doesn't correspond to a known
# bitrate, so we won't add an annotation
if annotation_str:
ax.axhline(y=250000, color='0.75', linestyle="--")
# Could also do va=bottom, ha=right to put the annotation in the graph
ax.annotate(annotation_str,
xy=(1.0, annotation_xpoint),
xycoords=("axes fraction", "data"),
va="center", ha="left")
ax.set_title(title)
# Nerf figure title
ax.get_figure().suptitle("")
@lru_cache()
def get_test_run_ids_for_group_id(test_group_id):
matching_run_ids = []
config = configparser.ConfigParser()
all_metadata_files = glob.glob(
os.path.join(TEST_RESULT_ROOT, "*", METADATA_FILENAME)
)
for metadata_file in all_metadata_files:
config.read(metadata_file)
if config.get("global", "test_group_id") == test_group_id:
matching_run_ids.append(config.get("global", "test_run_id"))
# can return duplicates but shouldn't... workaround in the meantime
return list(set(matching_run_ids))
def show_group_as_boxplot(test_group_id):
run_ids = get_test_run_ids_for_group_id(test_group_id)
group_df = pd.concat([get_dataframe_from_test_run(r) for r in run_ids])
annotation_details = get_bw_annotation_detail(run_ids[0])
show_run_df_as_boxplot(group_df, get_graph_title_for_group(test_group_id),
*annotation_details)
In [21]:
# Single runs
run_id = 2124364206 # pilot1 clients on 00
run_id = 1218829561 # pilot clients on AU
run_id = 2071502190 # 12@480p
run_id = 666798872 # 16@480p
run_id = 2056138332 # 20@360p
run_id = 1537579495 # 20@480p (802.11ac)
#run_id = 1577073988 # 20@480p (802.11n)
show_multiple_run_ids_as_line_graph([run_id])
In [ ]:
# Groups
"""
group_ids = [
"opiz2-test_inventories/6c6s-480p-@20ft-neoVopiz2-1805082127",
"pilot2-test_inventories/6c6s-480p-@20ft-neoVopiz2-1805082127",
"opiz2-test_inventories/7c7s-480p-@20ft-neoVopiz2-1805082127",
"pilot2-test_inventories/7c7s-480p-@20ft-neoVopiz2-1805082127",
"opiz2-test_inventories/9c9s-480p-@20ft-neoVopiz2-1805072151",
"pilot2-test_inventories/9c9s-480p-@20ft-neoVopiz2-1805072151",
"opiz2-test_inventories/10c10s-480p-@20ft-neoVopiz2-1805072151",
"pilot2-test_inventories/10c10s-480p-@20ft-neoVopiz2-1805072151",
"opiz2-test_inventories/11c11s-480p-@20ft-neoVopiz2-1805072151",
"pilot2-test_inventories/11c11s-480p-@20ft-neoVopiz2-1805072151",
"opiz2-test_inventories/8c8s-360p-@20ft-neoVopiz2-1805082127",
"pilot2-test_inventories/8c8s-360p-@20ft-neoVopiz2-1805082127",
"opiz2-test_inventories/10c10s-360p-@20ft-neoVopiz2-1805082127",
"pilot2-test_inventories/10c10s-360p-@20ft-neoVopiz2-1805082127",
"opiz2-test_inventories/12c12s-360p-@20ft-neoVopiz2-1805082127",
"pilot2-test_inventories/12c12s-360p-@20ft-neoVopiz2-1805082127",
]
"""
group_ids = [
"opiz2-test_inventories/7c7s-480p-@20ft-neoVopiz2-1805092129",
"pilot2-test_inventories/7c7s-480p-@20ft-neoVopiz2-1805092129",
"opiz2-test_inventories/8c8s-480p-@20ft-neoVopiz2-1805092129",
"pilot2-test_inventories/8c8s-480p-@20ft-neoVopiz2-1805092129",
"opiz2-test_inventories/9c9s-480p-@20ft-neoVopiz2-1805092129",
"pilot2-test_inventories/9c9s-480p-@20ft-neoVopiz2-1805092129",
"opiz2-test_inventories/10c10s-480p-@20ft-neoVopiz2-1805092129",
"pilot2-test_inventories/10c10s-480p-@20ft-neoVopiz2-1805092129",
]
for group_id in group_ids:
show_group_as_boxplot(group_id)
#show_multiple_run_ids_as_line_graph(get_test_run_ids_for_group_id(group_id))