#[test]
#[cfg(feature = "cuda")]
fn test_imp_1006b_block_incremental_uses_cuda() {
use crate::cuda::CudaExecutor;
use std::time::Instant;
if !CudaExecutor::is_available() {
println!("IMP-1006b: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 2,
intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let mut cuda_model =
GpuModel::new_with_cuda(config.clone()).expect("Failed to create CUDA model");
let mut hybrid_model = GpuModel::new(config).expect("Failed to create Hybrid model");
let input: Vec<f32> = vec![0.1; 256];
let block_idx = 0;
let num_kv_heads = cuda_model.config.num_kv_heads;
let head_dim = cuda_model.config.head_dim();
let max_positions = 128;
let mut cuda_cache = StreamingKVCache::new(
cuda_model.config.num_layers,
max_positions,
num_kv_heads,
head_dim,
);
let mut hybrid_cache = StreamingKVCache::new(
hybrid_model.config.num_layers,
max_positions,
num_kv_heads,
head_dim,
);
let _ = cuda_model.forward_block_incremental_optimized(&input, block_idx, &mut cuda_cache);
let _ = hybrid_model.forward_block_incremental_optimized(&input, block_idx, &mut hybrid_cache);
let iterations = 20;
let mut cuda_cache2 = StreamingKVCache::new(
cuda_model.config.num_layers,
max_positions,
num_kv_heads,
head_dim,
);
let start = Instant::now();
for _ in 0..iterations {
let _ = cuda_model.forward_block_incremental_optimized(&input, block_idx, &mut cuda_cache2);
}
let cuda_time = start.elapsed();
let mut hybrid_cache2 = StreamingKVCache::new(
hybrid_model.config.num_layers,
max_positions,
num_kv_heads,
head_dim,
);
let start = Instant::now();
for _ in 0..iterations {
let _ =
hybrid_model.forward_block_incremental_optimized(&input, block_idx, &mut hybrid_cache2);
}
let hybrid_time = start.elapsed();
let cuda_avg_ms = cuda_time.as_secs_f64() * 1000.0 / iterations as f64;
let hybrid_avg_ms = hybrid_time.as_secs_f64() * 1000.0 / iterations as f64;
let speedup = hybrid_avg_ms / cuda_avg_ms;
println!(
"IMP-1006b: block_incremental (m=1) - CUDA={:.3}ms, Hybrid={:.3}ms, Speedup={:.2}x",
cuda_avg_ms, hybrid_avg_ms, speedup
);
assert!(
cuda_avg_ms > 0.0,
"IMP-1006b: CUDA path should complete successfully"
);
}
#[test]
#[cfg(feature = "cuda")]
fn test_imp_1006c_generate_throughput_improved() {
use crate::cuda::CudaExecutor;
use std::time::Instant;
if !CudaExecutor::is_available() {
println!("IMP-1006c: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 4, intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let mut cuda_model = GpuModel::new_with_cuda(config).expect("Failed to create CUDA model");
let prompt: Vec<usize> = vec![1, 2, 3, 4, 5];
let gen_config = GpuGenerateConfig::deterministic(10);
let start = Instant::now();
let tokens = cuda_model
.generate(&prompt, &gen_config)
.expect("Generation failed");
let elapsed = start.elapsed();
let tokens_generated = tokens.len() - prompt.len();
let tok_per_sec = tokens_generated as f64 / elapsed.as_secs_f64();
println!(
"IMP-1006c: Generated {} tokens in {:.3}ms ({:.1} tok/s)",
tokens_generated,
elapsed.as_secs_f64() * 1000.0,
tok_per_sec
);
println!(
"IMP-1006c: Previous=9.1 tok/s, Current={:.1} tok/s, Target=228 tok/s (Ollama)",
tok_per_sec
);
assert!(
tokens_generated > 0,
"IMP-1006c: Should generate at least some tokens"
);
}
#[test]
#[cfg(feature = "cuda")]
fn test_imp_1006d_all_matmuls_routed_to_cuda() {
use crate::cuda::CudaExecutor;
if !CudaExecutor::is_available() {
println!("IMP-1006d: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 2,
intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let mut cuda_model = GpuModel::new_with_cuda(config).expect("Failed to create CUDA model");
assert!(
cuda_model.has_cuda_scheduler(),
"IMP-1006d: CUDA model should have cuda_scheduler"
);
let a: Vec<f32> = vec![1.0; 256 * 128];
let b: Vec<f32> = vec![1.0; 128 * 64];
let result = cuda_model.do_matmul(&a, &b, 256, 128, 64);
assert!(
result.is_ok(),
"IMP-1006d: do_matmul should complete via CUDA"
);
let output = result.expect("test");
assert_eq!(
output.len(),
256 * 64,
"IMP-1006d: Output dimensions should be correct"
);
println!("IMP-1006d: All matmuls routed to CudaScheduler ✓");
}
#[test]
#[cfg(feature = "cuda")]
fn test_imp_1007a_no_clone_matmul() {
use crate::cuda::CudaExecutor;
if !CudaExecutor::is_available() {
println!("IMP-1007a: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 2,
intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let mut model = GpuModel::new_with_cuda(config).expect("Failed to create model");
let input: Vec<f32> = vec![0.1; 256];
let result = model.matmul_split(&input, 0, WeightType::Qkv);
assert!(result.is_ok(), "IMP-1007a: matmul_split should work");
println!("IMP-1007a: Zero-clone matmul verified ✓");
}
#[test]
#[cfg(feature = "cuda")]
fn test_imp_1007b_incremental_no_clone_speedup() {
use crate::cuda::CudaExecutor;
use std::time::Instant;
if !CudaExecutor::is_available() {
println!("IMP-1007b: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 2,
intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let mut model = GpuModel::new_with_cuda(config.clone()).expect("Failed to create model");
let num_kv_heads = model.config.num_kv_heads;
let head_dim = model.config.head_dim();
let max_positions = 128;
let input: Vec<f32> = vec![0.1; 256];
let block_idx = 0;
let mut cache = StreamingKVCache::new(
model.config.num_layers,
max_positions,
num_kv_heads,
head_dim,
);
let _ = model.forward_block_incremental_optimized(&input, block_idx, &mut cache);
let iterations = 50;
let mut cache2 = StreamingKVCache::new(
model.config.num_layers,
max_positions,
num_kv_heads,
head_dim,
);
let start = Instant::now();
for _ in 0..iterations {
let _ = model.forward_block_incremental_optimized(&input, block_idx, &mut cache2);
}
let elapsed = start.elapsed();
let avg_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
println!(
"IMP-1007b: block_incremental avg={:.3}ms ({} iterations)",
avg_ms, iterations
);
println!(
"IMP-1007b: Previous=0.698ms, Current={:.3}ms, Target=<0.5ms",
avg_ms
);
assert!(avg_ms > 0.0, "IMP-1007b: Should complete successfully");
}
#[test]
#[cfg(feature = "cuda")]
fn test_imp_1007c_generate_throughput_improved() {
use crate::cuda::CudaExecutor;
use std::time::Instant;
if !CudaExecutor::is_available() {
println!("IMP-1007c: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 4,
intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let mut model = GpuModel::new_with_cuda(config).expect("Failed to create model");
let prompt: Vec<usize> = vec![1, 2, 3, 4, 5];
let gen_config = GpuGenerateConfig::deterministic(10);
let start = Instant::now();
let tokens = model
.generate(&prompt, &gen_config)
.expect("Generation failed");
let elapsed = start.elapsed();
let tokens_generated = tokens.len() - prompt.len();
let tok_per_sec = tokens_generated as f64 / elapsed.as_secs_f64();
println!(
"IMP-1007c: Generated {} tokens in {:.3}ms ({:.1} tok/s)",
tokens_generated,
elapsed.as_secs_f64() * 1000.0,
tok_per_sec
);
println!(
"IMP-1007c: Previous=37.3 tok/s, Current={:.1} tok/s, Target=228 tok/s (Ollama)",
tok_per_sec
);
assert!(
tokens_generated > 0,
"IMP-1007c: Should generate at least some tokens"
);
}
#[test]
#[cfg(feature = "cuda")]
fn test_imp_1008a_refcell_scheduler_matmul() {
use crate::cuda::CudaExecutor;
if !CudaExecutor::is_available() {
println!("IMP-1008a: CUDA not available, skipping");
return;
}
let config = GpuModelConfig {
vocab_size: 100,
hidden_dim: 256,
num_heads: 4,
num_kv_heads: 4,
num_layers: 2,
intermediate_dim: 512,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let model = GpuModel::new_with_cuda(config).expect("Failed to create CUDA model");
let a: Vec<f32> = vec![0.1; 256];
let b: Vec<f32> = vec![0.2; 256 * 512];
let result = model
.matmul_refcell(&a, &b, 1, 256, 512)
.expect("matmul_refcell should work");
assert_eq!(
result.len(),
512,
"IMP-1008a: Output should be 512 elements"
);
let sum: f32 = result.iter().sum();
assert!(sum.is_finite(), "IMP-1008a: Result should be finite");
assert!(sum != 0.0, "IMP-1008a: Result should be non-zero");
println!("IMP-1008a: matmul_refcell works with &self");
}