mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-08-18 15:08:06 +00:00
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:
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
Reference in New Issue
Block a user