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