I'm getting much worst performance than what I expect when running some simple requests with the reqwest
crate. I wrote a simple program just to demonstrate that:
use tokio::time::Instant;
const URL: &str = "http://localhost:3000/health";
#[tokio::main]
async fn main() {
{
let now = Instant::now();
reqwest::get(URL).await.unwrap();
println!("reqwest: {:.3?}", now.elapsed());
}
{
let now = Instant::now();
ureq::get(URL).call().unwrap();
println!("ureq: {:.3?}", now.elapsed());
}
}
When running it I get:
$ cargo run --bin perf --release
Finished release [optimized] target(s) in 0.08s
Running `target/release/perf`
reqwest: 33.126ms
ureq: 543.672µs
That's a 60x difference. I've done a bit more testing with concurrent and parallel queries (some of those experiments can be seen here) and it even gets worth when increasing the parallelism.
How do I get better query performance with reqwest
?
Profiling the program execution with samply showed that reqwest is spending most of its time in openssl::ssl::connector::SslConnector::builder
on the test machine running Ubuntu 23.10.
reqwest
supports many optional features that may have an impact of the performances. In that cas, using the rustls-tls
feature of the default native-tls
drastically reduce the reqwest's client creation time.
In Cargo.toml
, native-tls
may be disabled by specifying default-features = false
on the reqwest
dependency:
[dependencies]
reqwest = { version = "0.11.23", default-features = false, features = ["rustls-tls"] }
With only that change, the same test program now produces much closer performance between reqwest
and ureq
:
$ cargo run --bin perf --release
Finished release [optimized] target(s) in 0.08s
Running `target/release/perf`
reqwest: 539.808µs
ureq: 208.114µs
The results are now quite close and shouldn't be read as anything significant: no effort was done to be statistically relevant.