1use crate::benchmarks;
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct BenchmarkOutput {
22 pub name: String,
23 pub success: bool,
24 pub execution_time_ms: u128,
25 pub results: serde_json::Value,
26 pub errors: Vec<String>,
27}
28
29pub fn available_benchmarks() -> Vec<&'static str> {
35 vec![
36 "gpu-capability",
37 "memory-bandwidth",
38 "tensor-core",
39 "gemm",
40 "pytorch",
41 "llama-cpp",
42 "flash-attention",
43 "flash-attention-ck",
44 "vllm",
45 "deepspeed",
46 "megatron",
47 "onnx",
48 "all-pre",
49 "all",
50 ]
51}
52
53pub fn is_valid_benchmark(name: &str) -> bool {
55 available_benchmarks().contains(&name)
56}
57
58pub fn run_benchmark(name: &str) -> Result<BenchmarkOutput, String> {
64 match name {
65 "gpu-capability" => Ok(convert_result(
66 name,
67 benchmarks::run_gpu_capability_benchmark(),
68 )),
69 "memory-bandwidth" => Ok(convert_result(
70 name,
71 benchmarks::run_memory_bandwidth_benchmark(),
72 )),
73 "tensor-core" => Ok(convert_result(
74 name,
75 benchmarks::run_tensor_core_benchmark(),
76 )),
77 "gemm" => Ok(convert_result(name, benchmarks::run_gemm_benchmark())),
78 "pytorch" => Ok(convert_result(name, benchmarks::run_pytorch_benchmark())),
79 "llama-cpp" => Ok(convert_result(name, benchmarks::run_llama_cpp_benchmark())),
80 "flash-attention" => Ok(convert_result(
81 name,
82 benchmarks::run_flash_attention_benchmark(),
83 )),
84 "flash-attention-ck" => Ok(convert_result(
85 name,
86 benchmarks::run_flash_attention_ck_benchmark(),
87 )),
88 "vllm" => Ok(convert_result(name, benchmarks::run_vllm_benchmark())),
89 "deepspeed" => Ok(convert_result(name, benchmarks::run_deepspeed_benchmark())),
90 "megatron" => Ok(convert_result(name, benchmarks::run_megatron_benchmark())),
91 "onnx" => Ok(convert_result(name, benchmarks::run_onnx_benchmark())),
92 "all-pre" => Ok(run_all_pre()),
93 "all" => Ok(run_all()),
94 _ => Err(format!("Unknown benchmark: {}", name)),
95 }
96}
97
98fn run_all_pre() -> BenchmarkOutput {
100 let mut output = BenchmarkOutput {
101 name: "all-pre".to_string(),
102 success: true,
103 execution_time_ms: 0,
104 results: serde_json::Value::Object(serde_json::Map::new()),
105 errors: Vec::new(),
106 };
107
108 let mut combined = serde_json::Value::Object(serde_json::Map::new());
109
110 collect_benchmark(
111 &mut combined,
112 "gpu_capability",
113 &mut output,
114 benchmarks::run_gpu_capability_benchmark(),
115 true,
116 );
117 collect_benchmark(
118 &mut combined,
119 "memory_bandwidth",
120 &mut output,
121 benchmarks::run_memory_bandwidth_benchmark(),
122 true,
123 );
124 collect_benchmark(
125 &mut combined,
126 "tensor_core",
127 &mut output,
128 benchmarks::run_tensor_core_benchmark(),
129 true,
130 );
131
132 output.results = combined;
133 output
134}
135
136fn run_all() -> BenchmarkOutput {
138 let mut output = BenchmarkOutput {
139 name: "all".to_string(),
140 success: true,
141 execution_time_ms: 0,
142 results: serde_json::Value::Object(serde_json::Map::new()),
143 errors: Vec::new(),
144 };
145
146 let mut combined = serde_json::Value::Object(serde_json::Map::new());
147
148 collect_benchmark(
150 &mut combined,
151 "gpu_capability",
152 &mut output,
153 benchmarks::run_gpu_capability_benchmark(),
154 true,
155 );
156 collect_benchmark(
157 &mut combined,
158 "memory_bandwidth",
159 &mut output,
160 benchmarks::run_memory_bandwidth_benchmark(),
161 true,
162 );
163 collect_benchmark(
164 &mut combined,
165 "tensor_core",
166 &mut output,
167 benchmarks::run_tensor_core_benchmark(),
168 true,
169 );
170
171 collect_benchmark(
173 &mut combined,
174 "gemm",
175 &mut output,
176 benchmarks::run_gemm_benchmark(),
177 true,
178 );
179
180 collect_benchmark(
182 &mut combined,
183 "pytorch",
184 &mut output,
185 benchmarks::run_pytorch_benchmark(),
186 false,
187 );
188 collect_benchmark(
189 &mut combined,
190 "llama_cpp",
191 &mut output,
192 benchmarks::run_llama_cpp_benchmark(),
193 false,
194 );
195 collect_benchmark(
196 &mut combined,
197 "flash_attention",
198 &mut output,
199 benchmarks::run_flash_attention_benchmark(),
200 false,
201 );
202 collect_benchmark(
203 &mut combined,
204 "flash_attention_ck",
205 &mut output,
206 benchmarks::run_flash_attention_ck_benchmark(),
207 false,
208 );
209 collect_benchmark(
210 &mut combined,
211 "vllm",
212 &mut output,
213 benchmarks::run_vllm_benchmark(),
214 false,
215 );
216 collect_benchmark(
217 &mut combined,
218 "deepspeed",
219 &mut output,
220 benchmarks::run_deepspeed_benchmark(),
221 false,
222 );
223 collect_benchmark(
224 &mut combined,
225 "megatron",
226 &mut output,
227 benchmarks::run_megatron_benchmark(),
228 false,
229 );
230 collect_benchmark(
231 &mut combined,
232 "onnx",
233 &mut output,
234 benchmarks::run_onnx_benchmark(),
235 false,
236 );
237
238 output.results = combined;
239 output
240}
241
242fn convert_result(name: &str, result: benchmarks::BenchmarkResult) -> BenchmarkOutput {
247 BenchmarkOutput {
248 name: name.to_string(),
249 success: result.success,
250 execution_time_ms: result.execution_time_ms,
251 results: serde_json::to_value(&result).unwrap_or_default(),
252 errors: result.errors.clone(),
253 }
254}
255
256fn collect_benchmark(
257 combined: &mut serde_json::Value,
258 key: &str,
259 output: &mut BenchmarkOutput,
260 result: benchmarks::BenchmarkResult,
261 required: bool,
262) {
263 if !result.success {
264 if required {
265 output.success = false;
266 }
267 if result.errors.is_empty() {
268 output.errors.push(format!(
269 "{}: benchmark failed ({})",
270 key,
271 if required { "required" } else { "optional" }
272 ));
273 }
274 }
275 output.execution_time_ms += result.execution_time_ms;
276 for err in &result.errors {
277 output.errors.push(format!(
278 "{} ({}): {}",
279 key,
280 if required { "required" } else { "optional" },
281 err
282 ));
283 }
284
285 if let serde_json::Value::Object(map) = combined {
286 map.insert(
287 key.to_string(),
288 serde_json::to_value(&result).unwrap_or_default(),
289 );
290 }
291}
292
293pub fn format_output_human(output: &BenchmarkOutput) -> String {
299 let mut text = String::new();
300 text.push_str(&format!("Benchmark: {}\n", output.name));
301 text.push_str(&format!(
302 "Status: {}\n",
303 if output.success { "SUCCESS" } else { "FAILED" }
304 ));
305 text.push_str(&format!("Time: {} ms\n", output.execution_time_ms));
306
307 if !output.errors.is_empty() {
308 text.push_str("Errors:\n");
309 for e in &output.errors {
310 text.push_str(&format!(" - {}\n", e));
311 }
312 }
313
314 if output.success {
315 if let Some(map) = output.results.as_object() {
316 text.push_str("\nResults:\n");
317 for (key, value) in map {
318 if let Some(obj) = value.as_object() {
319 let status = obj
320 .get("success")
321 .and_then(|v| v.as_bool())
322 .map(|ok| if ok { "SUCCESS" } else { "FAILED" })
323 .unwrap_or("UNKNOWN");
324 let time_ms = obj
325 .get("execution_time_ms")
326 .and_then(|v| v.as_u64())
327 .unwrap_or(0);
328 text.push_str(&format!(" {} [{} | {} ms]\n", key, status, time_ms));
329 if let Some(metrics) = obj.get("metrics").and_then(|m| m.as_object()) {
330 for (metric_key, metric_val) in metrics.iter().take(4) {
331 text.push_str(&format!(" - {}: {}\n", metric_key, metric_val));
332 }
333 if metrics.len() > 4 {
334 text.push_str(&format!(
335 " - ... {} more metrics\n",
336 metrics.len() - 4
337 ));
338 }
339 }
340 } else {
341 text.push_str(&format!(" {}: {}\n", key, value));
342 }
343 }
344 }
345 }
346
347 text
348}
349
350pub fn format_output_json(output: &BenchmarkOutput) -> String {
352 serde_json::to_string_pretty(output)
353 .unwrap_or_else(|e| format!("{{\"error\": \"failed to serialize: {}\"}}", e))
354}
355
356#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn test_available_benchmarks_contains_known_names() {
366 let names = available_benchmarks();
367 assert!(names.contains(&"gpu-capability"));
368 assert!(names.contains(&"memory-bandwidth"));
369 assert!(names.contains(&"tensor-core"));
370 assert!(names.contains(&"gemm"));
371 assert!(names.contains(&"pytorch"));
372 assert!(names.contains(&"flash-attention"));
373 assert!(names.contains(&"flash-attention-ck"));
374 assert!(names.contains(&"vllm"));
375 assert!(names.contains(&"deepspeed"));
376 assert!(names.contains(&"megatron"));
377 assert!(names.contains(&"onnx"));
378 assert!(names.contains(&"all-pre"));
379 assert!(names.contains(&"all"));
380 }
381
382 #[test]
383 fn test_is_valid_benchmark() {
384 assert!(is_valid_benchmark("gpu-capability"));
385 assert!(is_valid_benchmark("all"));
386 assert!(!is_valid_benchmark("nonexistent"));
387 assert!(!is_valid_benchmark(""));
388 }
389
390 #[test]
391 fn test_run_unknown_benchmark_returns_error() {
392 let result = run_benchmark("nonexistent-xyz");
393 assert!(result.is_err());
394 assert!(
395 result.unwrap_err().contains("Unknown benchmark"),
396 "Error should mention 'Unknown benchmark'"
397 );
398 }
399
400 #[test]
401 fn test_run_gpu_capability_dispatches() {
402 let result = run_benchmark("gpu-capability");
404 assert!(result.is_ok());
405 let output = result.unwrap();
406 assert_eq!(output.name, "gpu-capability");
407 }
408
409 #[test]
410 fn test_run_all_pre_dispatches() {
411 let result = run_benchmark("all-pre");
412 assert!(result.is_ok());
413 let output = result.unwrap();
414 assert_eq!(output.name, "all-pre");
415 }
416
417 #[test]
418 fn test_format_human_output() {
419 let output = BenchmarkOutput {
420 name: "test".to_string(),
421 success: true,
422 execution_time_ms: 100,
423 results: serde_json::Value::Object(serde_json::Map::new()),
424 errors: vec![],
425 };
426 let text = format_output_human(&output);
427 assert!(text.contains("test"));
428 assert!(text.contains("SUCCESS"));
429 }
430
431 #[test]
432 fn test_format_json_output() {
433 let output = BenchmarkOutput {
434 name: "test".to_string(),
435 success: true,
436 execution_time_ms: 100,
437 results: serde_json::Value::Object(serde_json::Map::new()),
438 errors: vec![],
439 };
440 let json = format_output_json(&output);
441 let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should be valid JSON");
442 assert_eq!(parsed["name"], "test");
443 assert_eq!(parsed["success"], true);
444 }
445
446 #[test]
447 fn test_benchmark_output_serialization_roundtrip() {
448 let output = BenchmarkOutput {
449 name: "gpu-capability".to_string(),
450 success: false,
451 execution_time_ms: 42,
452 results: serde_json::json!({"key": "value"}),
453 errors: vec!["test error".to_string()],
454 };
455 let json = serde_json::to_string(&output).unwrap();
456 let parsed: BenchmarkOutput = serde_json::from_str(&json).unwrap();
457 assert_eq!(parsed.name, "gpu-capability");
458 assert!(!parsed.success);
459 assert_eq!(parsed.execution_time_ms, 42);
460 assert_eq!(parsed.errors.len(), 1);
461 }
462}