metrale_bench/benchmarks/video/
concurrency.rs1use std::time::{Duration, Instant};
17
18use crate::http::{self, ChatOutcome};
19use crate::plugin::PluginHandle;
20
21pub const LEVELS: &[usize] = &[1, 2, 4];
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct LevelResult {
28 pub conc: usize,
29 pub returned: usize,
31 pub correct: usize,
33 pub distinct_token_counts: usize,
36 pub prompt_tokens: Option<usize>,
39 pub wall_ms: u128,
40 pub errors: Vec<String>,
41}
42
43impl LevelResult {
44 pub fn ok(&self) -> bool {
45 self.correct == self.conc && self.distinct_token_counts == 1 && self.prompt_tokens.is_some()
46 }
47
48 pub fn ok_against(&self, baseline_prompt_tokens: usize) -> bool {
51 self.ok() && self.prompt_tokens == Some(baseline_prompt_tokens)
52 }
53
54 pub fn geometry_detail(&self, baseline_prompt_tokens: Option<usize>) -> String {
55 match (self.prompt_tokens, baseline_prompt_tokens) {
56 (Some(got), Some(want)) if got != want => {
57 format!("{got} prompt tokens, C=1 baseline {want}")
58 }
59 (Some(got), _) => format!("one geometry ({got} prompt tokens)"),
60 (None, _) => format!("{} distinct token counts", self.distinct_token_counts),
61 }
62 }
63}
64
65pub fn sweep_ok(results: &[LevelResult]) -> bool {
68 let Some(baseline_prompt_tokens) = results.first().and_then(|r| r.prompt_tokens) else {
69 return false;
70 };
71 results.len() == LEVELS.len()
72 && results
73 .iter()
74 .zip(LEVELS)
75 .all(|(r, &level)| r.conc == level && r.ok_against(baseline_prompt_tokens))
76}
77
78pub async fn run_level(
81 handle: &PluginHandle,
82 body: &serde_json::Value,
83 conc: usize,
84 timeout: Duration,
85 is_correct: &(dyn Fn(&str) -> bool + Sync),
89) -> LevelResult {
90 let start = Instant::now();
91 let futures: Vec<_> = (0..conc)
92 .map(|_| http::chat_stream(handle.target(), body, timeout))
93 .collect();
94 let outcomes: Vec<anyhow::Result<ChatOutcome>> = futures::future::join_all(futures).await;
95 let wall_ms = start.elapsed().as_millis();
96
97 let mut returned = 0usize;
98 let mut correct = 0usize;
99 let mut counts: Vec<usize> = Vec::new();
100 let mut errors = Vec::new();
101 for o in outcomes {
102 match o {
103 Ok(out) => {
104 returned += 1;
105 counts.push(out.prompt_tokens);
106 if is_correct(out.text.trim()) {
107 correct += 1;
108 }
109 }
110 Err(e) => errors.push(crate::benchmarks::one_line(format!("{e:#}"))),
111 }
112 }
113 counts.sort_unstable();
114 counts.dedup();
115 let prompt_tokens = (counts.len() == 1).then(|| counts[0]);
116 LevelResult {
117 conc,
118 returned,
119 correct,
120 distinct_token_counts: counts.len(),
121 prompt_tokens,
122 wall_ms,
123 errors,
124 }
125}
126
127#[cfg(test)]
128#[path = "concurrency_tests.rs"]
129mod concurrency_tests;