Rust program slower than expected

Making your CPU waste a whole lot of time, then - for no reason in particular. The "speed" part of Rust (or any other lower level PL) is in the ability of the compiler to remove (needless) layers upon layers of indirections and/or checks, otherwise unavoidable in langs like Python/PHP/JS (unless JIT-ed, which is a whole another topic).

Instead of checking at runtime whether any particular instance of an object has any given method or operation, once compiled to machine code the only bare essentials are (hopefully) left. Which bytes to move from where to where. Nothing else - that's what gives it the overall "speed" boost.

Your test, by its very nature, prevented any and all optimization. Not only you're constantly turning your Map back and forth into/from a String - yet you're keeping the Map itself opaque, with no clear (low level) data types for the compiler to optimize for. Account for that, and suddenly:

use serde_json::{Map, Value, json};
use std::time::Instant;

fn main() {
    println!("Wait...");
    let start = Instant::now();
    let last = r#"{ "counter": 0 }"#;
    // (1) keep the map outside of the loop, otherwise you're 
    // just wasting CPU cycles on repeated (de)serialization into/from `String`
    let mut x: Map<String, Value> = serde_json::from_str(&last).unwrap();
    for _i in 0..3000 {
        let counter = x["counter"].to_string();
        let mut node = &mut x;
        for char in counter.chars() {
            let key = char.to_string();
            if !node.contains_key(&key) {
                let new = Value::Object(Map::new());
                node.insert(key.clone(), new);
            }
            node = node
                .get_mut(&key).unwrap()
                .as_object_mut().unwrap();
        }
        let counter = x["counter"].as_i64().unwrap();
        x["counter"] = Value::from(counter + 1);
    }
    // (2) turn it back into a `String` only at the very end
    let json = json!(x).to_string(); 
    println!("{}", json.len());
    let time_taken = Instant::now().duration_since(start);
    println!("elapsed: {:?}", time_taken);
}

- the whole thing now only takes 2-6 ms (check the playground version).

12 Likes