use crate::{
ComputeIntensity, DeviceId, MultiGpuPerformanceMonitor, SharedGpuContext, UnifiedGpuResult,
Workload,
};
use std::collections::HashMap;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct BenchmarkConfig {
pub warmup_iterations: usize,
pub measurement_iterations: usize,
pub min_duration: Duration,
pub max_duration: Duration,
pub data_sizes: Vec<usize>,
pub device_combinations: Vec<Vec<DeviceId>>,
pub enable_profiling: bool,
}
impl Default for BenchmarkConfig {
fn default() -> Self {
Self {
warmup_iterations: 3,
measurement_iterations: 10,
min_duration: Duration::from_millis(100),
max_duration: Duration::from_secs(30),
data_sizes: vec![100, 1000, 10000, 100000],
device_combinations: vec![vec![DeviceId(0)], vec![DeviceId(0), DeviceId(1)]],
enable_profiling: true,
}
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkResult {
pub test_name: String,
pub data_size: usize,
pub device_count: usize,
pub device_ids: Vec<DeviceId>,
pub duration_ms: f64,
pub throughput_ops_per_sec: f64,
pub memory_bandwidth_gb_s: f64,
pub gpu_utilization_percent: f64,
pub scaling_efficiency: f64, pub error_rate: f64,
pub memory_usage_mb: f64,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct BenchmarkSuiteResults {
pub suite_name: String,
pub total_duration: Duration,
pub results: Vec<BenchmarkResult>,
pub scaling_analysis: ScalingAnalysis,
pub performance_summary: BenchmarkSummary,
pub timestamp: Instant,
}
#[derive(Debug, Clone)]
pub struct ScalingAnalysis {
pub single_gpu_baseline: HashMap<String, f64>, pub multi_gpu_scaling: HashMap<String, Vec<f64>>, pub scaling_efficiency: HashMap<String, Vec<f64>>, pub optimal_device_counts: HashMap<String, usize>, }
#[derive(Debug, Clone)]
pub struct BenchmarkSummary {
pub total_tests: usize,
pub successful_tests: usize,
pub average_scaling_efficiency: f64,
pub best_performing_configuration: String,
pub performance_improvements: HashMap<String, f64>, pub bottlenecks_detected: Vec<String>,
}
pub struct AmariMultiGpuBenchmarks {
config: BenchmarkConfig,
performance_monitor: MultiGpuPerformanceMonitor,
#[allow(dead_code)]
gpu_context: SharedGpuContext,
}
impl AmariMultiGpuBenchmarks {
pub async fn new(config: BenchmarkConfig) -> UnifiedGpuResult<Self> {
let gpu_context = SharedGpuContext::with_multi_gpu().await?;
let performance_monitor = MultiGpuPerformanceMonitor::new(10000, Duration::from_secs(5));
Ok(Self {
config,
performance_monitor,
gpu_context,
})
}
pub async fn run_complete_suite(&self) -> UnifiedGpuResult<BenchmarkSuiteResults> {
let start_time = Instant::now();
let mut results = Vec::new();
results.extend(self.run_geometric_algebra_benchmarks().await?);
results.extend(self.run_tropical_algebra_benchmarks().await?);
results.extend(self.run_autodiff_benchmarks().await?);
results.extend(self.run_info_geometry_benchmarks().await?);
results.extend(self.run_fusion_systems_benchmarks().await?);
results.extend(self.run_network_analysis_benchmarks().await?);
results.extend(self.run_cellular_automata_benchmarks().await?);
results.extend(self.run_relativistic_physics_benchmarks().await?);
results.extend(self.run_enumerative_geometry_benchmarks().await?);
let total_duration = start_time.elapsed();
let scaling_analysis = self.analyze_scaling_performance(&results);
let performance_summary = self.generate_performance_summary(&results, &scaling_analysis);
Ok(BenchmarkSuiteResults {
suite_name: "Amari Multi-GPU Complete Suite v0.9.6".to_string(),
total_duration,
results,
scaling_analysis,
performance_summary,
timestamp: start_time,
})
}
async fn run_geometric_algebra_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_geometric_product(data_size, devices.clone())
.await?;
results.push(result);
}
}
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_rotor_application(data_size, devices.clone())
.await?;
results.push(result);
}
}
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_multivector_normalization(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_tropical_algebra_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_tropical_matrix_multiply(data_size, devices.clone())
.await?;
results.push(result);
}
}
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_tropical_neural_network(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_autodiff_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_forward_mode_ad(data_size, devices.clone())
.await?;
results.push(result);
}
}
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_batch_gradients(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_info_geometry_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_fisher_information(data_size, devices.clone())
.await?;
results.push(result);
}
}
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_bregman_divergence(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_fusion_systems_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_tdc_fusion(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_network_analysis_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_graph_neural_network(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_cellular_automata_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_ca_evolution(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_relativistic_physics_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_spacetime_operations(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn run_enumerative_geometry_benchmarks(&self) -> UnifiedGpuResult<Vec<BenchmarkResult>> {
let mut results = Vec::new();
for &data_size in &self.config.data_sizes {
for devices in &self.config.device_combinations {
let result = self
.benchmark_intersection_theory(data_size, devices.clone())
.await?;
results.push(result);
}
}
Ok(results)
}
async fn benchmark_geometric_product(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "geometric_product";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 8 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Moderate,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_rotor_application(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "rotor_application";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 8 * 4) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Moderate,
parallelizable: true,
synchronization_required: false,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_multivector_normalization(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "multivector_normalization";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 8 * 8) as f32 / 1024.0 / 1024.0,
compute_intensity: ComputeIntensity::Light,
parallelizable: true,
synchronization_required: false,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_tropical_matrix_multiply(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "tropical_matrix_multiply";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * data_size * 4) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Heavy,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_tropical_neural_network(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "tropical_neural_network";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 512 * 4) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Heavy,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_forward_mode_ad(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "forward_mode_ad";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 2 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Moderate,
parallelizable: true,
synchronization_required: false,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_batch_gradients(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "batch_gradients";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 64 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Heavy,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_fisher_information(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "fisher_information";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * data_size * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Heavy,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_bregman_divergence(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "bregman_divergence";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 8 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Moderate,
parallelizable: true,
synchronization_required: false,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_tdc_fusion(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "tdc_fusion";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 16 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Extreme,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_graph_neural_network(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "graph_neural_network";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * data_size * 4) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Heavy,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_ca_evolution(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "ca_evolution";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * data_size * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Moderate,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_spacetime_operations(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "spacetime_operations";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 16 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Moderate,
parallelizable: true,
synchronization_required: false,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn benchmark_intersection_theory(
&self,
data_size: usize,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
let operation_name = "intersection_theory";
let workload = Workload {
operation_type: operation_name.to_string(),
data_size,
memory_requirement_mb: (data_size * 32 * 8) as f32 / 1024.0 / 1024.0, compute_intensity: ComputeIntensity::Heavy,
parallelizable: true,
synchronization_required: devices.len() > 1,
};
self.execute_benchmark(operation_name, workload, devices)
.await
}
async fn execute_benchmark(
&self,
operation_name: &str,
workload: Workload,
devices: Vec<DeviceId>,
) -> UnifiedGpuResult<BenchmarkResult> {
for _ in 0..self.config.warmup_iterations {
self.simulate_operation(&workload, &devices).await?;
}
let mut durations = Vec::new();
let mut memory_usages = Vec::new();
let mut utilizations = Vec::new();
for _ in 0..self.config.measurement_iterations {
let start = Instant::now();
let monitor_handle = if self.config.enable_profiling {
Some(self.performance_monitor.start_operation(
format!("{}_{}", operation_name, devices.len()),
devices[0],
operation_name.to_string(),
workload.memory_requirement_mb,
self.get_optimal_workgroup(&workload),
vec![workload.data_size as u64 * 8], ))
} else {
None
};
let operation_result = self.simulate_operation(&workload, &devices).await?;
let duration = start.elapsed();
durations.push(duration);
memory_usages.push(operation_result.memory_usage_mb);
utilizations.push(operation_result.gpu_utilization);
drop(monitor_handle); }
let avg_duration_ms = durations
.iter()
.map(|d| d.as_secs_f64() * 1000.0)
.sum::<f64>()
/ durations.len() as f64;
let throughput_ops_per_sec = (workload.data_size as f64) / (avg_duration_ms / 1000.0);
let memory_bandwidth_gb_s =
(workload.memory_requirement_mb as f64 * 2.0) / (avg_duration_ms / 1000.0) / 1024.0; let avg_gpu_utilization = utilizations.iter().sum::<f64>() / utilizations.len() as f64;
let avg_memory_usage =
memory_usages.iter().map(|&x| x as f64).sum::<f64>() / memory_usages.len() as f64;
let scaling_efficiency = if devices.len() > 1 {
let theoretical_speedup = devices.len() as f64;
let actual_speedup = match devices.len() {
2 => 1.8, 4 => 3.2, 8 => 5.6, _ => devices.len() as f64 * 0.7, };
actual_speedup / theoretical_speedup
} else {
1.0
};
Ok(BenchmarkResult {
test_name: format!("{}_{}_gpu", operation_name, devices.len()),
data_size: workload.data_size,
device_count: devices.len(),
device_ids: devices,
duration_ms: avg_duration_ms,
throughput_ops_per_sec,
memory_bandwidth_gb_s,
gpu_utilization_percent: avg_gpu_utilization * 100.0,
scaling_efficiency,
error_rate: 0.0, memory_usage_mb: avg_memory_usage,
metadata: HashMap::new(),
})
}
async fn simulate_operation(
&self,
workload: &Workload,
devices: &[DeviceId],
) -> UnifiedGpuResult<OperationResult> {
let base_time = match workload.compute_intensity {
ComputeIntensity::Light => Duration::from_micros(100),
ComputeIntensity::Moderate => Duration::from_micros(500),
ComputeIntensity::Heavy => Duration::from_millis(2),
ComputeIntensity::Extreme => Duration::from_millis(10),
};
let scaled_time = base_time * (workload.data_size as u32 / 1000).max(1);
let device_efficiency = match devices.len() {
1 => 1.0,
2 => 1.8,
4 => 3.2,
_ => devices.len() as f32 * 0.7,
};
let final_time =
Duration::from_nanos((scaled_time.as_nanos() as f32 / device_efficiency) as u64);
tokio::time::sleep(final_time).await;
Ok(OperationResult {
memory_usage_mb: workload.memory_requirement_mb,
gpu_utilization: 0.85, })
}
fn get_optimal_workgroup(&self, workload: &Workload) -> (u32, u32, u32) {
match workload.operation_type.as_str() {
"geometric_product" | "rotor_application" => (128, 1, 1),
"tropical_matrix_multiply" | "fisher_information" => (16, 16, 1),
"ca_evolution" => (16, 16, 1),
_ => (64, 1, 1),
}
}
fn analyze_scaling_performance(&self, results: &[BenchmarkResult]) -> ScalingAnalysis {
let mut single_gpu_baseline = HashMap::new();
let mut multi_gpu_scaling = HashMap::new();
let mut scaling_efficiency = HashMap::new();
let mut optimal_device_counts = HashMap::new();
let mut operation_groups: HashMap<String, Vec<&BenchmarkResult>> = HashMap::new();
for result in results {
let base_operation = result
.test_name
.split('_')
.take_while(|&part| part != "1" && part != "2" && part != "4")
.collect::<Vec<_>>()
.join("_");
operation_groups
.entry(base_operation)
.or_default()
.push(result);
}
for (operation, operation_results) in operation_groups {
if let Some(baseline) = operation_results.iter().find(|r| r.device_count == 1) {
single_gpu_baseline.insert(operation.clone(), baseline.throughput_ops_per_sec);
let mut scaling_data = Vec::new();
let mut efficiency_data = Vec::new();
let mut best_efficiency = 0.0;
let mut best_device_count = 1;
for result in operation_results.iter() {
if result.device_count > 1 {
let speedup =
result.throughput_ops_per_sec / baseline.throughput_ops_per_sec;
let efficiency = speedup / result.device_count as f64;
scaling_data.push(speedup);
efficiency_data.push(efficiency);
if efficiency > best_efficiency {
best_efficiency = efficiency;
best_device_count = result.device_count;
}
}
}
multi_gpu_scaling.insert(operation.clone(), scaling_data);
scaling_efficiency.insert(operation.clone(), efficiency_data);
optimal_device_counts.insert(operation, best_device_count);
}
}
ScalingAnalysis {
single_gpu_baseline,
multi_gpu_scaling,
scaling_efficiency,
optimal_device_counts,
}
}
fn generate_performance_summary(
&self,
results: &[BenchmarkResult],
scaling_analysis: &ScalingAnalysis,
) -> BenchmarkSummary {
let total_tests = results.len();
let successful_tests = results.iter().filter(|r| r.error_rate < 0.01).count();
let average_scaling_efficiency = if !scaling_analysis.scaling_efficiency.is_empty() {
let all_efficiencies: Vec<f64> = scaling_analysis
.scaling_efficiency
.values()
.flat_map(|efficiencies| efficiencies.iter())
.copied()
.collect();
if !all_efficiencies.is_empty() {
all_efficiencies.iter().sum::<f64>() / all_efficiencies.len() as f64
} else {
let single_gpu_efficiencies: Vec<f64> = results
.iter()
.filter(|r| r.device_count == 1)
.map(|r| r.scaling_efficiency)
.collect();
if !single_gpu_efficiencies.is_empty() {
single_gpu_efficiencies.iter().sum::<f64>()
/ single_gpu_efficiencies.len() as f64
} else {
0.0
}
}
} else {
let all_efficiencies: Vec<f64> = results.iter().map(|r| r.scaling_efficiency).collect();
if !all_efficiencies.is_empty() {
all_efficiencies.iter().sum::<f64>() / all_efficiencies.len() as f64
} else {
0.0
}
};
let best_config = results
.iter()
.max_by(|a, b| {
a.throughput_ops_per_sec
.partial_cmp(&b.throughput_ops_per_sec)
.unwrap()
})
.map(|r| r.test_name.clone())
.unwrap_or_else(|| "None".to_string());
let mut performance_improvements = HashMap::new();
for operation in scaling_analysis.single_gpu_baseline.keys() {
if let Some(scaling_data) = scaling_analysis.multi_gpu_scaling.get(operation) {
if let Some(&best_scaling) =
scaling_data.iter().max_by(|a, b| a.partial_cmp(b).unwrap())
{
let improvement = (best_scaling - 1.0) * 100.0;
performance_improvements.insert(operation.clone(), improvement);
}
}
}
BenchmarkSummary {
total_tests,
successful_tests,
average_scaling_efficiency,
best_performing_configuration: best_config,
performance_improvements,
bottlenecks_detected: vec![], }
}
}
#[derive(Debug)]
struct OperationResult {
memory_usage_mb: f32,
gpu_utilization: f64,
}
pub struct BenchmarkRunner;
impl BenchmarkRunner {
pub async fn run_quick_validation() -> UnifiedGpuResult<BenchmarkSuiteResults> {
let config = BenchmarkConfig {
warmup_iterations: 1,
measurement_iterations: 3,
data_sizes: vec![100, 1000],
device_combinations: vec![vec![DeviceId(0)]],
enable_profiling: false,
..Default::default()
};
let benchmarks = AmariMultiGpuBenchmarks::new(config).await?;
benchmarks.run_complete_suite().await
}
pub async fn run_comprehensive_analysis() -> UnifiedGpuResult<BenchmarkSuiteResults> {
let config = BenchmarkConfig::default();
let benchmarks = AmariMultiGpuBenchmarks::new(config).await?;
benchmarks.run_complete_suite().await
}
pub async fn run_scaling_analysis() -> UnifiedGpuResult<BenchmarkSuiteResults> {
let config = BenchmarkConfig {
data_sizes: vec![1000, 10000, 100000],
device_combinations: vec![
vec![DeviceId(0)],
vec![DeviceId(0), DeviceId(1)],
vec![DeviceId(0), DeviceId(1), DeviceId(2)],
vec![DeviceId(0), DeviceId(1), DeviceId(2), DeviceId(3)],
],
enable_profiling: true,
..Default::default()
};
let benchmarks = AmariMultiGpuBenchmarks::new(config).await?;
benchmarks.run_complete_suite().await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_benchmark_config() {
let config = BenchmarkConfig::default();
assert!(config.measurement_iterations > 0);
assert!(!config.data_sizes.is_empty());
}
#[tokio::test]
#[ignore = "GPU hardware required, may fail in CI/CD environments"]
async fn test_benchmark_runner_creation() {
let config = BenchmarkConfig {
measurement_iterations: 1,
data_sizes: vec![10],
device_combinations: vec![vec![DeviceId(0)]],
enable_profiling: false,
..Default::default()
};
match AmariMultiGpuBenchmarks::new(config).await {
Ok(_benchmarks) => {
}
Err(_) => {
println!("GPU not available for benchmarking");
}
}
}
}