aboutsummaryrefslogtreecommitdiffstats
path: root/src/simtest/bench.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/simtest/bench.rs')
-rw-r--r--src/simtest/bench.rs90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/simtest/bench.rs b/src/simtest/bench.rs
index 8368f7d..30a43c7 100644
--- a/src/simtest/bench.rs
+++ b/src/simtest/bench.rs
@@ -567,6 +567,9 @@ async fn run(seed: u64, scenario: &'static Scenario) -> String {
converged,
);
trees(&mut out, &nodes, &sim.net).await;
+ if let Some(writer) = writer {
+ slowest(&mut out, &nodes, &nodes[writer], &sim.net).await;
+ }
}
out
}
@@ -646,6 +649,93 @@ async fn trees(out: &mut String, nodes: &[AppState], net: &crate::transport::sim
);
}
+/// The members `writer`'s changes take longest to reach along its tree, by the links'
+/// latencies alone (the parents as each member chose them, `swarm::tree::own_place`),
+/// those later than `LATE_MS`: how far each is from the writer, its parent and the
+/// latency to it, and the members it could take instead (they come before it) and the
+/// fastest link to one. One way, in milliseconds.
+async fn slowest(
+ out: &mut String,
+ nodes: &[AppState],
+ writer: &AppState,
+ net: &crate::transport::sim::SimNet,
+) {
+ let names: HashMap<String, String> = nodes
+ .iter()
+ .filter_map(|node| Some((node.local_id().to_owned(), node.alias.clone()?)))
+ .collect();
+ let name = |id: &str| names.get(id).cloned().unwrap_or_else(|| id.to_owned());
+ let latency =
+ |from: &str, to: &str| net.mean_delay(&name(from), &name(to)).as_secs_f64() * 1000.0;
+ let mut places = HashMap::new();
+ for node in nodes {
+ if node.local_id() != writer.local_id()
+ && let Some(place) = crate::swarm::tree::own_place(node, writer.local_id()).await
+ {
+ places.insert(node.local_id().to_owned(), place);
+ }
+ }
+ // Arrival along parents; None where the chain ends without the writer.
+ fn arrival(
+ member: &str,
+ writer: &str,
+ places: &HashMap<String, crate::swarm::tree::Place>,
+ latency: &dyn Fn(&str, &str) -> f64,
+ depth: usize,
+ ) -> Option<f64> {
+ if member == writer {
+ return Some(0.0);
+ }
+ let parent = places.get(member)?.parent.as_deref()?;
+ if depth > places.len() {
+ return None;
+ }
+ Some(arrival(parent, writer, places, latency, depth + 1)? + latency(parent, member))
+ }
+ let mut late: Vec<(f64, &String)> = places
+ .keys()
+ .filter_map(|member| {
+ arrival(member, writer.local_id(), &places, &latency, 0).map(|at| (at, member))
+ })
+ .filter(|(at, _)| *at > LATE_MS as f64)
+ .collect();
+ if late.is_empty() {
+ return;
+ }
+ late.sort_by(|a, b| b.0.total_cmp(&a.0));
+ let listed: Vec<String> = late
+ .iter()
+ .take(5)
+ .map(|(at, member)| {
+ let place = &places[member.as_str()];
+ let parent = place.parent.as_deref().unwrap_or_default();
+ let fastest = place
+ .before
+ .iter()
+ .map(|other| (latency(other, member), other))
+ .min_by(|a, b| a.0.total_cmp(&b.0));
+ format!(
+ "{} {at:.0} ms ({} hops; from {} over {:.0} ms; of {} it could take, {})",
+ name(member),
+ place.distance,
+ name(parent),
+ latency(parent, member),
+ place.before.len(),
+ fastest.map_or_else(String::new, |(ms, other)| format!(
+ "{} over {ms:.0} ms",
+ name(other)
+ )),
+ )
+ })
+ .collect();
+ let _ = writeln!(
+ out,
+ " later than {LATE_MS} ms by the links along the writer's tree: {}{}",
+ listed.join("; "),
+ if late.len() > 5 { "; …" } else { "" }
+ );
+}
+
/// The workload's counts (`during`), and the quiet phase's for as long.
fn report(
out: &mut String,