use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::time::Instant;
#[inline(always)]
fn rdtsc_start() -> u64 {
unsafe {
std::arch::x86_64::_mm_lfence();
std::arch::x86_64::_rdtsc()
}
}
#[inline(always)]
fn rdtsc_end() -> u64 {
unsafe {
let mut aux = 0u32;
let tsc = std::arch::x86_64::__rdtscp(&raw mut aux);
std::arch::x86_64::_mm_lfence();
tsc
}
}
fn percentile(sorted: &[u64], p: f64) -> u64 {
let idx = ((sorted.len() as f64) * p / 100.0) as usize;
sorted[idx.min(sorted.len() - 1)]
}
fn print_row(label: &str, samples: &mut [u64]) {
samples.sort_unstable();
println!(
" {:<45} {:>7} {:>7} {:>7} {:>8} {:>8}",
label,
percentile(samples, 50.0),
percentile(samples, 90.0),
percentile(samples, 99.0),
percentile(samples, 99.9),
samples[samples.len() - 1],
);
}
fn print_header() {
println!(
" {:<45} {:>7} {:>7} {:>7} {:>8} {:>8}",
"(cycles)", "p50", "p90", "p99", "p99.9", "max"
);
}
const RESPONSE_BODY: &[u8] = b"{\"orderId\":12345,\"status\":\"FILLED\"}";
#[allow(clippy::needless_pass_by_value)] fn server_thread(listener: TcpListener) {
let (mut tcp, _) = listener.accept().unwrap();
tcp.set_nodelay(true).unwrap();
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\nX-RateLimit-Remaining: 1200\r\n\r\n{}",
RESPONSE_BODY.len(),
std::str::from_utf8(RESPONSE_BODY).unwrap(),
);
let resp_bytes = response.as_bytes();
let mut buf = [0u8; 4096];
loop {
let n = match tcp.read(&mut buf) {
Ok(0) | Err(_) => break,
Ok(n) => n,
};
let _ = n;
if tcp.write_all(resp_bytes).is_err() {
break;
}
}
}
fn bench_nexus(addr: std::net::SocketAddr) {
use nexus_net::http::ResponseReader;
use nexus_net::rest::{Client, RequestWriter};
const WARMUP: usize = 5_000;
const SAMPLES: usize = 100_000;
let tcp = TcpStream::connect(addr).unwrap();
tcp.set_nodelay(true).unwrap();
let mut writer = RequestWriter::new(&addr.to_string()).unwrap();
writer
.default_header(
"X-MBX-APIKEY",
"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
)
.unwrap();
writer
.default_header("Content-Type", "application/json")
.unwrap();
let mut reader = ResponseReader::new(4096).max_body_size(32 * 1024);
let mut conn = Client::new(tcp);
let order_body = br#"{"symbol":"BTCUSDT","side":"BUY","type":"LIMIT","quantity":"0.001","price":"67234.50"}"#;
let timestamp = "1700000000000";
let signature = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
for _ in 0..WARMUP {
let req = writer
.post("/api/v3/order")
.header("X-MBX-TIMESTAMP", timestamp)
.header("X-MBX-SIGNATURE", signature)
.body(order_body)
.finish()
.unwrap();
let resp = conn.send(req, &mut reader).unwrap();
std::hint::black_box(resp.status());
}
let mut samples = vec![0u64; SAMPLES];
for s in &mut samples {
let t0 = rdtsc_start();
let req = writer
.post("/api/v3/order")
.header("X-MBX-TIMESTAMP", timestamp)
.header("X-MBX-SIGNATURE", signature)
.body(order_body)
.finish()
.unwrap();
let resp = conn.send(req, &mut reader).unwrap();
std::hint::black_box(resp.status());
let t1 = rdtsc_end();
*s = t1 - t0;
}
print_row("nexus-net loopback (full round-trip)", &mut samples);
let iterations = 200_000u64;
let start = Instant::now();
for _ in 0..iterations {
let req = writer
.post("/api/v3/order")
.header("X-MBX-TIMESTAMP", timestamp)
.header("X-MBX-SIGNATURE", signature)
.body(order_body)
.finish()
.unwrap();
let resp = conn.send(req, &mut reader).unwrap();
std::hint::black_box(resp.status());
}
let elapsed = start.elapsed();
let rps = iterations as f64 / elapsed.as_secs_f64();
println!(
" {:<45} {:>7.0} req/sec ({:.2?} for {iterations} reqs)",
"nexus-net throughput", rps, elapsed,
);
}
fn bench_reqwest(addr: std::net::SocketAddr) {
const WARMUP: usize = 1_000;
const SAMPLES: usize = 50_000;
let client = reqwest::blocking::ClientBuilder::new()
.no_proxy()
.tcp_nodelay(true)
.pool_max_idle_per_host(1)
.build()
.unwrap();
let url = format!("http://{addr}/api/v3/order");
let order_body =
r#"{"symbol":"BTCUSDT","side":"BUY","type":"LIMIT","quantity":"0.001","price":"67234.50"}"#;
let timestamp = "1700000000000";
let signature = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
for _ in 0..WARMUP {
let resp = client
.post(&url)
.header(
"X-MBX-APIKEY",
"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
)
.header("Content-Type", "application/json")
.header("X-MBX-TIMESTAMP", timestamp)
.header("X-MBX-SIGNATURE", signature)
.body(order_body)
.send()
.unwrap();
std::hint::black_box(resp.status());
}
let mut samples = vec![0u64; SAMPLES];
for s in &mut samples {
let t0 = rdtsc_start();
let resp = client
.post(&url)
.header(
"X-MBX-APIKEY",
"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
)
.header("Content-Type", "application/json")
.header("X-MBX-TIMESTAMP", timestamp)
.header("X-MBX-SIGNATURE", signature)
.body(order_body)
.send()
.unwrap();
std::hint::black_box(resp.status());
let t1 = rdtsc_end();
*s = t1 - t0;
}
print_row("reqwest loopback (full round-trip)", &mut samples);
let iterations = 50_000u64;
let start = Instant::now();
for _ in 0..iterations {
let resp = client
.post(&url)
.header(
"X-MBX-APIKEY",
"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
)
.header("Content-Type", "application/json")
.header("X-MBX-TIMESTAMP", timestamp)
.header("X-MBX-SIGNATURE", signature)
.body(order_body)
.send()
.unwrap();
std::hint::black_box(resp.status());
}
let elapsed = start.elapsed();
let rps = iterations as f64 / elapsed.as_secs_f64();
println!(
" {:<45} {:>7.0} req/sec ({:.2?} for {iterations} reqs)",
"reqwest throughput", rps, elapsed,
);
}
fn main() {
println!("\n REST loopback benchmark: real TCP, localhost");
println!(" POST + 4 headers + JSON body → 200 OK + JSON response");
println!(" Nagle disabled on both sides\n");
print_header();
println!();
let listener1 = TcpListener::bind("127.0.0.1:0").unwrap();
let addr1 = listener1.local_addr().unwrap();
let server1 = std::thread::spawn(move || server_thread(listener1));
bench_nexus(addr1);
drop(server1);
let listener2 = TcpListener::bind("127.0.0.1:0").unwrap();
let addr2 = listener2.local_addr().unwrap();
let server2 = std::thread::spawn(move || server_thread(listener2));
bench_reqwest(addr2);
drop(server2);
println!();
}