feat: integrate infrastructure modules and achieve 8.9% performance improvement

Integrated DNS caching, connection manager, and buffer pool modules into ClobClient structure to enable production use. Added start_keepalive() and stop_keepalive() methods for maintaining warm connections through background keep-alive pings. Implemented DNS cache pre-warming on client initialization and buffer pool with 512KB buffers for reducing allocation overhead. Cleaned up temporary test and analysis files from optimization exploration. Benchmark results with keep-alive enabled show 368.6ms mean latency compared to polymarket-rs-client's 404.5ms, representing 8.9% improvement and 35.9ms faster performance while maintaining production-safe approaches
This commit is contained in:
floor-licker
2025-12-06 17:47:35 -05:00
parent af9e1a1939
commit dc6092c244
13 changed files with 206 additions and 1392 deletions
-232
View File
@@ -1,232 +0,0 @@
use polyfill_rs::ClobClient;
use std::time::Instant;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🚀 Advanced Network Optimizations - polyfill-rs");
println!("===============================================");
// Use the best-performing configuration (Internet)
let client = ClobClient::new_internet("https://clob.polymarket.com");
println!("📊 Test 1: Connection Pre-warming");
println!("=================================");
// Test without pre-warming
let start = Instant::now();
let _ = client.get_server_time().await;
let cold_start = start.elapsed();
println!(" ❄️ Cold start: {:?}", cold_start);
// Test with pre-warming
let client_warm = ClobClient::new_internet("https://clob.polymarket.com");
let _ = client_warm.prewarm_connections().await;
let start = Instant::now();
let _ = client_warm.get_server_time().await;
let warm_start = start.elapsed();
println!(" 🔥 Warm start: {:?}", warm_start);
println!(
" 📈 Improvement: {:.1}x faster",
cold_start.as_millis() as f64 / warm_start.as_millis() as f64
);
println!("\n📊 Test 2: Request Batching Simulation");
println!("=====================================");
// Sequential requests
let start = Instant::now();
for _ in 0..5 {
let _ = client.get_server_time().await;
}
let sequential_time = start.elapsed();
println!(" 📝 Sequential: 5 requests in {:?}", sequential_time);
// Parallel requests (simulating batching)
let start = Instant::now();
let futures = (0..5).map(|_| client.get_server_time());
let _results: Vec<_> = futures_util::future::join_all(futures).await;
let parallel_time = start.elapsed();
println!(" ⚡ Parallel: 5 requests in {:?}", parallel_time);
println!(
" 📈 Improvement: {:.1}x faster",
sequential_time.as_millis() as f64 / parallel_time.as_millis() as f64
);
println!("\n📊 Test 3: Circuit Breaker Pattern");
println!("=================================");
struct SimpleCircuitBreaker {
failure_count: u32,
failure_threshold: u32,
recovery_timeout: Duration,
last_failure: Option<Instant>,
state: CircuitState,
}
#[derive(Debug, PartialEq)]
enum CircuitState {
Closed, // Normal operation
Open, // Failing, reject requests
HalfOpen, // Testing if service recovered
}
impl SimpleCircuitBreaker {
fn new() -> Self {
Self {
failure_count: 0,
failure_threshold: 3,
recovery_timeout: Duration::from_secs(10),
last_failure: None,
state: CircuitState::Closed,
}
}
fn can_execute(&mut self) -> bool {
match self.state {
CircuitState::Closed => true,
CircuitState::Open => {
if let Some(last_failure) = self.last_failure {
if last_failure.elapsed() > self.recovery_timeout {
self.state = CircuitState::HalfOpen;
true
} else {
false
}
} else {
false
}
},
CircuitState::HalfOpen => true,
}
}
fn on_success(&mut self) {
self.failure_count = 0;
self.state = CircuitState::Closed;
}
fn on_failure(&mut self) {
self.failure_count += 1;
self.last_failure = Some(Instant::now());
if self.failure_count >= self.failure_threshold {
self.state = CircuitState::Open;
}
}
}
let mut circuit_breaker = SimpleCircuitBreaker::new();
let mut successful_requests = 0;
let mut rejected_requests = 0;
// Simulate some requests with circuit breaker
for i in 0..10 {
if circuit_breaker.can_execute() {
match client.get_server_time().await {
Ok(_) => {
circuit_breaker.on_success();
successful_requests += 1;
if i < 3 {
println!(" ✅ Request {} succeeded", i + 1);
}
},
Err(_) => {
circuit_breaker.on_failure();
if i < 3 {
println!(" ❌ Request {} failed", i + 1);
}
},
}
} else {
rejected_requests += 1;
if i < 3 {
println!(" 🚫 Request {} rejected by circuit breaker", i + 1);
}
}
// Small delay between requests
sleep(Duration::from_millis(100)).await;
}
println!(
" 📊 Results: {} successful, {} rejected",
successful_requests, rejected_requests
);
println!("\n📊 Test 4: Adaptive Timeout Strategy");
println!("===================================");
struct AdaptiveTimeout {
recent_times: Vec<Duration>,
max_samples: usize,
}
impl AdaptiveTimeout {
fn new() -> Self {
Self {
recent_times: Vec::new(),
max_samples: 10,
}
}
fn add_sample(&mut self, duration: Duration) {
self.recent_times.push(duration);
if self.recent_times.len() > self.max_samples {
self.recent_times.remove(0);
}
}
fn get_adaptive_timeout(&self) -> Duration {
if self.recent_times.is_empty() {
return Duration::from_millis(5000); // Default
}
let avg = self.recent_times.iter().sum::<Duration>() / self.recent_times.len() as u32;
// Set timeout to 3x average response time
avg * 3
}
}
let mut adaptive_timeout = AdaptiveTimeout::new();
// Collect some samples
for i in 0..5 {
let start = Instant::now();
if (client.get_server_time().await).is_ok() {
let duration = start.elapsed();
adaptive_timeout.add_sample(duration);
if i < 3 {
println!(" 📊 Sample {}: {:?}", i + 1, duration);
}
}
}
let recommended_timeout = adaptive_timeout.get_adaptive_timeout();
println!(" 🎯 Recommended timeout: {:?}", recommended_timeout);
println!("\n🎯 Advanced Optimization Summary");
println!("===============================");
println!("Implemented Optimizations:");
println!(" ✅ Connection pre-warming (reduces cold start latency)");
println!(" ✅ Request parallelization (batching simulation)");
println!(" ✅ Circuit breaker pattern (prevents cascade failures)");
println!(" ✅ Adaptive timeouts (dynamic based on network conditions)");
println!("\nFurther Optimizations Available:");
println!(" 🔧 Custom DNS resolver with caching");
println!(" 🔧 Connection affinity (sticky connections)");
println!(" 🔧 Request prioritization queues");
println!(" 🔧 Geographical load balancing");
println!(" 🔧 WebSocket connections for real-time data");
println!(" 🔧 HTTP/3 (QUIC) when supported");
println!("\n📈 Expected Network Improvements:");
println!(" • 10-30% latency reduction from optimized HTTP client");
println!(" • 50-80% improvement in connection reuse scenarios");
println!(" • Better resilience during network instability");
println!(" • Adaptive performance based on network conditions");
Ok(())
}
+81
View File
@@ -0,0 +1,81 @@
use polyfill_rs::ClobClient;
use std::time::{Duration, Instant};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Benchmark with Keep-Alive Enabled");
println!("==================================\n");
let mut client = ClobClient::new("https://clob.polymarket.com");
// Start keep-alive
println!("Starting keep-alive...");
client.start_keepalive(Duration::from_secs(30)).await;
// Give it a moment to establish
tokio::time::sleep(Duration::from_millis(500)).await;
println!("Keep-alive started\n");
println!("Testing: /simplified-markets endpoint");
println!("Iterations: 20");
println!("Delay: 100ms between requests\n");
let mut times = Vec::new();
for i in 1..=20 {
let start = Instant::now();
let response = client.http_client
.get(format!("{}/simplified-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let _json: serde_json::Value = response.json().await?;
let elapsed = start.elapsed();
times.push(elapsed);
if i <= 5 || i > 15 {
println!(" Request {:2}: {:.1} ms", i, elapsed.as_micros() as f64 / 1000.0);
} else if i == 6 {
println!(" ...");
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
// Stop keep-alive
client.stop_keepalive().await;
// Calculate statistics
let values: Vec<f64> = times.iter().map(|d| d.as_micros() as f64 / 1000.0).collect();
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values.iter()
.map(|v| (v - mean).powi(2))
.sum::<f64>() / values.len() as f64;
let std_dev = variance.sqrt();
let mut sorted = values.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
let min = sorted[0];
let max = sorted[sorted.len() - 1];
let median = sorted[sorted.len() / 2];
println!("\n\n📊 RESULTS WITH KEEP-ALIVE");
println!("===========================\n");
println!("Mean: {:.1} ms ± {:.1} ms", mean, std_dev);
println!("Median: {:.1} ms", median);
println!("Range: {:.1} - {:.1} ms", min, max);
println!("\nvs polymarket-rs-client: 404.5 ms ± 22.9 ms");
println!("vs previous (no keep-alive): 382.6 ms ± 75.1 ms");
let diff = mean - 404.5;
if diff < 0.0 {
println!("\n{:.1}% FASTER than polymarket-rs-client", -diff / 404.5 * 100.0);
} else {
println!("\n⚠️ {:.1}% slower than polymarket-rs-client", diff / 404.5 * 100.0);
}
Ok(())
}
-85
View File
@@ -1,85 +0,0 @@
use polyfill_rs::ClobClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
println!("📦 Payload Size Comparison");
println!("==========================\n");
let api_key = std::env::var("POLYMARKET_API_KEY")?;
let secret = std::env::var("POLYMARKET_SECRET")?;
let passphrase = std::env::var("POLYMARKET_PASSPHRASE")?;
let api_creds = polyfill_rs::ApiCredentials {
api_key,
secret,
passphrase,
};
let mut client = ClobClient::new("https://clob.polymarket.com");
client.set_api_creds(api_creds);
println!("Testing different endpoints and parameters...\n");
// Test 1: sampling-markets (what we're currently using)
let response1 = client.http_client
.get(format!("{}/sampling-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let body1 = response1.bytes().await?;
let json1: serde_json::Value = serde_json::from_slice(&body1)?;
let count1 = json1["data"].as_array().map(|a| a.len()).unwrap_or(0);
println!("1. /sampling-markets (default):");
println!(" Response: {} bytes", body1.len());
println!(" Markets: {}", count1);
println!();
// Test 2: markets endpoint
let response2 = client.http_client
.get(format!("{}/markets", client.base_url))
.send()
.await?;
let body2 = response2.bytes().await?;
let json2: serde_json::Value = serde_json::from_slice(&body2)?;
let count2 = json2["data"].as_array().map(|a| a.len()).unwrap_or(0);
println!("2. /markets (no cursor):");
println!(" Response: {} bytes", body2.len());
println!(" Markets: {}", count2);
println!();
// Test 3: simplified-markets
let response3 = client.http_client
.get(format!("{}/simplified-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let body3 = response3.bytes().await?;
let json3: serde_json::Value = serde_json::from_slice(&body3)?;
let count3 = json3["data"].as_array().map(|a| a.len()).unwrap_or(0);
println!("3. /simplified-markets:");
println!(" Response: {} bytes", body3.len());
println!(" Markets: {}", count3);
println!();
println!("💡 Analysis:");
println!("============");
println!("The polymarket-rs-client might be:");
println!("1. Using a different endpoint with less data");
println!("2. Requesting fewer markets (pagination)");
println!("3. Using HTTP/2 multiplexing for better performance");
println!("4. Making fewer redundant requests");
println!();
println!("📌 Recommendation:");
println!("For typical use cases, consider:");
println!("- Using /simplified-markets for listings (smaller payload)");
println!("- Adding limit parameters to reduce payload");
println!("- Caching market data locally");
Ok(())
}
-284
View File
@@ -1,284 +0,0 @@
use polyfill_rs::{ClobClient, OrderBookImpl};
use rust_decimal::Decimal;
use std::str::FromStr;
use std::time::Instant;
// Simple memory tracker using system allocator
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
struct TrackingAllocator;
static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
static ALLOCATIONS: AtomicUsize = AtomicUsize::new(0);
static DEALLOCATIONS: AtomicUsize = AtomicUsize::new(0);
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ret = System.alloc(layout);
if !ret.is_null() {
ALLOCATED.fetch_add(layout.size(), Ordering::SeqCst);
ALLOCATIONS.fetch_add(1, Ordering::SeqCst);
}
ret
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout);
ALLOCATED.fetch_sub(layout.size(), Ordering::SeqCst);
DEALLOCATIONS.fetch_add(1, Ordering::SeqCst);
}
}
#[global_allocator]
static GLOBAL: TrackingAllocator = TrackingAllocator;
fn reset_counters() {
ALLOCATED.store(0, Ordering::SeqCst);
ALLOCATIONS.store(0, Ordering::SeqCst);
DEALLOCATIONS.store(0, Ordering::SeqCst);
}
fn get_memory_stats() -> (usize, usize, usize) {
(
ALLOCATED.load(Ordering::SeqCst),
ALLOCATIONS.load(Ordering::SeqCst),
DEALLOCATIONS.load(Ordering::SeqCst),
)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🧠 Memory Usage Benchmark - Real Measurements");
println!("==============================================");
println!("Comparing with polymarket-rs-client baseline:");
println!(" 88,053 allocs, 81,823 frees, 15,945,966 bytes allocated");
println!();
// Load environment variables
dotenv::dotenv().ok();
let client = ClobClient::new_internet("https://clob.polymarket.com");
// Test 1: Market Data Fetching Memory Usage
println!("📊 Test 1: Market Data Fetching Memory");
println!("=====================================");
// Reset and measure market data fetching
reset_counters();
let start_stats = get_memory_stats();
let start_time = Instant::now();
let result = client.get_sampling_simplified_markets(None).await;
let duration = start_time.elapsed();
let end_stats = get_memory_stats();
match result {
Ok(markets) => {
println!(
"✅ Fetched {} markets in {:?}",
markets.data.len(),
duration
);
let (bytes_allocated, allocs, deallocs) = (
end_stats.0 - start_stats.0,
end_stats.1 - start_stats.1,
end_stats.2 - start_stats.2,
);
println!("📈 polyfill-rs memory usage:");
println!(
" {} allocs, {} frees, {} bytes allocated",
allocs, deallocs, bytes_allocated
);
println!(
"📊 vs baseline (15,945,966 bytes): {:.1}x less memory",
15_945_966.0 / bytes_allocated as f64
);
println!(
"📊 vs baseline ({} allocs): {:.1}x fewer allocations",
88_053,
88_053.0 / allocs as f64
);
},
Err(e) => {
println!("❌ Error: {}", e);
println!("⚠️ Still measuring memory usage of error handling...");
let (bytes_allocated, allocs, deallocs) = (
end_stats.0 - start_stats.0,
end_stats.1 - start_stats.1,
end_stats.2 - start_stats.2,
);
println!("📈 Memory usage (even with error):");
println!(
" {} allocs, {} frees, {} bytes allocated",
allocs, deallocs, bytes_allocated
);
},
}
// Test 2: Order Book Memory Efficiency
println!("\n📊 Test 2: Order Book Memory Efficiency");
println!("======================================");
reset_counters();
let start_stats = get_memory_stats();
// Create order book and populate it
let mut book = OrderBookImpl::new("test_token".to_string(), 100);
// Add many orders to test memory efficiency
for i in 0..1000 {
let price = Decimal::from_str(&format!("0.{:04}", 5000 + (i % 100))).unwrap();
let size = Decimal::from_str("100.0").unwrap();
let delta = polyfill_rs::OrderDelta {
token_id: "test_token".to_string(),
timestamp: chrono::Utc::now(),
side: if i % 2 == 0 {
polyfill_rs::Side::BUY
} else {
polyfill_rs::Side::SELL
},
price,
size,
sequence: i as u64,
};
let _ = book.apply_delta(delta);
}
let end_stats = get_memory_stats();
let (bytes_allocated, allocs, deallocs) = (
end_stats.0 - start_stats.0,
end_stats.1 - start_stats.1,
end_stats.2 - start_stats.2,
);
println!("📈 Order book (1000 updates):");
println!(
" {} allocs, {} frees, {} bytes allocated",
allocs, deallocs, bytes_allocated
);
println!(
"📊 Per update: {:.1} bytes/update",
bytes_allocated as f64 / 1000.0
);
// Test 3: JSON Parsing Memory
println!("\n📊 Test 3: JSON Parsing Memory Usage");
println!("===================================");
let sample_json = r#"{
"data": [
{
"condition_id": "test123",
"question": "Test market?",
"description": "Test description",
"end_date_iso": "2024-01-01T00:00:00Z",
"game_start_time": "2024-01-01T00:00:00Z",
"active": true,
"closed": false,
"archived": false,
"accepting_orders": true,
"minimum_order_size": "1.0",
"minimum_tick_size": "0.01",
"market_slug": "test-market",
"seconds_delay": 0,
"tokens": []
}
]
}"#;
reset_counters();
let start_stats = get_memory_stats();
// Parse JSON 1000 times to measure memory usage
for _ in 0..1000 {
let _: Result<serde_json::Value, _> = serde_json::from_str(sample_json);
}
let end_stats = get_memory_stats();
let (bytes_allocated, allocs, deallocs) = (
end_stats.0 - start_stats.0,
end_stats.1 - start_stats.1,
end_stats.2 - start_stats.2,
);
println!("📈 JSON parsing (1000 operations):");
println!(
" {} allocs, {} frees, {} bytes allocated",
allocs, deallocs, bytes_allocated
);
println!(
"📊 Per parse: {:.1} bytes/parse",
bytes_allocated as f64 / 1000.0
);
// Test 4: Fixed-point vs Decimal Memory
println!("\n📊 Test 4: Fixed-point vs Decimal Memory");
println!("=======================================");
// Test Decimal operations
reset_counters();
let start_stats = get_memory_stats();
let mut decimals = Vec::new();
for i in 0..1000 {
let decimal = Decimal::from_str(&format!("0.{:04}", i)).unwrap();
decimals.push(decimal);
}
let end_stats = get_memory_stats();
let decimal_memory = end_stats.0 - start_stats.0;
let decimal_allocs = end_stats.1 - start_stats.1;
println!("📈 Decimal operations (1000 values):");
println!(
" {} allocs, {} bytes allocated",
decimal_allocs, decimal_memory
);
// Test fixed-point operations
reset_counters();
let start_stats = get_memory_stats();
let mut fixed_points = Vec::new();
for i in 0..1000 {
let fixed_point = (i as u32) * 10000; // Scale factor of 10000
fixed_points.push(fixed_point);
}
let end_stats = get_memory_stats();
let fixed_memory = end_stats.0 - start_stats.0;
let fixed_allocs = end_stats.1 - start_stats.1;
println!("📈 Fixed-point operations (1000 values):");
println!(
" {} allocs, {} bytes allocated",
fixed_allocs, fixed_memory
);
if decimal_memory > 0 && fixed_memory > 0 {
println!(
"📊 Fixed-point vs Decimal: {:.1}x less memory",
decimal_memory as f64 / fixed_memory as f64
);
}
println!("\n🎯 Memory Benchmark Summary");
println!("==========================");
println!("Key Findings:");
println!(" • Order book operations: Minimal allocation overhead");
println!(" • Fixed-point arithmetic: Significantly less memory than Decimal");
println!(" • JSON parsing: Efficient deserialization");
println!(" • Network operations: Memory usage dominated by response size");
println!("\nNote: These are ACTUAL measured values, not estimates!");
Ok(())
}
-152
View File
@@ -1,152 +0,0 @@
use polyfill_rs::ClobClient;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🚀 Network Optimization Test - polyfill-rs");
println!("===========================================");
// Test different client configurations
let clients = vec![
("Standard", ClobClient::new("https://clob.polymarket.com")),
(
"Colocated",
ClobClient::new_colocated("https://clob.polymarket.com"),
),
(
"Internet",
ClobClient::new_internet("https://clob.polymarket.com"),
),
];
for (name, client) in clients {
println!("\n📊 Testing {} Client Configuration", name);
println!("{}=", "=".repeat(40 + name.len()));
// Test 1: Server time (baseline latency)
println!(" 🔍 Server Time Test:");
let mut times = Vec::new();
for i in 0..10 {
let start = Instant::now();
match client.get_server_time().await {
Ok(timestamp) => {
let duration = start.elapsed();
times.push(duration);
if i < 2 {
println!(" Run {}: ✅ {} in {:?}", i + 1, timestamp, duration);
}
},
Err(e) => {
let duration = start.elapsed();
times.push(duration);
if i < 2 {
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
}
},
}
}
if !times.is_empty() {
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
let min = times.iter().min().unwrap();
let max = times.iter().max().unwrap();
let std_dev = {
let mean = avg.as_millis() as f64;
let variance = times
.iter()
.map(|t| (t.as_millis() as f64 - mean).powi(2))
.sum::<f64>()
/ times.len() as f64;
variance.sqrt()
};
println!(
" 📈 Average: {:.1}ms ± {:.1}ms",
avg.as_millis(),
std_dev
);
println!(" 📊 Range: {:?} - {:?}", min, max);
println!(" 🌐 Best: {:?}", min);
}
// Test 2: Market data fetching
println!(" 🔍 Market Data Test:");
let mut times = Vec::new();
for i in 0..5 {
let start = Instant::now();
match client.get_sampling_simplified_markets(None).await {
Ok(markets) => {
let duration = start.elapsed();
times.push(duration);
if i < 2 {
println!(
" Run {}: ✅ {} markets in {:?}",
i + 1,
markets.data.len(),
duration
);
}
},
Err(e) => {
let duration = start.elapsed();
times.push(duration);
if i < 2 {
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
}
},
}
}
if !times.is_empty() {
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
let min = times.iter().min().unwrap();
let max = times.iter().max().unwrap();
println!(" 📈 Average: {:?}", avg);
println!(" 📊 Range: {:?} - {:?}", min, max);
println!(" 🌐 Best: {:?}", min);
}
// Test 3: Connection reuse test
println!(" 🔍 Connection Reuse Test:");
let start = Instant::now();
for i in 0..5 {
match client.get_server_time().await {
Ok(_) => {
if i == 0 {
println!(" First request: {:?}", start.elapsed());
}
},
Err(e) => {
println!(" Error on request {}: {}", i + 1, e);
break;
},
}
}
let total_time = start.elapsed();
println!(" 📈 5 requests total: {:?}", total_time);
println!(" 📊 Average per request: {:?}", total_time / 5);
}
println!("\n🎯 Network Optimization Summary");
println!("===============================");
println!("HTTP Client Optimizations Applied:");
println!(" • Connection pooling (10-20 connections per host)");
println!(" • TCP_NODELAY enabled (disables Nagle's algorithm)");
println!(" • HTTP/2 with keep-alive");
println!(" • Optimized timeouts for different environments");
println!(" • Compression enabled/disabled based on use case");
println!("\nConfiguration Recommendations:");
println!(" • Colocated: Use for servers close to exchange");
println!(" • Internet: Use for retail/remote connections");
println!(" • Standard: Balanced settings for most use cases");
println!("\nAdditional Optimizations Available:");
println!(" • Custom DNS resolver");
println!(" • Connection pre-warming");
println!(" • Request batching");
println!(" • Circuit breaker patterns");
Ok(())
}
-137
View File
@@ -1,137 +0,0 @@
use polyfill_rs::ClobClient;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🌐 Network Latency Test for polyfill-rs");
println!("======================================");
let client = ClobClient::new("https://clob.polymarket.com");
// Test 1: Simplified markets (comparable to original 404.5ms benchmark)
println!("\n📊 Test 1: Simplified Markets");
println!("-----------------------------");
let mut times = Vec::new();
for i in 0..5 {
let start = Instant::now();
match client.get_sampling_simplified_markets(None).await {
Ok(markets) => {
let duration = start.elapsed();
times.push(duration);
println!(
" Run {}: ✅ {} markets in {:?}",
i + 1,
markets.data.len(),
duration
);
},
Err(e) => {
let duration = start.elapsed();
times.push(duration);
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
},
}
}
if !times.is_empty() {
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
let min = times.iter().min().unwrap();
let max = times.iter().max().unwrap();
println!(" 📈 Average: {:?}", avg);
println!(" 📊 Range: {:?} - {:?}", min, max);
println!(
" 🆚 vs original (404.5ms): {:.1}x",
404.5 / avg.as_millis() as f64
);
}
// Test 2: Full markets
println!("\n📊 Test 2: Full Markets");
println!("----------------------");
let mut times = Vec::new();
for i in 0..3 {
let start = Instant::now();
match client.get_sampling_markets(None).await {
Ok(markets) => {
let duration = start.elapsed();
times.push(duration);
println!(
" Run {}: ✅ {} markets in {:?}",
i + 1,
markets.data.len(),
duration
);
},
Err(e) => {
let duration = start.elapsed();
times.push(duration);
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
},
}
}
if !times.is_empty() {
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
let min = times.iter().min().unwrap();
let max = times.iter().max().unwrap();
println!(" 📈 Average: {:?}", avg);
println!(" 📊 Range: {:?} - {:?}", min, max);
}
// Test 3: Server time (lightweight endpoint)
println!("\n📊 Test 3: Server Time (Lightweight)");
println!("-----------------------------------");
let mut times = Vec::new();
for i in 0..10 {
let start = Instant::now();
match client.get_server_time().await {
Ok(timestamp) => {
let duration = start.elapsed();
times.push(duration);
if i == 0 {
println!(
" Run {}: ✅ Timestamp {} in {:?}",
i + 1,
timestamp,
duration
);
}
},
Err(e) => {
let duration = start.elapsed();
times.push(duration);
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
},
}
}
if !times.is_empty() {
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
let min = times.iter().min().unwrap();
let max = times.iter().max().unwrap();
println!(" 📈 Average: {:?}", avg);
println!(" 📊 Range: {:?} - {:?}", min, max);
println!(" 🌐 Network baseline latency: ~{:?}", min);
}
println!("\n🎯 Summary");
println!("=========");
println!("Network latency dominates end-to-end performance.");
println!("Our computational optimizations provide benefits when:");
println!("• Processing cached/local data");
println!("• Running in co-located environments");
println!("• Performing high-frequency operations");
println!();
println!("For fair comparison with polymarket-rs-client:");
println!("• Run from same geographic location");
println!("• Use same network conditions");
println!("• Measure full end-to-end latency");
Ok(())
}
-94
View File
@@ -1,94 +0,0 @@
use polyfill_rs::ClobClient;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
println!("🔍 Detailed Latency Profiling");
println!("=============================\n");
let api_key = std::env::var("POLYMARKET_API_KEY")?;
let secret = std::env::var("POLYMARKET_SECRET")?;
let passphrase = std::env::var("POLYMARKET_PASSPHRASE")?;
let api_creds = polyfill_rs::ApiCredentials {
api_key,
secret,
passphrase,
};
let mut client = ClobClient::new("https://clob.polymarket.com");
client.set_api_creds(api_creds);
println!("Running 5 requests with detailed timing breakdown...\n");
for i in 1..=5 {
println!("Request {}:", i);
let total_start = Instant::now();
// DNS + Connection establishment
let connect_start = Instant::now();
let response = client.http_client
.get(format!("{}/sampling-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let connect_time = connect_start.elapsed();
// Response headers received
let status = response.status();
let headers_time = connect_start.elapsed();
// Read response body
let body_start = Instant::now();
let body_bytes = response.bytes().await?;
let body_time = body_start.elapsed();
// Parse JSON
let parse_start = Instant::now();
let json: serde_json::Value = serde_json::from_slice(&body_bytes)?;
let parse_time = parse_start.elapsed();
let total_time = total_start.elapsed();
// Calculate derived metrics
let network_time = connect_time;
let download_time = body_time;
let overhead = total_time.saturating_sub(network_time + download_time + parse_time);
println!(" Total: {:>8.1} ms", total_time.as_micros() as f64 / 1000.0);
println!(" Network: {:>8.1} ms (DNS + TCP + TLS + HTTP)", network_time.as_micros() as f64 / 1000.0);
println!(" Headers: {:>8.1} ms (time to first byte)", headers_time.as_micros() as f64 / 1000.0);
println!(" Download: {:>8.1} ms (response body)", download_time.as_micros() as f64 / 1000.0);
println!(" JSON Parse: {:>8.1} ms (deserialization)", parse_time.as_micros() as f64 / 1000.0);
println!(" Overhead: {:>8.1} ms", overhead.as_micros() as f64 / 1000.0);
println!(" Status: {} ({})", status.as_u16(), status.canonical_reason().unwrap_or("Unknown"));
println!(" Body Size: {} bytes", body_bytes.len());
if let Some(markets) = json["data"].as_array() {
println!(" Markets: {}", markets.len());
}
println!();
// Small delay between requests
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
println!("\n📊 ANALYSIS:");
println!("============");
println!("Network time includes:");
println!(" - DNS resolution (if not cached)");
println!(" - TCP connection establishment");
println!(" - TLS handshake");
println!(" - HTTP request/response");
println!();
println!("💡 OPTIMIZATION TARGETS:");
println!("- If Network > 400ms: DNS caching, connection pooling, HTTP/2");
println!("- If Download > 100ms: Compression, smaller payload");
println!("- If JSON Parse > 50ms: Faster parsing, streaming parser");
println!("- If Overhead > 50ms: Reduce allocations, optimize client");
Ok(())
}
-140
View File
@@ -1,140 +0,0 @@
use reqwest::Client;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
println!("Testing Request Burst Patterns");
println!("===============================\n");
let client = Client::new();
// Pattern 1: Burst (no delay) - simulates high-frequency trading
println!("Pattern 1: Burst Requests (0ms delay)");
println!("======================================");
let mut burst_times = Vec::new();
for i in 1..=10 {
let start = Instant::now();
let _ = client
.get("https://clob.polymarket.com/simplified-markets?next_cursor=MA==")
.send()
.await?
.bytes()
.await?;
let elapsed = start.elapsed();
burst_times.push(elapsed);
if i <= 5 {
println!(" Request {}: {:.1} ms", i, elapsed.as_micros() as f64 / 1000.0);
}
// No delay - immediate next request
}
// Pattern 2: Short delay (50ms) - like our benchmark
println!("\nPattern 2: Short Delay (50ms between requests)");
println!("===============================================");
let mut short_delay_times = Vec::new();
for i in 1..=10 {
let start = Instant::now();
let _ = client
.get("https://clob.polymarket.com/simplified-markets?next_cursor=MA==")
.send()
.await?
.bytes()
.await?;
let elapsed = start.elapsed();
short_delay_times.push(elapsed);
if i <= 5 {
println!(" Request {}: {:.1} ms", i, elapsed.as_micros() as f64 / 1000.0);
}
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
// Pattern 3: Medium delay (100ms) - our current benchmark
println!("\nPattern 3: Medium Delay (100ms between requests)");
println!("=================================================");
let mut medium_delay_times = Vec::new();
for i in 1..=10 {
let start = Instant::now();
let _ = client
.get("https://clob.polymarket.com/simplified-markets?next_cursor=MA==")
.send()
.await?
.bytes()
.await?;
let elapsed = start.elapsed();
medium_delay_times.push(elapsed);
if i <= 5 {
println!(" Request {}: {:.1} ms", i, elapsed.as_micros() as f64 / 1000.0);
}
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
// Statistics
fn calc_stats(times: &[std::time::Duration]) -> (f64, f64, f64, f64) {
let values: Vec<f64> = times.iter().map(|d| d.as_micros() as f64 / 1000.0).collect();
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / values.len() as f64;
let std_dev = variance.sqrt();
let mut sorted = values.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
(mean, std_dev, sorted[0], sorted[sorted.len() - 1])
}
let (burst_mean, burst_std, burst_min, burst_max) = calc_stats(&burst_times);
let (short_mean, short_std, short_min, short_max) = calc_stats(&short_delay_times);
let (med_mean, med_std, med_min, med_max) = calc_stats(&medium_delay_times);
println!("\n\n📊 RESULTS");
println!("==========\n");
println!("Burst (0ms delay):");
println!(" Mean: {:.1} ms ± {:.1} ms", burst_mean, burst_std);
println!(" Range: {:.1} - {:.1} ms", burst_min, burst_max);
println!(" First request: {:.1} ms", burst_times[0].as_micros() as f64 / 1000.0);
println!(" Avg of requests 2-10: {:.1} ms",
burst_times.iter().skip(1).sum::<std::time::Duration>().as_millis() as f64 / 9.0);
println!("\nShort Delay (50ms):");
println!(" Mean: {:.1} ms ± {:.1} ms", short_mean, short_std);
println!(" Range: {:.1} - {:.1} ms", short_min, short_max);
println!("\nMedium Delay (100ms):");
println!(" Mean: {:.1} ms ± {:.1} ms", med_mean, med_std);
println!(" Range: {:.1} - {:.1} ms", med_min, med_max);
println!("\n💡 INSIGHTS");
println!("============\n");
if burst_mean < short_mean && burst_mean < med_mean {
let improvement_vs_100ms = ((med_mean - burst_mean) / med_mean) * 100.0;
println!("✅ Burst requests are fastest: {:.1}% faster than 100ms delay", improvement_vs_100ms);
println!(" This confirms connection reuse is critical!");
let warm_avg = burst_times.iter().skip(1).sum::<std::time::Duration>().as_millis() as f64 / 9.0;
let first = burst_times[0].as_micros() as f64 / 1000.0;
println!(" First request (cold): {:.1} ms", first);
println!(" Subsequent (warm): {:.1} ms", warm_avg);
println!(" Connection reuse benefit: {:.1}%", ((first - warm_avg) / first) * 100.0);
}
if burst_std < med_std {
println!("✅ Burst requests are more consistent: ±{:.1} ms vs ±{:.1} ms", burst_std, med_std);
}
println!("\n🎯 RECOMMENDATION");
println!("==================");
println!("For real-world high-frequency trading:");
println!(" - Expected latency: {:.1} ms ± {:.1} ms (with warm connection)", burst_mean, burst_std);
println!(" - First request will be slower: ~{:.1} ms (connection establishment)",
burst_times[0].as_micros() as f64 / 1000.0);
println!(" - Keep client alive between requests for best performance");
Ok(())
}
-70
View File
@@ -1,70 +0,0 @@
use polyfill_rs::ClobClient;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
println!("🔄 Connection Reuse Test");
println!("========================\n");
let api_key = std::env::var("POLYMARKET_API_KEY")?;
let secret = std::env::var("POLYMARKET_SECRET")?;
let passphrase = std::env::var("POLYMARKET_PASSPHRASE")?;
let api_creds = polyfill_rs::ApiCredentials {
api_key,
secret,
passphrase,
};
let mut client = ClobClient::new("https://clob.polymarket.com");
client.set_api_creds(api_creds);
println!("Making 10 sequential requests (should reuse connection)...\n");
let mut times = Vec::new();
for i in 1..=10 {
let start = Instant::now();
let response = client.http_client
.get(format!("{}/sampling-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let status = response.status();
let _json: serde_json::Value = response.json().await?;
let elapsed = start.elapsed();
times.push(elapsed);
println!("Request {:2}: {:>6.1} ms (status: {})",
i,
elapsed.as_micros() as f64 / 1000.0,
status.as_u16()
);
}
println!("\n📊 Analysis:");
println!("===========");
let first = times[0];
let avg_rest: std::time::Duration = times[1..].iter().sum::<std::time::Duration>() / (times.len() - 1) as u32;
println!("First request: {:.1} ms (includes connection setup)", first.as_micros() as f64 / 1000.0);
println!("Avg subsequent: {:.1} ms (should reuse connection)", avg_rest.as_micros() as f64 / 1000.0);
let improvement = ((first.as_micros() as f64 - avg_rest.as_micros() as f64) / first.as_micros() as f64) * 100.0;
if improvement > 20.0 {
println!("\n✅ Connection reuse is working! ({:.0}% faster)", improvement);
} else if improvement > 5.0 {
println!("\n⚠️ Some connection reuse, but not optimal ({:.0}% improvement)", improvement);
} else {
println!("\n❌ Connection reuse NOT working (only {:.0}% improvement)", improvement);
println!(" Expected: 30-50% improvement on subsequent requests");
}
Ok(())
}
-119
View File
@@ -1,119 +0,0 @@
use polyfill_rs::ClobClient;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
println!("Connection Pre-warming Test");
println!("===========================\n");
let api_key = std::env::var("POLYMARKET_API_KEY")?;
let secret = std::env::var("POLYMARKET_SECRET")?;
let passphrase = std::env::var("POLYMARKET_PASSPHRASE")?;
let api_creds = polyfill_rs::ApiCredentials {
api_key,
secret,
passphrase,
};
let mut client = ClobClient::new("https://clob.polymarket.com");
client.set_api_creds(api_creds);
println!("Phase 1: Cold start (no pre-warming)");
println!("=====================================");
// Make 3 requests cold
let mut cold_times = Vec::new();
for i in 1..=3 {
let start = Instant::now();
let response = client.http_client
.get(format!("{}/simplified-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let _json: serde_json::Value = response.json().await?;
let elapsed = start.elapsed();
cold_times.push(elapsed);
println!("Request {}: {:.1} ms", i, elapsed.as_micros() as f64 / 1000.0);
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
println!("\nPhase 2: Pre-warmed connection");
println!("================================");
// Pre-warm by making several requests
println!("Pre-warming with 5 requests...");
for _ in 0..5 {
let _ = client.http_client
.get(format!("{}/simplified-markets?next_cursor=MA==", client.base_url))
.send()
.await?
.bytes()
.await?;
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
println!("Testing with warmed connection...\n");
// Now test with warmed connection
let mut warm_times = Vec::new();
for i in 1..=20 {
let start = Instant::now();
let response = client.http_client
.get(format!("{}/simplified-markets?next_cursor=MA==", client.base_url))
.send()
.await?;
let _json: serde_json::Value = response.json().await?;
let elapsed = start.elapsed();
warm_times.push(elapsed);
if i <= 5 {
println!("Request {}: {:.1} ms", i, elapsed.as_micros() as f64 / 1000.0);
}
// Minimal delay
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
// Statistics
fn calc_stats(times: &[std::time::Duration]) -> (f64, f64, f64, f64) {
let values: Vec<f64> = times.iter().map(|d| d.as_micros() as f64 / 1000.0).collect();
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / values.len() as f64;
let std_dev = variance.sqrt();
let mut sorted = values.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
(mean, std_dev, sorted[0], sorted[sorted.len() - 1])
}
let (cold_mean, cold_std, cold_min, cold_max) = calc_stats(&cold_times);
let (warm_mean, warm_std, warm_min, warm_max) = calc_stats(&warm_times);
println!("\n\nResults:");
println!("========\n");
println!("Cold Start:");
println!(" Mean: {:.1} ms ± {:.1} ms", cold_mean, cold_std);
println!(" Range: {:.1} - {:.1} ms\n", cold_min, cold_max);
println!("Pre-warmed:");
println!(" Mean: {:.1} ms ± {:.1} ms", warm_mean, warm_std);
println!(" Range: {:.1} - {:.1} ms", warm_min, warm_max);
let improvement = ((cold_mean - warm_mean) / cold_mean) * 100.0;
let variance_reduction = ((cold_std - warm_std) / cold_std) * 100.0;
println!("\nImprovement:");
println!(" Speed: {:.1}% faster", improvement);
println!(" Variance: {:.1}% more consistent", variance_reduction);
if warm_std < 30.0 {
println!("\nSUCCESS: Achieved target variance (±{:.1}ms < ±30ms)", warm_std);
} else {
println!("\nStill need work: Current ±{:.1}ms, target ±30ms", warm_std);
println!("Remaining variance is likely server-side or network conditions");
}
Ok(())
}
-71
View File
@@ -1,71 +0,0 @@
use polyfill_rs::decode::fast_parse;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
println!("SIMD JSON Parsing Benchmark");
println!("============================\n");
// Fetch real data to parse
let client = reqwest::Client::new();
let response = client
.get("https://clob.polymarket.com/simplified-markets?next_cursor=MA==")
.send()
.await?;
let data = response.bytes().await?;
println!("Response size: {} KB\n", data.len() / 1024);
// Test 1: Standard serde_json
println!("Test 1: Standard serde_json");
println!("----------------------------");
let mut serde_times = Vec::new();
for i in 1..=10 {
let data_copy = data.clone();
let start = Instant::now();
let _json: serde_json::Value = serde_json::from_slice(&data_copy)?;
let elapsed = start.elapsed();
serde_times.push(elapsed);
if i <= 3 {
println!(" Run {}: {:.2} ms", i, elapsed.as_micros() as f64 / 1000.0);
}
}
// Test 2: SIMD JSON
println!("\nTest 2: SIMD JSON (simd-json)");
println!("------------------------------");
let mut simd_times = Vec::new();
for i in 1..=10 {
let mut data_copy = data.to_vec();
let start = Instant::now();
let _json: serde_json::Value = fast_parse::parse_json_fast(&mut data_copy)?;
let elapsed = start.elapsed();
simd_times.push(elapsed);
if i <= 3 {
println!(" Run {}: {:.2} ms", i, elapsed.as_micros() as f64 / 1000.0);
}
}
// Statistics
let serde_avg = serde_times.iter().sum::<std::time::Duration>().as_micros() as f64 / serde_times.len() as f64 / 1000.0;
let simd_avg = simd_times.iter().sum::<std::time::Duration>().as_micros() as f64 / simd_times.len() as f64 / 1000.0;
println!("\n\nResults:");
println!("========");
println!("serde_json: {:.2} ms", serde_avg);
println!("simd-json: {:.2} ms", simd_avg);
let speedup = serde_avg / simd_avg;
let improvement = ((serde_avg - simd_avg) / serde_avg) * 100.0;
println!("\nSpeedup: {:.2}x ({:.1}% faster)", speedup, improvement);
println!("Time saved per request: {:.2} ms", serde_avg - simd_avg);
Ok(())
}