Skip to main content

yaml_bench/
yaml_bench.rs

1use std::collections::BTreeMap;
2use std::time::Instant;
3
4use dynamic::{Dynamic, ToYaml};
5
6fn gen_node(depth: i64, idx: i64) -> Dynamic {
7    let mut m: BTreeMap<smol_str::SmolStr, Dynamic> = BTreeMap::new();
8    m.insert("id".into(), Dynamic::I64(idx));
9    m.insert("name".into(), Dynamic::String(format!("user_{idx}").into()));
10    m.insert(
11        "email".into(),
12        Dynamic::String(format!("user{idx}@example.com").into()),
13    );
14    m.insert("active".into(), Dynamic::Bool(idx % 3 == 0));
15    m.insert("score".into(), Dynamic::F64((idx as f64) * 7.0 / 13.0));
16    let tags = vec![
17        Dynamic::String(format!("tag_{}", idx % 5).into()),
18        Dynamic::String(format!("tag_{}", idx % 7).into()),
19    ];
20    m.insert("tags".into(), Dynamic::list(tags));
21    if depth > 0 && idx % 10 == 0 {
22        let mut children = Vec::new();
23        for i in 0..3 {
24            children.push(gen_node(depth - 1, idx * 1000 + i));
25        }
26        m.insert("children".into(), Dynamic::list(children));
27    } else {
28        m.insert("note".into(), Dynamic::String("leaf node".into()));
29    }
30    Dynamic::map(m)
31}
32
33fn gen_dataset(n: i64) -> Dynamic {
34    let mut root: BTreeMap<smol_str::SmolStr, Dynamic> = BTreeMap::new();
35    root.insert("version".into(), Dynamic::String("1.0".into()));
36    root.insert(
37        "generated_at".into(),
38        Dynamic::String("2026-07-08T19:51:00Z".into()),
39    );
40    root.insert("count".into(), Dynamic::I64(n));
41    let mut users = Vec::new();
42    for i in 0..n {
43        users.push(gen_node(0, i));
44    }
45    root.insert("users".into(), Dynamic::list(users));
46    Dynamic::map(root)
47}
48
49fn main() {
50    for &size in &[1_000_i64, 10_000, 100_000] {
51        let t0 = Instant::now();
52        let data = gen_dataset(size);
53        let build_ms = t0.elapsed().as_millis();
54
55        let t1 = Instant::now();
56        let yaml = data.to_yaml_string();
57        let emit_ms = t1.elapsed().as_millis();
58        let yaml_len = yaml.len();
59
60        let t2 = Instant::now();
61        let back = match Dynamic::from_yaml_buf(yaml.as_bytes()) {
62            Ok(v) => v,
63            Err(e) => {
64                println!("parse error: {e}");
65                return;
66            }
67        };
68        let parse_ms = t2.elapsed().as_millis();
69
70        let back_count = back.get_dynamic("count").and_then(|v| v.as_int());
71        let back_users = back.get_dynamic("users");
72        let back_users_len = back_users.as_ref().map(|v| v.len()).unwrap_or(0);
73        let ok = back_count == Some(size) && back_users_len as i64 == size;
74
75        println!("---");
76        println!("size:        {}", size);
77        println!("build:       {} ms", build_ms);
78        println!(
79            "to_yaml:     {} ms ({} bytes, {:.1} bytes/node)",
80            emit_ms,
81            yaml_len,
82            yaml_len as f64 / size as f64
83        );
84        println!(
85            "from_yaml:   {} ms ({:.0} bytes/ms)",
86            parse_ms,
87            yaml_len as f64 / parse_ms.max(1) as f64
88        );
89        println!(
90            "round-trip:  {}",
91            if ok { "ok" } else { "FAIL" }
92        );
93    }
94}