diff options
Diffstat (limited to 'src/simtest/bench.rs')
| -rw-r--r-- | src/simtest/bench.rs | 90 |
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, |
