I'm using Tokio
, Serde
and Reqwest
to do HTTP requests. What I see that request execution is extremely slow comparing to Swift or Kotlin .(There I also do request + serialization, all on the same laptop, at the same time).
Average out of 10:
- Rust: 1309 ms
- Kotlin: 335 ms
- Swift : 265 ms
Is there anything wrong with my code?
My Rust code:
use serde::{Deserialize, Serialize};
//URLs
const SWAPI_BASE_URL: &str = "https://swapi.dev/api/";
const PEOPLE: &str = "people";
//Threaded runtime
lazy_static! {
static ref RUN_TIME: tokio::runtime::Runtime = tokio::runtime::Builder::new()
.threaded_scheduler()
.enable_all()
.build()
.unwrap();
}
//DTO
#[repr(C)]
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResponsePeople {
pub count: i64,
pub next: String,
pub results: Vec<People>,
}
#[repr(C)]
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct People {
pub name: String,
pub height: String,
pub mass: String,
#[serde(rename = "hair_color")]
pub hair_color: String,
#[serde(rename = "skin_color")]
pub skin_color: String,
#[serde(rename = "eye_color")]
pub eye_color: String,
#[serde(rename = "birth_year")]
pub birth_year: String,
pub gender: String,
pub homeworld: String,
pub films: Vec<String>,
pub species: Vec<String>,
pub vehicles: Vec<String>,
pub starships: Vec<String>,
pub created: String,
pub edited: String,
pub url: String,
}
//Callback
#[allow(non_snake_case)]
pub trait SwapiCallback {
fn onLoad(&mut self, s: Vec<People>);
fn onError(&mut self, s: &str);
}
//SwapiClient
#[repr(C)]
pub struct SwapiClient();
#[allow(non_snake_case)]
impl SwapiClient {
pub fn new() -> SwapiClient {
SwapiClient()
}
pub fn loadAllPeople(&self, mut callback: Box<dyn SwapiCallback + Send>) {
(*RUN_TIME).spawn(async move {
let res = load_all_people().await;
match res {
Ok(root) => {
//print response
//println!("Response: {:#?}", root.results);
callback.onLoad(root.results);
}
Err(err) => {
let error = format!("Failed to load people {}", err);
println!("Error: {}", error);
callback.onError(error.as_str())
}
}
});
}
}
pub async fn load_all_people() -> Result<ResponsePeople, Box<dyn std::error::Error>> {
let url = format!("{}{}", SWAPI_BASE_URL, PEOPLE);
let people: ResponsePeople = reqwest::get(url.as_str())
.await?
.json()
.await?;
Ok(people)
}
Main:
//swapi
pub mod swapi;
//used in swapi client
#[macro_use]
extern crate lazy_static;
//thread sleep
use std::{thread, time};
//measure time
use std::time::Instant;
//DTO
use swapi::{SwapiCallback, People};
fn main() {
println!("Main started");
swapi_call_with_thread_sleep();
println!("Main finished");
}
fn swapi_call_with_thread_sleep() {
let client = swapi::SwapiClient::new();
for _i in 0..10 {
thread_sleep(500);
let callback = Callback::new();
client.loadAllPeople(Box::new(callback));
}
thread_sleep(10000);
}
//Thread sleep
fn thread_sleep(millis: u64) {
let sleep = time::Duration::from_millis(millis);
thread::sleep(sleep);
}
//Create callback
struct Callback {
start: Instant,
}
impl Callback {
fn new() -> Callback {
Callback {
start: Instant::now(),
}
}
}
//Send - types that can be transferred across thread boundaries.
unsafe impl Send for Callback {}
//require to share it between threads
impl SwapiCallback for Callback {
#[allow(non_snake_case)]
fn onLoad(&mut self, s: Vec<People>) {
let diff = self.start.elapsed().as_millis();
println!("Request: count {}; duration: {}", s.len(), diff);
}
#[allow(non_snake_case)]
fn onError(&mut self, s: &str) {
println!("Error: {:#?}", s);
}
}
Results are the same when running with:
* cargo run
* cargo run --release
I found a similar question: