I'm looking to create a high throughput, low-latency marketplace using Rust. I was doing some performance testing on the serialization and found that it was pretty slow (0.05ms). After some investigation I found that using Tokio and Async with Rust slowed down code that is just part of a function that has an await in it.
I have two main questions:
- Is there any information on why this might be?
- How can I trace the code to see exactly what's being run so I can find out what's going on (I've done profiling already using perf and hotspot).
Below is the code that I've created that shows the performance difference. It runs a "with_await" and a "without_await" function in two configurations, once just by awaiting and once as a spawned future, and both show similar performance. (These times are in ms)
with_await 0.04643426799999998
without_await 0.009190136999999998
without_await 0.009858687000000007
with_await 0.050352950999999944
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde_json::Value;
use tokio::time::sleep;
static JSON_BLOB: &str = "{\"arg\":{\"channel\":\"basket_orders\",\"order_name\":\"socks\"},\"data\":[{\"prices\":[[\"15.15\",\"22.22\",\"12.5\",\"22.0\"]],\"amounts\":[[\"10\",\"20\",\"10\",\"15\"]],\"ts\":\"1680917016349\"}]}";
pub fn utcnow() -> Duration {
SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
}
async fn with_await() {
let mut diff_sum = 0_f64;
for count in 1..=1000 {
sleep(Duration::from_millis(1)).await;
let blah: &str = JSON_BLOB.as_ref();
let received_time = utcnow().as_nanos();
let value: Value = serde_json::from_str(blah).unwrap();
let millis_diff = (utcnow().as_nanos() - received_time) as f64 / 1_000_000.0;
diff_sum += millis_diff;
}
println!("with_await {}", diff_sum / 1000_f64);
}
async fn without_await() {
let mut diff_sum = 0_f64;
for count in 1..=1000 {
let blah: &str = JSON_BLOB.as_ref();
let received_time = utcnow().as_nanos();
let value: Value = serde_json::from_str(blah).unwrap();
let millis_diff = (utcnow().as_nanos() - received_time) as f64 / 1_000_000.0;
diff_sum += millis_diff;
}
println!("without_await {}", diff_sum / 1000_f64);
}
#[tokio::main]
async fn main() {
with_await().await;
without_await().await;
let mut handles = vec![];
handles.push(tokio::spawn(with_await()));
handles.push(tokio::spawn(without_await()));
futures::future::join_all(handles).await;
}
Dependencies are:
[dependencies]
futures = "0.3.28"
serde = "1.0.159"
serde_json = "1.0.95"
tokio = { version = "1.27.0", features = ["full"] }