diff --git a/analysis/values/slowstart.py b/analysis/values/slowstart.py new file mode 100644 index 0000000..49219a6 --- /dev/null +++ b/analysis/values/slowstart.py @@ -0,0 +1,33 @@ +"""Initial ssthresh per (solution, qlen) from slowstart raw text files.""" +import os +from pathlib import Path + +import numpy as np + + +def _read(path: Path) -> dict[int, np.ndarray]: + out = {} + for line in path.read_text().splitlines(): + line = line.strip() + if not line: + continue + qlen, vals = line.split(":", 1) + out[int(qlen)] = np.array([float(v) for v in vals.split()]) + return out + + +def compute(derived: Path) -> tuple[dict[str, str], list[Path]]: + raw_root = Path(os.environ.get("RAW_DATA_ROOT", derived.parent / "raw_data")) + raw = raw_root / "slowstart" + files = {"tso": raw / "tso.txt", "tso-pacing": raw / "tso_pacing.txt"} + if not all(p.exists() for p in files.values()): + return {}, [] + + out: dict[str, str] = {} + for sol, path in files.items(): + for qlen, vals in _read(path).items(): + base = f"slowstart/{sol}/qlen-{qlen}" + out[f"{base}/mean-pkts"] = f"{vals.mean():.0f}" + out[f"{base}/sd-pkts"] = f"{vals.std(ddof=1):.0f}" + out[f"{base}/n-runs"] = str(vals.size) + return out, list(files.values()) diff --git a/figures/fig_slowstart.R b/figures/fig_slowstart.R new file mode 100644 index 0000000..be0796c --- /dev/null +++ b/figures/fig_slowstart.R @@ -0,0 +1,46 @@ +#!/usr/bin/env Rscript +source("common.R") + +parser <- fig_parser(description = "Initial ssthresh vs bottleneck queue") +parser$add_argument("--ymin", type = "double") +parser$add_argument("--ymax", type = "double") +parser$add_argument("--ystep", type = "double") +args <- parser$parse_args() + +read_runs <- function(path, label) { + lines <- readLines(path) + do.call(rbind, lapply(lines, function(ln) { + parts <- strsplit(ln, ":", fixed = TRUE)[[1]] + qlen <- as.integer(trimws(parts[1])) + vals <- as.numeric(strsplit(trimws(parts[2]), "\\s+")[[1]]) + data.frame(solution = label, qlen = qlen, ssthresh = vals) + })) +} + +runs <- rbind( + read_runs(file.path(args$data, "tso.txt"), "tso"), + read_runs(file.path(args$data, "tso_pacing.txt"), "tso-pacing") +) %>% + prepare_solution() %>% + group_by(solution, qlen) %>% + summarise(mean = mean(ssthresh), + sd = sd(ssthresh), .groups = "drop") + +p <- ggplot(runs, aes(x = factor(qlen), y = mean, fill = solution)) + + geom_col(position = position_dodge(width = 0.75), width = 0.65, colour = "black") + + geom_errorbar(aes(ymin = mean - sd, ymax = mean + sd), + position = position_dodge(width = 0.75), width = 0.25) + + scale_fill_manual(values = LABEL_COLORS) + + labs(x = "Bottleneck queue limit (pkts)", y = "Max CWND (pkts)") + + theme_paper() + + theme_debug_margins(args) + +if (!is.null(args$ymax)) { + ymin <- if (is.null(args$ymin)) 0 else args$ymin + p <- p + coord_cartesian(ylim = c(ymin, args$ymax)) + if (!is.null(args$ystep)) { + p <- p + scale_y_continuous(breaks = seq(ymin, args$ymax, args$ystep)) + } +} + +save_figure(p, args)