mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-08-07 01:37:46 +00:00
Merge pull request #35 from floor-licker/feat/official-sdk-benchmark
perf: benchmark official Polymarket SDK
This commit is contained in:
Generated
+1223
-834
File diff suppressed because it is too large
Load Diff
+9
-1
@@ -20,7 +20,7 @@ futures = "0.3"
|
||||
futures-util = "0.3"
|
||||
|
||||
# HTTP client
|
||||
reqwest = { version = "0.12", features = ["json", "stream", "gzip"] }
|
||||
reqwest = { version = "0.13", default-features = false, features = ["json", "query", "stream", "gzip", "rustls", "http2"] }
|
||||
hickory-resolver = "0.26"
|
||||
|
||||
# Serialization
|
||||
@@ -64,6 +64,9 @@ rand = "0.8"
|
||||
# Optional WebSocket support for streaming
|
||||
tokio-tungstenite = { version = "0.21", optional = true, features = ["native-tls"] }
|
||||
|
||||
# Optional benchmark-only dependency for comparing against Polymarket's active Rust SDK.
|
||||
polymarket_client_sdk_v2 = { git = "https://github.com/Polymarket/rs-clob-client-v2", rev = "8ba5008733c3c03e92041eef8b1cb8495dbed718", features = ["clob"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
tokio-test = "0.4"
|
||||
@@ -75,11 +78,16 @@ env_logger = "0.10"
|
||||
default = ["stream"]
|
||||
stream = ["tokio-tungstenite"]
|
||||
side-by-side-benchmark = []
|
||||
official-client-benchmark = ["dep:polymarket_client_sdk_v2"]
|
||||
|
||||
[[example]]
|
||||
name = "side_by_side_benchmark"
|
||||
required-features = ["side-by-side-benchmark"]
|
||||
|
||||
[[example]]
|
||||
name = "official_client_side_by_side_benchmark"
|
||||
required-features = ["official-client-benchmark"]
|
||||
|
||||
[[bench]]
|
||||
name = "book_updates"
|
||||
harness = false
|
||||
|
||||
@@ -43,11 +43,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
**Real-World API Performance (with network I/O)**
|
||||
|
||||
End-to-end performance with Polymarket's API, including network latency, JSON parsing, and decompression:
|
||||
Real-world Polymarket API latency broken down by request phase:
|
||||
|
||||
| Operation | polyfill-rs | polymarket-rs-client | Official Python Client |
|
||||
|-----------|-------------|----------------------|------------------------|
|
||||
| **Fetch Markets** | **321.6 ms ± 92.9 ms** | 409.3 ms ± 137.6 ms | 1.366 s ± 0.048 s |
|
||||

|
||||
|
||||
| Operation | Metric | polyfill-rs | rs-clob-client-v2 | polymarket-rs-client | Official Python Client |
|
||||
|-----------|--------|-------------|-------------------|----------------------|------------------------|
|
||||
| **Fetch Markets** | mean ± sd | **321.6 ms ± 92.9 ms** | - | 409.3 ms ± 137.6 ms | 1.366 s ± 0.048 s |
|
||||
| **Cold Start** | single run | 651.7 ms | **543.9 ms** | - | - |
|
||||
| **Warm Connection** | single run | **202.9 ms** | 497.2 ms | - | - |
|
||||
| **Steady Typed Total** | p50 / p95 / p99 | **209.9 / 276.1 / 509.5 ms** | 215.9 / 284.7 / 312.4 ms | - | - |
|
||||
| **Network-Only Byte Fetch** | p50 / p95 / p99 | 382.5 / 590.3 / 626.7 ms | **300.1 / 449.0 / 520.1 ms** | - | - |
|
||||
| **CPU Parse Only** | p50 / p95 / p99 | **0.5 / 1.2 / 1.4 ms** | 1.3 / 1.4 / 1.4 ms | - | - |
|
||||
|
||||
|
||||
**Performance vs polymarket-rs-client:**
|
||||
@@ -55,7 +62,7 @@ End-to-end performance with Polymarket's API, including network latency, JSON pa
|
||||
- **32.5% more consistent**
|
||||
- **4.2x faster** than Official Python Client
|
||||
|
||||
**Benchmark Methodology:** All benchmarks run side-by-side on the same machine, same network, same time using 20 iterations, 100ms delay between requests, /simplified-markets endpoint. Best performance achieved with connection keep-alive enabled. See `examples/side_by_side_benchmark.rs` in commit `a63a170`: https://github.com/floor-licker/polyfill-rs/blob/a63a170/examples/side_by_side_benchmark.rs for the complete benchmark implementation.
|
||||
**Benchmark Methodology:** The `rs-clob-client-v2` comparison separates cold start, warm connection, steady-state typed requests, network-only byte fetches, and CPU-only parsing. Steady-state rows use 20 paired iterations with alternating order and 100ms delay; parse rows use 200 iterations from a cached 480KB payload. The network-only row compares byte fetches through each HTTP stack without typed deserialization. Run it with `cargo run --release --example official_client_side_by_side_benchmark --features official-client-benchmark`. See `examples/side_by_side_benchmark.rs` in commit `a63a170`: https://github.com/floor-licker/polyfill-rs/blob/a63a170/examples/side_by_side_benchmark.rs for the original legacy benchmark implementation.
|
||||
|
||||
**Computational Performance (pure CPU, no I/O)**
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="980" height="380" viewBox="0 0 980 380" role="img" aria-labelledby="title desc">
|
||||
<title id="title">polyfill-rs benchmark bars</title>
|
||||
<desc id="desc">Minimal horizontal bars visualizing p50 benchmark latency. Lower values are faster.</desc>
|
||||
<style>
|
||||
text { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
||||
.group { fill: #6b7280; font-size: 12px; font-weight: 650; }
|
||||
.label { fill: #374151; font-size: 12px; font-weight: 600; }
|
||||
.value { fill: #111827; font-size: 12px; font-weight: 650; }
|
||||
.rule { stroke: #e5e7eb; stroke-width: 1; }
|
||||
.polyfill { fill: #0f766e; }
|
||||
.compare { fill: #9ca3af; }
|
||||
</style>
|
||||
|
||||
<rect width="980" height="380" fill="#ffffff"/>
|
||||
|
||||
<text class="group" x="40" y="38">Steady typed total, p50</text>
|
||||
<text class="label" x="40" y="68">polyfill-rs</text>
|
||||
<rect class="polyfill" x="210" y="54" width="584" height="18" rx="3"/>
|
||||
<text class="value" x="812" y="68">209.9 ms</text>
|
||||
<text class="label" x="40" y="96">rs-clob-client-v2</text>
|
||||
<rect class="compare" x="210" y="82" width="600" height="18" rx="3"/>
|
||||
<text class="value" x="828" y="96">215.9 ms</text>
|
||||
|
||||
<line class="rule" x1="40" y1="128" x2="940" y2="128"/>
|
||||
|
||||
<text class="group" x="40" y="158">Network-only byte fetch, p50</text>
|
||||
<text class="label" x="40" y="188">polyfill-rs</text>
|
||||
<rect class="polyfill" x="210" y="174" width="600" height="18" rx="3"/>
|
||||
<text class="value" x="828" y="188">382.5 ms</text>
|
||||
<text class="label" x="40" y="216">rs-clob-client-v2</text>
|
||||
<rect class="compare" x="210" y="202" width="471" height="18" rx="3"/>
|
||||
<text class="value" x="699" y="216">300.1 ms</text>
|
||||
|
||||
<line class="rule" x1="40" y1="248" x2="940" y2="248"/>
|
||||
|
||||
<text class="group" x="40" y="278">CPU parse only, p50</text>
|
||||
<text class="label" x="40" y="308">polyfill-rs</text>
|
||||
<rect class="polyfill" x="210" y="294" width="231" height="18" rx="3"/>
|
||||
<text class="value" x="459" y="308">0.5 ms</text>
|
||||
<text class="label" x="40" y="336">rs-clob-client-v2</text>
|
||||
<rect class="compare" x="210" y="322" width="600" height="18" rx="3"/>
|
||||
<text class="value" x="828" y="336">1.3 ms</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,507 @@
|
||||
//! Side-by-side benchmark comparing polyfill-rs against Polymarket's
|
||||
//! `rs-clob-client-v2`.
|
||||
//!
|
||||
//! Run with:
|
||||
//! `cargo run --release --example official_client_side_by_side_benchmark --features official-client-benchmark`
|
||||
//!
|
||||
//! Optional env vars:
|
||||
//! - `POLYMARKET_BENCH_HOST` defaults to `https://clob.polymarket.com`
|
||||
//! - `POLYMARKET_BENCH_ITERATIONS` defaults to `20`
|
||||
//! - `POLYMARKET_BENCH_WARMUPS` defaults to `3`
|
||||
//! - `POLYMARKET_BENCH_PARSE_ITERATIONS` defaults to `200`
|
||||
//! - `POLYMARKET_BENCH_DELAY_MS` defaults to `100`
|
||||
//! - `POLYMARKET_BENCH_KEEPALIVE` defaults to `true`
|
||||
|
||||
use std::hint::black_box;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use polyfill_rs::ClobClient;
|
||||
use polymarket_client_sdk_v2::clob::types::response::{
|
||||
Page as OfficialPage, SimplifiedMarketResponse as OfficialSimplifiedMarketResponse,
|
||||
};
|
||||
use polymarket_client_sdk_v2::clob::{Client as OfficialClient, Config as OfficialConfig};
|
||||
|
||||
const INITIAL_CURSOR: &str = "MA==";
|
||||
const OFFICIAL_SDK_REV: &str = "8ba5008733c3c03e92041eef8b1cb8495dbed718";
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Sample {
|
||||
elapsed: Duration,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Stats {
|
||||
mean_ms: f64,
|
||||
std_dev_ms: f64,
|
||||
min_ms: f64,
|
||||
max_ms: f64,
|
||||
p50_ms: f64,
|
||||
p95_ms: f64,
|
||||
p99_ms: f64,
|
||||
}
|
||||
|
||||
fn env_usize(name: &str, default: usize) -> usize {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(default)
|
||||
}
|
||||
|
||||
fn env_u64(name: &str, default: u64) -> u64 {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(default)
|
||||
}
|
||||
|
||||
fn env_bool(name: &str, default: bool) -> bool {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(default)
|
||||
}
|
||||
|
||||
fn percentile(sorted: &[f64], percentile: f64) -> f64 {
|
||||
let rank = (percentile * sorted.len() as f64).ceil() as usize;
|
||||
let index = rank.saturating_sub(1).min(sorted.len() - 1);
|
||||
sorted[index]
|
||||
}
|
||||
|
||||
fn calc_stats(times: &[Duration]) -> Option<Stats> {
|
||||
if times.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut values: Vec<f64> = times
|
||||
.iter()
|
||||
.map(|duration| duration.as_micros() as f64 / 1000.0)
|
||||
.collect();
|
||||
values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
|
||||
|
||||
let mean_ms = values.iter().sum::<f64>() / values.len() as f64;
|
||||
let variance = values
|
||||
.iter()
|
||||
.map(|value| (value - mean_ms).powi(2))
|
||||
.sum::<f64>()
|
||||
/ values.len() as f64;
|
||||
|
||||
Some(Stats {
|
||||
mean_ms,
|
||||
std_dev_ms: variance.sqrt(),
|
||||
min_ms: values[0],
|
||||
max_ms: values[values.len() - 1],
|
||||
p50_ms: percentile(&values, 0.50),
|
||||
p95_ms: percentile(&values, 0.95),
|
||||
p99_ms: percentile(&values, 0.99),
|
||||
})
|
||||
}
|
||||
|
||||
fn print_stats(name: &str, stats: Option<Stats>, successes: usize, attempts: usize) {
|
||||
println!("{name}:");
|
||||
match stats {
|
||||
Some(stats) => {
|
||||
println!(
|
||||
" p50/p95/p99: {:.1} / {:.1} / {:.1} ms",
|
||||
stats.p50_ms, stats.p95_ms, stats.p99_ms
|
||||
);
|
||||
println!(
|
||||
" mean: {:.1} ms +/- {:.1} ms",
|
||||
stats.mean_ms, stats.std_dev_ms
|
||||
);
|
||||
println!(
|
||||
" range: {:.1} - {:.1} ms",
|
||||
stats.min_ms, stats.max_ms
|
||||
);
|
||||
println!(" success: {successes}/{attempts}");
|
||||
},
|
||||
None => println!(" success: 0/{attempts}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_single(name: &str, sample: &Result<Sample, String>) {
|
||||
match sample {
|
||||
Ok(sample) => println!(
|
||||
"{name}: {:.1} ms ({} items)",
|
||||
sample.elapsed.as_micros() as f64 / 1000.0,
|
||||
sample.count
|
||||
),
|
||||
Err(error) => println!("{name}: ERROR - {error}"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn time_polyfill_typed(client: &ClobClient) -> Result<Sample, String> {
|
||||
let start = Instant::now();
|
||||
let page = client
|
||||
.get_simplified_markets(Some(INITIAL_CURSOR))
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(Sample {
|
||||
elapsed: start.elapsed(),
|
||||
count: page.data.len(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn time_official_typed(client: &OfficialClient) -> Result<Sample, String> {
|
||||
let start = Instant::now();
|
||||
let page = client
|
||||
.simplified_markets(Some(INITIAL_CURSOR.to_string()))
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(Sample {
|
||||
elapsed: start.elapsed(),
|
||||
count: page.data.len(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn time_polyfill_cold(host: &str) -> Result<Sample, String> {
|
||||
let start = Instant::now();
|
||||
let client = ClobClient::new(host);
|
||||
let page = client
|
||||
.get_simplified_markets(Some(INITIAL_CURSOR))
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(Sample {
|
||||
elapsed: start.elapsed(),
|
||||
count: page.data.len(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn time_official_cold(host: &str) -> Result<Sample, String> {
|
||||
let start = Instant::now();
|
||||
let client =
|
||||
OfficialClient::new(host, OfficialConfig::default()).map_err(|error| error.to_string())?;
|
||||
let page = client
|
||||
.simplified_markets(Some(INITIAL_CURSOR.to_string()))
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(Sample {
|
||||
elapsed: start.elapsed(),
|
||||
count: page.data.len(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn time_polyfill_raw(client: &ClobClient, url: &str) -> Result<Sample, String> {
|
||||
let start = Instant::now();
|
||||
let bytes = client
|
||||
.http_client
|
||||
.get(url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(Sample {
|
||||
elapsed: start.elapsed(),
|
||||
count: bytes.len(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn time_reqwest_raw(client: &reqwest::Client, url: &str) -> Result<Sample, String> {
|
||||
let start = Instant::now();
|
||||
let bytes = client
|
||||
.get(url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(Sample {
|
||||
elapsed: start.elapsed(),
|
||||
count: bytes.len(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn run_typed_pairs(
|
||||
polyfill_client: &ClobClient,
|
||||
official_client: &OfficialClient,
|
||||
iterations: usize,
|
||||
delay: Duration,
|
||||
) -> (Vec<Duration>, Vec<Duration>) {
|
||||
let mut polyfill_times = Vec::with_capacity(iterations);
|
||||
let mut official_times = Vec::with_capacity(iterations);
|
||||
|
||||
for i in 1..=iterations {
|
||||
let polyfill_first = i % 2 == 1;
|
||||
let (polyfill_result, official_result) = if polyfill_first {
|
||||
let polyfill_result = time_polyfill_typed(polyfill_client).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let official_result = time_official_typed(official_client).await;
|
||||
(polyfill_result, official_result)
|
||||
} else {
|
||||
let official_result = time_official_typed(official_client).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let polyfill_result = time_polyfill_typed(polyfill_client).await;
|
||||
(polyfill_result, official_result)
|
||||
};
|
||||
|
||||
if let Ok(sample) = polyfill_result {
|
||||
polyfill_times.push(sample.elapsed);
|
||||
}
|
||||
if let Ok(sample) = official_result {
|
||||
official_times.push(sample.elapsed);
|
||||
}
|
||||
|
||||
tokio::time::sleep(delay).await;
|
||||
}
|
||||
|
||||
(polyfill_times, official_times)
|
||||
}
|
||||
|
||||
async fn run_raw_pairs(
|
||||
polyfill_client: &ClobClient,
|
||||
official_style_client: &reqwest::Client,
|
||||
url: &str,
|
||||
iterations: usize,
|
||||
delay: Duration,
|
||||
) -> (Vec<Duration>, Vec<Duration>) {
|
||||
let mut polyfill_times = Vec::with_capacity(iterations);
|
||||
let mut official_style_times = Vec::with_capacity(iterations);
|
||||
|
||||
for i in 1..=iterations {
|
||||
let polyfill_first = i % 2 == 1;
|
||||
let (polyfill_result, official_style_result) = if polyfill_first {
|
||||
let polyfill_result = time_polyfill_raw(polyfill_client, url).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let official_style_result = time_reqwest_raw(official_style_client, url).await;
|
||||
(polyfill_result, official_style_result)
|
||||
} else {
|
||||
let official_style_result = time_reqwest_raw(official_style_client, url).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let polyfill_result = time_polyfill_raw(polyfill_client, url).await;
|
||||
(polyfill_result, official_style_result)
|
||||
};
|
||||
|
||||
if let Ok(sample) = polyfill_result {
|
||||
polyfill_times.push(sample.elapsed);
|
||||
}
|
||||
if let Ok(sample) = official_style_result {
|
||||
official_style_times.push(sample.elapsed);
|
||||
}
|
||||
|
||||
tokio::time::sleep(delay).await;
|
||||
}
|
||||
|
||||
(polyfill_times, official_style_times)
|
||||
}
|
||||
|
||||
async fn run_typed_warmups(
|
||||
polyfill_client: &ClobClient,
|
||||
official_client: &OfficialClient,
|
||||
warmups: usize,
|
||||
delay: Duration,
|
||||
) {
|
||||
for _ in 0..warmups {
|
||||
let _ = time_polyfill_typed(polyfill_client).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let _ = time_official_typed(official_client).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn official_style_http_client() -> Result<reqwest::Client, reqwest::Error> {
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
reqwest::header::USER_AGENT,
|
||||
reqwest::header::HeaderValue::from_static("rs_clob_client"),
|
||||
);
|
||||
headers.insert(
|
||||
reqwest::header::ACCEPT,
|
||||
reqwest::header::HeaderValue::from_static("*/*"),
|
||||
);
|
||||
headers.insert(
|
||||
reqwest::header::CONNECTION,
|
||||
reqwest::header::HeaderValue::from_static("keep-alive"),
|
||||
);
|
||||
headers.insert(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
reqwest::header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
|
||||
reqwest::Client::builder().default_headers(headers).build()
|
||||
}
|
||||
|
||||
fn parse_polyfill_once(bytes: &[u8]) -> Result<Duration, serde_json::Error> {
|
||||
let start = Instant::now();
|
||||
let page: polyfill_rs::types::SimplifiedMarketsResponse = serde_json::from_slice(bytes)?;
|
||||
black_box(page);
|
||||
Ok(start.elapsed())
|
||||
}
|
||||
|
||||
fn parse_official_direct_once(bytes: &[u8]) -> Result<Duration, serde_json::Error> {
|
||||
let start = Instant::now();
|
||||
let page: OfficialPage<OfficialSimplifiedMarketResponse> = serde_json::from_slice(bytes)?;
|
||||
black_box(page);
|
||||
Ok(start.elapsed())
|
||||
}
|
||||
|
||||
fn parse_official_helper_once(bytes: &[u8]) -> Result<Duration, serde_json::Error> {
|
||||
let start = Instant::now();
|
||||
let value: serde_json::Value = serde_json::from_slice(bytes)?;
|
||||
let page: Option<OfficialPage<OfficialSimplifiedMarketResponse>> =
|
||||
serde_json::from_value(value)?;
|
||||
black_box(page);
|
||||
Ok(start.elapsed())
|
||||
}
|
||||
|
||||
fn run_parse_samples<F>(
|
||||
bytes: &[u8],
|
||||
iterations: usize,
|
||||
mut parse_once: F,
|
||||
) -> Result<Vec<Duration>, serde_json::Error>
|
||||
where
|
||||
F: FnMut(&[u8]) -> Result<Duration, serde_json::Error>,
|
||||
{
|
||||
let mut times = Vec::with_capacity(iterations);
|
||||
for _ in 0..iterations {
|
||||
times.push(parse_once(bytes)?);
|
||||
}
|
||||
Ok(times)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let host = std::env::var("POLYMARKET_BENCH_HOST")
|
||||
.unwrap_or_else(|_| "https://clob.polymarket.com".to_string());
|
||||
let url = format!("{host}/simplified-markets?next_cursor={INITIAL_CURSOR}");
|
||||
let iterations = env_usize("POLYMARKET_BENCH_ITERATIONS", 20);
|
||||
let warmups = env_usize("POLYMARKET_BENCH_WARMUPS", 3);
|
||||
let parse_iterations = env_usize("POLYMARKET_BENCH_PARSE_ITERATIONS", 200);
|
||||
let delay = Duration::from_millis(env_u64("POLYMARKET_BENCH_DELAY_MS", 100));
|
||||
let keepalive = env_bool("POLYMARKET_BENCH_KEEPALIVE", true);
|
||||
|
||||
println!("=======================================================");
|
||||
println!(" polyfill-rs vs rs-clob-client-v2 benchmark");
|
||||
println!("=======================================================");
|
||||
println!("Endpoint: {url}");
|
||||
println!("Iterations: {iterations}");
|
||||
println!("Warmups: {warmups}");
|
||||
println!("Parse iterations: {parse_iterations}");
|
||||
println!("Delay: {} ms", delay.as_millis());
|
||||
println!("polyfill-rs background keepalive: {keepalive}");
|
||||
println!("rs-clob-client-v2 rev: {OFFICIAL_SDK_REV}");
|
||||
println!();
|
||||
|
||||
println!("Cold start: client construction + first typed request");
|
||||
println!("-------------------------------------------------------");
|
||||
let polyfill_cold = time_polyfill_cold(&host).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let official_cold = time_official_cold(&host).await;
|
||||
print_single("polyfill-rs", &polyfill_cold);
|
||||
print_single("rs-clob-client-v2", &official_cold);
|
||||
println!();
|
||||
|
||||
let polyfill_client = ClobClient::new(&host);
|
||||
let official_client = OfficialClient::new(&host, OfficialConfig::default())?;
|
||||
let official_style_http = official_style_http_client()?;
|
||||
|
||||
if keepalive {
|
||||
polyfill_client
|
||||
.start_keepalive(Duration::from_secs(30))
|
||||
.await;
|
||||
}
|
||||
|
||||
run_typed_warmups(&polyfill_client, &official_client, warmups, delay).await;
|
||||
|
||||
println!("Warm connection: first typed request after warmup");
|
||||
println!("-------------------------------------------------------");
|
||||
let polyfill_warm = time_polyfill_typed(&polyfill_client).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let official_warm = time_official_typed(&official_client).await;
|
||||
print_single("polyfill-rs", &polyfill_warm);
|
||||
print_single("rs-clob-client-v2", &official_warm);
|
||||
println!();
|
||||
|
||||
println!("Steady state: typed client total time");
|
||||
println!("-------------------------------------------------------");
|
||||
let (polyfill_typed_times, official_typed_times) =
|
||||
run_typed_pairs(&polyfill_client, &official_client, iterations, delay).await;
|
||||
print_stats(
|
||||
"polyfill-rs",
|
||||
calc_stats(&polyfill_typed_times),
|
||||
polyfill_typed_times.len(),
|
||||
iterations,
|
||||
);
|
||||
println!();
|
||||
print_stats(
|
||||
"rs-clob-client-v2",
|
||||
calc_stats(&official_typed_times),
|
||||
official_typed_times.len(),
|
||||
iterations,
|
||||
);
|
||||
println!();
|
||||
|
||||
println!("Steady state: network-only byte fetch");
|
||||
println!("-------------------------------------------------------");
|
||||
let _ = time_polyfill_raw(&polyfill_client, &url).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let _ = time_reqwest_raw(&official_style_http, &url).await;
|
||||
tokio::time::sleep(delay).await;
|
||||
let (polyfill_raw_times, official_style_raw_times) = run_raw_pairs(
|
||||
&polyfill_client,
|
||||
&official_style_http,
|
||||
&url,
|
||||
iterations,
|
||||
delay,
|
||||
)
|
||||
.await;
|
||||
print_stats(
|
||||
"polyfill-rs HTTP",
|
||||
calc_stats(&polyfill_raw_times),
|
||||
polyfill_raw_times.len(),
|
||||
iterations,
|
||||
);
|
||||
println!();
|
||||
print_stats(
|
||||
"rs-clob-client-v2-style HTTP",
|
||||
calc_stats(&official_style_raw_times),
|
||||
official_style_raw_times.len(),
|
||||
iterations,
|
||||
);
|
||||
println!();
|
||||
|
||||
println!("CPU-only parse from cached payload");
|
||||
println!("-------------------------------------------------------");
|
||||
let payload = polyfill_client
|
||||
.http_client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await?
|
||||
.bytes()
|
||||
.await?
|
||||
.to_vec();
|
||||
println!("payload: {} bytes", payload.len());
|
||||
let polyfill_parse_times = run_parse_samples(&payload, parse_iterations, parse_polyfill_once)?;
|
||||
let official_direct_parse_times =
|
||||
run_parse_samples(&payload, parse_iterations, parse_official_direct_once)?;
|
||||
let official_helper_parse_times =
|
||||
run_parse_samples(&payload, parse_iterations, parse_official_helper_once)?;
|
||||
print_stats(
|
||||
"polyfill-rs typed parse",
|
||||
calc_stats(&polyfill_parse_times),
|
||||
polyfill_parse_times.len(),
|
||||
parse_iterations,
|
||||
);
|
||||
println!();
|
||||
print_stats(
|
||||
"rs-clob-client-v2 direct typed parse",
|
||||
calc_stats(&official_direct_parse_times),
|
||||
official_direct_parse_times.len(),
|
||||
parse_iterations,
|
||||
);
|
||||
println!();
|
||||
print_stats(
|
||||
"rs-clob-client-v2 request-helper parse",
|
||||
calc_stats(&official_helper_parse_times),
|
||||
official_helper_parse_times.len(),
|
||||
parse_iterations,
|
||||
);
|
||||
|
||||
if keepalive {
|
||||
polyfill_client.stop_keepalive().await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user