use reqwest::ClientBuilder;
use std::time::{Duration, Instant};
#[tokio::main]
#[allow(unused_assignments)]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("HTTP/2 Configuration Tuning Benchmark");
println!("======================================\n");
println!("Testing various HTTP/2 settings to find optimal configuration");
println!("Each configuration runs 20 iterations\n");
let stream_windows = vec![
512 * 1024, 1024 * 1024, 2 * 1024 * 1024, 4 * 1024 * 1024, 8 * 1024 * 1024, ];
let connection_windows = vec![
1024 * 1024, 2 * 1024 * 1024, 4 * 1024 * 1024, 8 * 1024 * 1024, 16 * 1024 * 1024, ];
let max_frame_sizes = vec![
None, Some(32 * 1024), Some(64 * 1024), ];
let keep_alive_intervals = vec![
Duration::from_secs(10),
Duration::from_secs(20),
Duration::from_secs(30),
Duration::from_secs(60),
];
let mut best_config = None;
let mut best_mean = f64::MAX;
println!("Baseline: Default Client");
println!("-------------------------");
let baseline_mean = test_config(reqwest::Client::new(), "Default").await?;
best_mean = baseline_mean;
best_config = Some("Default Client".to_string());
println!("\n\nTest 2: Stream Window Sizes");
println!("============================");
for stream_window in &stream_windows {
let client = ClientBuilder::new()
.http2_adaptive_window(true)
.http2_initial_stream_window_size(*stream_window as u32)
.tcp_nodelay(true)
.pool_max_idle_per_host(10)
.pool_idle_timeout(Duration::from_secs(90))
.build()?;
let name = format!("Stream: {}KB", stream_window / 1024);
let mean = test_config(client, &name).await?;
if mean < best_mean {
best_mean = mean;
best_config = Some(name.clone());
}
}
println!("\n\nTest 3: Connection Window Sizes");
println!("================================");
let default_stream_window = 2 * 1024 * 1024;
for conn_window in &connection_windows {
let client = ClientBuilder::new()
.http2_adaptive_window(true)
.http2_initial_stream_window_size(default_stream_window)
.http2_initial_connection_window_size(*conn_window as u32)
.tcp_nodelay(true)
.pool_max_idle_per_host(10)
.pool_idle_timeout(Duration::from_secs(90))
.build()?;
let name = format!("Conn: {}MB", conn_window / (1024 * 1024));
let mean = test_config(client, &name).await?;
if mean < best_mean {
best_mean = mean;
best_config = Some(name.clone());
}
}
println!("\n\nTest 4: Max Frame Sizes");
println!("========================");
for frame_size in &max_frame_sizes {
let mut builder = ClientBuilder::new()
.http2_adaptive_window(true)
.http2_initial_stream_window_size(default_stream_window)
.http2_initial_connection_window_size(4 * 1024 * 1024)
.tcp_nodelay(true)
.pool_max_idle_per_host(10)
.pool_idle_timeout(Duration::from_secs(90));
if let Some(size) = frame_size {
builder = builder.http2_max_frame_size(Some(*size));
}
let client = builder.build()?;
let name = match frame_size {
None => "Frame: Default".to_string(),
Some(s) => format!("Frame: {}KB", s / 1024),
};
let mean = test_config(client, &name).await?;
if mean < best_mean {
best_mean = mean;
best_config = Some(name.clone());
}
}
println!("\n\nTest 5: Keep-Alive Intervals");
println!("=============================");
for interval in &keep_alive_intervals {
let client = ClientBuilder::new()
.http2_adaptive_window(true)
.http2_initial_stream_window_size(default_stream_window)
.http2_initial_connection_window_size(4 * 1024 * 1024)
.http2_keep_alive_interval(*interval)
.http2_keep_alive_timeout(Duration::from_secs(10))
.http2_keep_alive_while_idle(true)
.tcp_nodelay(true)
.pool_max_idle_per_host(10)
.pool_idle_timeout(Duration::from_secs(90))
.build()?;
let name = format!("Keep-alive: {}s", interval.as_secs());
let mean = test_config(client, &name).await?;
if mean < best_mean {
best_mean = mean;
best_config = Some(name.clone());
}
}
println!("\n\n");
println!("═══════════════════════════════════════");
println!(" FINAL RESULTS ");
println!("═══════════════════════════════════════");
println!("\nBest Configuration: {}", best_config.unwrap());
println!("Best Mean Latency: {:.1} ms", best_mean);
println!("\nBaseline (default): {:.1} ms", baseline_mean);
let improvement = ((baseline_mean - best_mean) / baseline_mean) * 100.0;
if improvement > 0.0 {
println!("Improvement: {:.1}% faster", improvement);
} else {
println!("Note: Default client is fastest!");
}
Ok(())
}
async fn test_config(
client: reqwest::Client,
name: &str,
) -> Result<f64, Box<dyn std::error::Error>> {
let iterations = 20;
let mut times = Vec::new();
print!(" Testing {}... ", name);
for _ in 0..iterations {
let start = Instant::now();
match client
.get("https://clob.polymarket.com/simplified-markets?next_cursor=MA==")
.send()
.await
{
Ok(response) => {
if response.status().is_success() {
let _ = response.bytes().await;
times.push(start.elapsed());
}
},
Err(_) => {
continue;
},
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
if times.is_empty() {
println!("FAILED (all requests failed)");
return Ok(f64::MAX);
}
let mean = times.iter().sum::<Duration>().as_millis() as f64 / times.len() as f64;
let variance = times
.iter()
.map(|t| {
let diff = t.as_millis() as f64 - mean;
diff * diff
})
.sum::<f64>()
/ times.len() as f64;
let std_dev = variance.sqrt();
println!("{:.1} ms ± {:.1} ms", mean, std_dev);
Ok(mean)
}