From d772fa027de4f5c355b3762293f6e5cc981ccba6 Mon Sep 17 00:00:00 2001
From: Valentin Trophime <valentin.trophime@inria.fr>
Date: Wed, 23 Oct 2024 15:43:35 +0200
Subject: [PATCH] update scipts to get cdf function

---
 examples/overhead.rs     |  33 +++++----
 scripts/heatmap.py       | 146 ++++++++++++++++++++++++++++++---------
 scripts/requirements.txt |  17 +++++
 3 files changed, 148 insertions(+), 48 deletions(-)
 create mode 100644 scripts/requirements.txt

diff --git a/examples/overhead.rs b/examples/overhead.rs
index 06df51a..5a06c1e 100644
--- a/examples/overhead.rs
+++ b/examples/overhead.rs
@@ -42,23 +42,26 @@ struct Args {
     reactivity_us: u64,
     #[arg(long)]
     n_iter: u32,
+    #[arg(long, default_value_t = 1)]
+    repeat: u32,
+    #[arg(long, default_value_t = false)]
+    output_ns: bool,
 }
 
 fn main() {
     let args = Args::parse();
-    let time_ref = measure_busy_wait(args);
-    let _ = embassy_futures::block_on(measure_iter(args));
-    let time_iter = embassy_futures::block_on(measure_iter(args));
-    let duration = std::time::Duration::from_micros(args.duration_task);
-    println!(
-        "Baseline: {} busy_sleep of {:?} took {:?}",
-        args.n_iter, duration, time_ref
-    );
-    println!(
-        "Preemptive {} iters total time {:?}",
-        args.n_iter, time_iter
-    );
-    let baseline = time_ref.as_secs_f64();
-    let overhead = (time_iter.as_secs_f64() - baseline) / baseline;
-    println!("Preemptive iterator overhead {:.4}%", overhead * 100.0);
+    for _ in 0..args.repeat {
+        let time_ref = measure_busy_wait(args);
+        let time_iter = embassy_futures::block_on(measure_iter(args));
+        if args.output_ns {
+            println!("Baseline: {}", time_ref.as_nanos());
+            println!("Preemptive: {}", time_iter.as_nanos());
+        } else {
+            println!("Baseline: {:?}", time_ref);
+            println!("Preemptive: {:?}", time_iter);
+            let baseline = time_ref.as_secs_f64();
+            let overhead = (time_iter.as_secs_f64() - baseline) / baseline;
+            println!("Preemptive iterator overhead {:.4}%", overhead * 100.0);
+        }
+    }
 }
diff --git a/scripts/heatmap.py b/scripts/heatmap.py
index ac05d78..40cd3d8 100755
--- a/scripts/heatmap.py
+++ b/scripts/heatmap.py
@@ -3,22 +3,26 @@
 import argparse
 import subprocess
 import itertools
+from bisect import bisect_left
 import os
 import re
 import pandas
+
+import scipy
 import matplotlib.pyplot as plt
 import numpy as np
 
 PARAMETERS = {
-    "REACTIVITY": [10],
+    "Reactivity": [10],
     "DurationTask": [10, 100, 1_000],
     "N_Iter": [100, 1_000, 10_000],
 }
-CSV_COLS = ["REACTIVITY", "DurationTask", "N_Iter", "Overhead"]
+CSV_COLS = ["Reactivity", "DurationTask", "N_Iter", "Baseline", "Preemptive"]
 N_REPEAT = 100
 
 EXEC = "target/release/examples/overhead"
-REGEX = re.compile(r"Preemptive iterator overhead (-?\d+\.\d+)")
+REGEX_BASELINE = re.compile(r"Baseline: (\d+)")
+REGEX_PREEMPT = re.compile(r"Preemptive: (\d+)")
 
 CONFIDENCE_INTERVAL = 0.95
 
@@ -37,7 +41,7 @@ STATS = ["min", "max", "median", "mean", "std", percent_low, percent_up]
 GATHER_STATS = {"Overhead": STATS}
 
 
-def collect_heatmap(out_csv: str):
+def collect_heatmap(out_csv: str, repeat: int):
     if os.path.exists(out_csv):
         user_input = input("Remove old csv (y/n) ?")
         if user_input.startswith("y"):
@@ -48,31 +52,34 @@ def collect_heatmap(out_csv: str):
     with open(out_csv, "w") as f:
         f.write(",".join(CSV_COLS) + "\n")
         for reac, dur, niter in itertools.product(*PARAMETERS.values()):
-            for _ in range(N_REPEAT):
-                out = subprocess.run(
-                    [
-                        EXEC,
-                        "--duration-task",
-                        str(dur),
-                        "--reactivity-us",
-                        str(reac),
-                        "--n-iter",
-                        str(niter),
-                    ],
-                    check=True,
-                    text=True,
-                    stdout=subprocess.PIPE,
-                )
-                assert out.stdout
-                capture = re.search(REGEX, out.stdout)
-                assert capture
-                overhead = capture.group(1)
-                f.write(f"{reac},{dur},{niter},{overhead}\n")
-                print(f"{reac},{dur},{niter},{overhead}")
+            out = subprocess.run(
+                [
+                    EXEC,
+                    "--duration-task",
+                    str(dur),
+                    "--reactivity-us",
+                    str(reac),
+                    "--n-iter",
+                    str(niter),
+                    "--repeat",
+                    str(repeat),
+                    "--output-ns",
+                ],
+                check=True,
+                text=True,
+                stdout=subprocess.PIPE,
+            )
+            assert out.stdout
+            baselines = re.finditer(REGEX_BASELINE, out.stdout)
+            premptives = re.finditer(REGEX_PREEMPT, out.stdout)
+            for cb, cp in zip(baselines, premptives):
+                f.write(f"{reac},{dur},{niter},{cb.group(1)},{cp.group(1)}\n")
+            print(f"{reac=} {dur=} {niter=} done")
 
 
 def plot_heatmap(path: str):
     df = pandas.read_csv(path)
+    df["Overhead"] = (df["Preemptive"] - df["Baseline"]) / df["Baseline"] * 100.0
     print(df)
     df = df.groupby(list(PARAMETERS)).agg(GATHER_STATS).reset_index()
     print(df)
@@ -111,24 +118,97 @@ def plot_heatmap(path: str):
     ax.set_xlabel("Duration of each iteration in approx e-7 secs.")
     ax.set_ylabel("Number of iterations.")
     ax.set_title(
-        f"Overhead in % of the iteration using preemptive iter vs native iter. (reactivity = {PARAMETERS['REACTIVITY'][0]} μs)"
+        f"Overhead in % of the iteration using preemptive iter vs native iter. (reactivity = {PARAMETERS['Reactivity'][0]} μs)"
     )
     plt.show()
 
 
+def plot_likelyhood(path: str, n_iter: int, task_dur: int, reac: int):
+    print(f"{task_dur=} {n_iter=} {reac=}")
+    df = pandas.read_csv(path)
+    df_filtered = df[
+        (df["N_Iter"] == n_iter)
+        & (df["DurationTask"] == task_dur)
+        & (df["Reactivity"] == reac)
+    ]
+    assert len(df_filtered) > 0
+    baselines = df_filtered["Baseline"].values
+    preemptives = df_filtered["Preemptive"].values
+    overhead2 = np.zeros(len(baselines) * len(preemptives))
+    i = 0
+    for base in baselines:
+        for t in preemptives:
+            overhead2[i] = (t - base) / base * 100.0
+            i += 1
+    overhead2.sort()
+    estimated = scipy.stats.ecdf(overhead2)
+    x = np.linspace(-5, 100, 10000)
+    y = estimated.cdf.evaluate(x)
+    plt.plot(x, y, label="ecdf")
+    cdf = scipy.stats.norm.cdf(overhead2)
+    plt.plot(overhead2, cdf, label="cdf")
+    plt.xlabel("x in %")
+    plt.xlim([-5, 50])
+    plt.ylabel("P(X < x)")
+    plt.legend()
+    plt.title("Probability than the real overhead is below x")
+    x_95 = x[bisect_left(y, 0.95)]
+    x_99 = x[bisect_left(y, 0.99)]
+    print(f"P(Overhead < x) < 95% for x = {x_95}")
+    print(f"P(Overhead < x) < 99% for x = {x_99}")
+    plt.show()
+
+
 def main():
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
     )
-    parser.add_argument("--collect-csv", help="File to generate.")
-    parser.add_argument("--plot-csv", help="File to user for plot.")
-    args = parser.parse_args()
+    subparsers = parser.add_subparsers(dest="subparser_name")
+    parser_collect = subparsers.add_parser(
+        "collect", formatter_class=argparse.ArgumentDefaultsHelpFormatter
+    )
+    parser_collect.add_argument("output_csv", help="File to generate.")
+    parser_collect.add_argument(
+        "--repeat",
+        type=int,
+        help="Number of times to repeat each run.",
+        default=N_REPEAT,
+    )
 
-    if args.plot_csv:
-        plot_heatmap(args.plot_csv)
+    parser_plot = subparsers.add_parser("plot")
+    parser_plot.add_argument("input_csv", help="File to use for plot.")
+
+    parser_likelyhood = subparsers.add_parser("likelyhood")
+    parser_likelyhood.add_argument("input_csv", help="File to use for plot.")
+    parser_likelyhood.add_argument(
+        "--n-iter",
+        help="Value of N_Iter to fix.",
+        type=int,
+        default=PARAMETERS["N_Iter"][0],
+    )
+    parser_likelyhood.add_argument(
+        "--task-dur",
+        help="Value of DurationTask to fix.",
+        type=int,
+        default=PARAMETERS["DurationTask"][0],
+    )
+    parser_likelyhood.add_argument(
+        "--reac",
+        help="Value of Reactivity to fix.",
+        type=int,
+        default=PARAMETERS["Reactivity"][0],
+    )
+    args = parser.parse_args()
 
-    if args.collect_csv:
-        collect_heatmap(args.collect_csv)
+    match args.subparser_name:
+        case "collect":
+            collect_heatmap(args.output_csv, args.repeat)
+        case "plot":
+            plot_heatmap(args.input_csv)
+        case "likelyhood":
+            plot_likelyhood(args.input_csv, args.n_iter, args.task_dur, args.reac)
+        case other:
+            print(f"Unknown command: {other}")
 
 
 if __name__ == "__main__":
diff --git a/scripts/requirements.txt b/scripts/requirements.txt
new file mode 100644
index 0000000..823c63c
--- /dev/null
+++ b/scripts/requirements.txt
@@ -0,0 +1,17 @@
+contourpy==1.3.0
+cycler==0.12.1
+fonttools==4.54.1
+kiwisolver==1.4.7
+matplotlib==3.9.2
+numpy==2.1.1
+packaging==24.1
+pandas==2.2.3
+pillow==10.4.0
+pyparsing==3.1.4
+python-dateutil==2.9.0.post0
+pytz==2024.2
+scipy==1.14.1
+seaborn==0.13.2
+six==1.16.0
+tabulate==0.9.0
+tzdata==2024.2
-- 
GitLab