use super::Json; use std::{hint::black_box, time::Instant}; #[test] #[ignore = "explicit release performance probe"] fn document_string_cost() { use crate::wire::document::{self, Document, Value}; type Decode = fn(&str) -> Document; let operations: [(&str, Decode); 2] = [ ("dever", |source| document::parse(source).unwrap()), // 独立 Serde 解码保留相同的单节点结果和字节预算。 ("rust", |source| { assert!(source.len() <= document::MAX_BYTES); Document { root: 0, nodes: vec![Value::String(serde_json::from_str(source).unwrap())], } }), ]; println!("\ncase,sample,implementation,iterations,elapsed_ns,checksum"); for (name, text) in [ ("short", "hello".to_owned()), ("ascii", "a".repeat(8192)), ("unicode", "雪🙂".repeat(1024)), ("escaped", "雪\"\\\n".repeat(1024)), ] { let source = serde_json::to_string(&text).unwrap(); let expected = Document { root: 0, nodes: vec![Value::String(text.clone())], }; for (_, operation) in operations { assert_eq!(operation(&source), expected); for _ in 0..100 { black_box(operation(&source)); } } let iterations = if name == "short" { 10000 } else { 1000 }; for sample in 0..15 { for offset in 0..operations.len() { let (implementation, operation) = operations[(sample + offset) % operations.len()]; let started = Instant::now(); let mut checksum = 0; for _ in 0..iterations { let parsed = black_box(operation(black_box(&source))); let Value::String(value) = &parsed.nodes[parsed.root] else { panic!("expected string node"); }; checksum += value.len(); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, text.len() * iterations); println!( "document_string_{name},{sample},{implementation},{iterations},{elapsed},{checksum}" ); } } } } #[test] #[ignore = "explicit release performance probe"] fn encoded_fragment_cost() { type Encode = fn(&str, bool) -> String; println!("\ncase,sample,implementation,iterations,elapsed_ns,checksum"); for bytes in [64, 4096, 65536] { let text = "a".repeat(bytes); for nested in [false, true] { let operations: [(&str, Encode); 4] = [ ("proven", |text, nested| { let value = Json::encode_proven(&(text, 7i64), 3, 1).unwrap(); if nested { Json::array(&[value]).unwrap().into_text() } else { value.into_text() } }), ("shaped", |text, nested| { let value = Json::encode(&(text, 7i64)).unwrap(); if nested { Json::array(&[value]).unwrap().into_text() } else { value.into_text() } }), // 一次写完用于观察融合输出的成本,不能当作中间 Json 的包装开销。 ("rust_direct", |text, nested| { assert!(text.len() <= (crate::wire::document::MAX_BYTES - 32) / 6); if nested { serde_json::to_string(&[(text, 7i64)]).unwrap() } else { serde_json::to_string(&(text, 7i64)).unwrap() } }), // 与 Dever 一样先持有编码结果,再用标准库写入外层数组。 ("rust_materialized", |text, nested| { assert!(text.len() <= (crate::wire::document::MAX_BYTES - 32) / 6); let value = serde_json::to_string(&(text, 7i64)).unwrap(); if nested { let mut output = String::with_capacity(value.len() + 2); output.push('['); output.push_str(&value); output.push(']'); output } else { value } }), ]; let expected = operations[2].1(&text, nested); for (_, operation) in operations { assert_eq!(operation(&text, nested), expected); for _ in 0..100 { black_box(operation(&text, nested)); } } let iterations = if bytes == 64 { 10_000 } else { 1000 }; for sample in 0..15 { for offset in 0..operations.len() { let (name, operation) = operations[(sample + offset) % operations.len()]; let started = Instant::now(); let mut checksum = 0; for _ in 0..iterations { checksum += black_box(operation(black_box(&text), nested)).len(); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, expected.len() * iterations); println!( "encoded_{bytes}_{nested},{sample},{name},{iterations},{elapsed},{checksum}" ); } } } } } #[test] #[ignore = "explicit release performance probe"] fn command_json_decode_cost() { use crate::wire; #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct Input { source: Json, } #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct Native { source: Box, } type Decoder = fn(&str) -> usize; let decoders: [(&str, Decoder); 3] = [ ("before", |source| { wire::decode::(source).unwrap().source.as_str().len() }), ("after", |source| { wire::decode_json(source, |value: &Input, budget| { budget.open()?; budget.value(1)?; budget.json(&value.source)?; budget.close(); Ok(()) }) .unwrap() .source .as_str() .len() }), // 原生 RawValue 不做去重和结构预算,只提供语法解码成本下界。 ("rust_syntax_only", |source| { serde_json::from_str::(source) .unwrap() .source .get() .len() }), ]; println!("\ncase,sample,implementation,iterations,elapsed_ns,checksum"); for (name, text) in [ ("short", "a".repeat(64)), ("plain_65536", "a".repeat(65536)), ("escaped", "雪\"\\\n".repeat(1024)), ] { let payload = serde_json::to_string(&text).unwrap(); let source = format!("{{\"source\":{payload}}}"); let iterations = if name == "short" { 10_000 } else { 1000 }; for (_, decode) in decoders { for _ in 0..100 { assert_eq!(black_box(decode(&source)), payload.len()); } } for sample in 0..15 { for offset in 0..decoders.len() { let (label, decode) = decoders[(sample + offset) % decoders.len()]; let started = Instant::now(); let mut checksum = 0; for _ in 0..iterations { checksum += black_box(decode(black_box(&source))); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, payload.len() * iterations); println!("command_json_{name},{sample},{label},{iterations},{elapsed},{checksum}"); } } } } #[test] #[ignore = "explicit release performance probe"] fn parse_object_keys_cost() { println!("\ncase,sample,iterations,elapsed_ns,checksum"); for (name, suffix) in [ ("short", String::new()), ("long", "field".repeat(32)), ("escaped", "雪\"\\\n".repeat(16)), ] { let fields = (0..256) .map(|index| (format!("key_{index:03}_{suffix}"), index)) .collect::>(); let source = serde_json::to_string(&fields).unwrap(); assert_eq!(Json::parse(&source).unwrap().as_str(), source); for _ in 0..100 { black_box(Json::parse(&source).unwrap()); } for sample in 0..15 { let started = Instant::now(); let mut checksum = 0; for _ in 0..1000 { let parsed = Json::parse(black_box(&source)).unwrap(); checksum += black_box(parsed.as_str().len()); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, source.len() * 1000); println!("json_keys_{name},{sample},1000,{elapsed},{checksum}"); } } } #[test] #[ignore = "explicit release performance probe"] fn parse_text_json_cost() { println!("\ncase,sample,iterations,elapsed_ns,checksum"); for (name, text) in [ ("ascii", "a".repeat(8192)), ("unicode", "雪🙂".repeat(1024)), ("escaped", "雪\"\\\n".repeat(1024)), ] { let source = serde_json::to_string(&text).unwrap(); assert_eq!(Json::parse(&source).unwrap().as_text().unwrap(), text); for _ in 0..100 { black_box(Json::parse(&source).unwrap()); } for sample in 0..15 { let started = Instant::now(); let mut checksum = 0; for _ in 0..1000 { let parsed = Json::parse(black_box(&source)).unwrap(); checksum += black_box(parsed.as_str().len()); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, source.len() * 1000); println!("json_parse_{name},{sample},1000,{elapsed},{checksum}"); } } } #[test] #[ignore = "explicit release performance probe"] fn deserialize_raw_json_cost() { println!("\ncase,sample,iterations,elapsed_ns,checksum"); let values = (0..256).collect::>(); let text = serde_json::to_string(&"雪\"\\".repeat(512)).unwrap(); let array = format!(r#"{{"numbers":{values:?},"huge":1e9999}}"#); let nested = "[".repeat(32) + &array + &"]".repeat(32); for (name, source) in [("text", text), ("array", array), ("nested", nested)] { let parsed: Json = serde_json::from_str(&source).unwrap(); assert_eq!(parsed.as_str(), source); for _ in 0..100 { black_box(serde_json::from_str::(&source).unwrap()); } for sample in 0..15 { let started = Instant::now(); let mut checksum = 0; for _ in 0..1000 { let parsed: Json = serde_json::from_str(black_box(&source)).unwrap(); checksum += black_box(parsed.as_str().len()); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, source.len() * 1000); println!("json_deserialize_{name},{sample},1000,{elapsed},{checksum}"); } } } #[test] #[ignore = "explicit release performance probe"] fn property_lookup_cost() { println!("\ncase,sample,iterations,elapsed_ns,checksum"); for (name, count, suffix) in [ ("small", 4, ""), ("wide", 256, ""), ("escaped", 256, "雪\\\""), ] { let names = (0..count) .map(|index| format!("key_{index:03}_{suffix}")) .collect::>(); let fields = names .iter() .map(|key| format!("{}:1e9999", serde_json::to_string(key).unwrap())) .collect::>(); let source = format!("{{{}}}", fields.join(",")); let value = Json::parse(&source).unwrap(); let keys = [ names[0].as_str(), &names[count / 2], &names[count - 1], "missing", ]; let lookup = || { keys.iter() .map(|key| { value .property(black_box(key)) .unwrap() .map_or(0, |child| black_box(child.as_str().len())) }) .sum::() }; assert_eq!(lookup(), 18); for _ in 0..100 { black_box(lookup()); } for sample in 0..15 { let started = Instant::now(); let mut checksum = 0; for _ in 0..1000 { checksum += black_box(lookup()); } let elapsed = started.elapsed().as_nanos(); assert_eq!(checksum, 18000); println!("json_property_{name},{sample},1000,{elapsed},{checksum}"); } assert_eq!(value.as_str(), source); } }