#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use std::time::Instant;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::prover::backend::simd::SimdBackend;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::prover::backend::simd::column::BaseColumn;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::core::fields::m31::BaseField;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::core::poly::circle::CanonicCoset;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::prover::poly::circle::{CircleEvaluation, PolyOps};
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::prover::poly::BitReversedOrder;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
use stwo::prover::backend::gpu::pipeline::GpuProofPipeline;
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
fn main() {
println!("╔══════════════════════════════════════════════════════════════════════════════╗");
println!("║ TRUE GPU vs SIMD BENCHMARK - Real Code, Real Measurements ║");
println!("╚══════════════════════════════════════════════════════════════════════════════╝");
println!();
println!("This benchmark runs ACTUAL Stwo SIMD code vs ACTUAL GPU code.");
println!("No estimates - pure performance comparison.");
println!();
let configs = [
(16, 4, 5), (18, 4, 5), (18, 8, 10), (20, 4, 5), (20, 8, 10), ];
println!("┌──────────────────────────────────────────────────────────────────────────────┐");
println!("│ Benchmark: SIMD (Stwo's SimdBackend) vs GPU (Our CUDA Pipeline) │");
println!("└──────────────────────────────────────────────────────────────────────────────┘");
println!();
for (log_size, num_polys, num_rounds) in configs {
run_comparison(log_size, num_polys, num_rounds);
println!();
}
println!("┌──────────────────────────────────────────────────────────────────────────────┐");
println!("│ Summary │");
println!("└──────────────────────────────────────────────────────────────────────────────┘");
println!();
println!(" GPU Pipeline advantages:");
println!(" • Parallel FFT across thousands of CUDA cores");
println!(" • Data stays on GPU between operations (no transfer per-op)");
println!(" • Optimized kernels: vectorized loads, shared memory, etc.");
println!();
println!(" For maximum speedup:");
println!(" • Use larger polynomials (2^20+)");
println!(" • Process multiple polynomials together");
println!(" • Keep data on GPU for full proof pipeline");
}
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
fn run_comparison(log_size: u32, num_polys: usize, num_rounds: usize) {
let n = 1usize << log_size;
let total_ffts = num_polys * num_rounds * 2;
println!("Config: 2^{} × {} polys × {} rounds = {} FFTs", log_size, num_polys, num_rounds, total_ffts);
println!("─────────────────────────────────────────────────────────────────────────");
let test_data: Vec<Vec<BaseField>> = (0..num_polys)
.map(|i| {
(0..n)
.map(|j| BaseField::from_u32_unchecked(((i * n + j) % 0x7FFFFFFF) as u32))
.collect()
})
.collect();
let simd_time = run_simd_benchmark(&test_data, log_size, num_rounds);
#[cfg(feature = "cuda-runtime")]
let gpu_result = run_gpu_benchmark(&test_data, log_size, num_rounds);
#[cfg(not(feature = "cuda-runtime"))]
let gpu_result: Option<(std::time::Duration, std::time::Duration, std::time::Duration)> = None;
let simd_per_fft = simd_time.as_secs_f64() * 1000.0 / total_ffts as f64;
println!(" SIMD (SimdBackend):");
println!(" Total time: {:>10.2?}", simd_time);
println!(" Per FFT: {:>10.3} ms", simd_per_fft);
if let Some((gpu_total, gpu_compute, gpu_transfer)) = gpu_result {
let gpu_per_fft = gpu_compute.as_secs_f64() * 1000.0 / total_ffts as f64;
let speedup = simd_time.as_secs_f64() / gpu_total.as_secs_f64();
let compute_speedup = simd_time.as_secs_f64() / gpu_compute.as_secs_f64();
println!();
println!(" GPU Pipeline:");
println!(" Total time: {:>10.2?}", gpu_total);
println!(" Compute only: {:>10.2?}", gpu_compute);
println!(" Transfer: {:>10.2?}", gpu_transfer);
println!(" Per FFT: {:>10.3} ms (compute)", gpu_per_fft);
println!();
let (status, color) = if compute_speedup >= 50.0 {
("🚀 50x+ ACHIEVED!", "\x1b[32m")
} else if compute_speedup >= 30.0 {
("✅ EXCELLENT", "\x1b[32m")
} else if compute_speedup >= 20.0 {
("👍 GREAT", "\x1b[32m")
} else if compute_speedup >= 10.0 {
("👌 GOOD", "\x1b[33m")
} else {
("⚠️ MODEST", "\x1b[31m")
};
println!(" ╔═══════════════════════════════════════════════════════════════╗");
println!(" ║ {}{:<20}\x1b[0m ║", color, status);
println!(" ║ End-to-End Speedup: {:>6.1}x (SIMD / GPU total) ║", speedup);
println!(" ║ Compute Speedup: {:>6.1}x (SIMD / GPU compute) ║", compute_speedup);
println!(" ║ Transfer Overhead: {:>6.1}% ║",
gpu_transfer.as_secs_f64() / gpu_total.as_secs_f64() * 100.0);
println!(" ╚═══════════════════════════════════════════════════════════════╝");
} else {
println!();
println!(" GPU: Not available (compile with --features cuda-runtime)");
}
}
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
fn run_simd_benchmark(
test_data: &[Vec<BaseField>],
log_size: u32,
num_rounds: usize,
) -> std::time::Duration {
let domain = CanonicCoset::new(log_size).circle_domain();
let twiddles = SimdBackend::precompute_twiddles(domain.half_coset);
let mut evals: Vec<CircleEvaluation<SimdBackend, BaseField, BitReversedOrder>> = test_data
.iter()
.map(|data| {
let col: BaseColumn = data.iter().cloned().collect();
CircleEvaluation::new(domain, col)
})
.collect();
for eval in &evals {
let poly = SimdBackend::interpolate(eval.clone(), &twiddles);
let _ = SimdBackend::evaluate(&poly, domain, &twiddles);
}
let start = Instant::now();
for _round in 0..num_rounds {
for eval in &mut evals {
let poly = SimdBackend::interpolate(eval.clone(), &twiddles);
*eval = SimdBackend::evaluate(&poly, domain, &twiddles);
}
}
start.elapsed()
}
#[cfg(all(feature = "cuda-runtime", feature = "prover"))]
fn run_gpu_benchmark(
test_data: &[Vec<BaseField>],
log_size: u32,
num_rounds: usize,
) -> Option<(std::time::Duration, std::time::Duration, std::time::Duration)> {
let mut pipeline = match GpuProofPipeline::new(log_size) {
Ok(p) => p,
Err(e) => {
eprintln!("Failed to create GPU pipeline: {:?}", e);
return None;
}
};
let data_u32: Vec<Vec<u32>> = test_data
.iter()
.map(|v| v.iter().map(|f| f.0).collect())
.collect();
let upload_start = Instant::now();
for data in &data_u32 {
if pipeline.upload_polynomial(data).is_err() {
return None;
}
}
if pipeline.sync().is_err() {
return None;
}
let upload_time = upload_start.elapsed();
let compute_start = Instant::now();
for _round in 0..num_rounds {
for poly_idx in 0..test_data.len() {
if pipeline.ifft(poly_idx).is_err() {
return None;
}
if pipeline.fft(poly_idx).is_err() {
return None;
}
}
}
if pipeline.sync().is_err() {
return None;
}
let compute_time = compute_start.elapsed();
let download_start = Instant::now();
for poly_idx in 0..test_data.len() {
if pipeline.download_polynomial(poly_idx).is_err() {
return None;
}
}
let download_time = download_start.elapsed();
let total_time = upload_time + compute_time + download_time;
let transfer_time = upload_time + download_time;
Some((total_time, compute_time, transfer_time))
}
#[cfg(not(all(feature = "cuda-runtime", feature = "prover")))]
fn main() {
println!("This example requires the 'cuda-runtime' and 'prover' features.");
println!("Run with:");
println!(" cargo run --example gpu_vs_simd_real_benchmark --features cuda-runtime,prover --release");
}