use std::fs;
use std::time::Instant;
const ITERS: u32 = 2000;
fn count_leaves(data: &tson::TsonData) -> usize {
match data {
tson::TsonData::Array(_, _, items) => items.iter().map(count_leaves).sum(),
tson::TsonData::Object(_, fields) => 1 + fields.iter().map(count_leaves).sum::<usize>(),
_ => 0,
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let file = if args.len() > 1 {
args[1].clone()
} else {
"examples/users-t1.json".to_string()
};
let json_text = fs::read_to_string(&file).expect("read file");
let json_size = json_text.len();
let fname = std::path::Path::new(&file)
.file_name()
.unwrap()
.to_string_lossy();
for _ in 0..50 {
let _ = tson::compile_json(&json_text).unwrap();
}
let mut _check = 0usize;
let mut total = 0f64;
let start = Instant::now();
for _ in 0..ITERS {
let v: serde_json::Value = serde_json::from_str(&json_text).unwrap();
_check += v.as_array().map(|a| a.len()).unwrap_or(1);
}
let json_parse_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
total += json_parse_ns;
let start = Instant::now();
let mut doc = None;
for i in 0..ITERS {
let d = tson::compile_json(&json_text).unwrap();
if i == 0 {
doc = Some(d);
}
}
let compile_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
total += compile_ns;
let doc = doc.unwrap();
let start = Instant::now();
let mut bytes = None;
for i in 0..ITERS {
let b = tson::to_bytes(&doc).unwrap();
if i == 0 {
bytes = Some(b);
}
}
let encode_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
total += encode_ns;
let bytes = bytes.unwrap();
let start = Instant::now();
let mut decoded = None;
for i in 0..ITERS {
let d = tson::from_bytes(&bytes).unwrap();
if i == 0 {
decoded = Some(d);
}
}
let decode_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
total += decode_ns;
let decoded = decoded.unwrap();
let start = Instant::now();
for i in 0..ITERS {
let v = tson::decompile_to_value(&decoded).unwrap();
if i == 0 {
_check += v.as_array().map(|a| a.len()).unwrap_or(1);
}
}
let decompile_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
total += decompile_ns;
let start = Instant::now();
for _ in 0..ITERS {
let mut reader = tson::TsonStreamReader::new(&bytes).unwrap();
let _h = reader.header();
let _d = reader.definitions();
let _dict = reader.dict();
for e in &mut reader {
let _ = e.unwrap();
}
}
let stream_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
total += stream_ns;
let start = Instant::now();
for _ in 0..ITERS {
let d = tson::compile_json(&json_text).unwrap();
let b = tson::to_bytes(&d).unwrap();
let back = tson::from_bytes(&b).unwrap();
let _v = tson::decompile_to_value(&back).unwrap();
}
let roundtrip_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
let entry_count: usize = doc
.data
.iter()
.map(|c| count_leaves(&c.data))
.sum::<usize>()
.max(1);
let def_count = doc.definitions.len();
let dict_count = doc.dict.len();
let tson_size = bytes.len();
println!();
println!("╔═══════════════════════════════════════════════════════╗");
println!("║ TSON Detailed Performance Comparison ║");
println!("╠═══════════════════════════════════════════════════════╣");
println!("║ File: {:<42} ║", fname);
println!(
"║ JSON: {} B TSON: {} B Ratio: {:.1}% ║",
json_size,
tson_size,
tson_size as f64 / json_size as f64 * 100.0
);
println!(
"║ Defs: {} Dict: {} Entries (leaves): {} ║",
def_count, dict_count, entry_count
);
println!(
"║ Iterations: {} ║",
ITERS
);
println!("╠══════════════════════╤══════════════╤══════════════════╣");
println!("║ Operation │ avg / iter│ % of full RT ║");
println!("╠══════════════════════╪══════════════╪══════════════════╣");
println!(
"║ serde_json parse │ {:>9.1} ns │ {:>13.1}% ║",
json_parse_ns,
json_parse_ns / total * 100.0
);
println!(
"║ TSON compile │ {:>9.1} ns │ {:>13.1}% ║",
compile_ns,
compile_ns / total * 100.0
);
println!(
"║ TSON encode │ {:>9.1} ns │ {:>13.1}% ║",
encode_ns,
encode_ns / total * 100.0
);
println!(
"║ TSON decode │ {:>9.1} ns │ {:>13.1}% ║",
decode_ns,
decode_ns / total * 100.0
);
println!(
"║ TSON decompile │ {:>9.1} ns │ {:>13.1}% ║",
decompile_ns,
decompile_ns / total * 100.0
);
println!(
"║ TSON stream (full) │ {:>9.1} ns │ {:>13.1}% ║",
stream_ns,
stream_ns / total * 100.0
);
println!("╟──────────────────────┼──────────────┼──────────────────╢");
println!(
"║ Full round-trip │ {:>9.1} ns │ {:>13} ║",
roundtrip_ns, roundtrip_ns
);
println!("╚══════════════════════╧══════════════════════════════════╝");
println!();
println!(" -- Observations --");
println!(
" * JSON parse alone dominates ({:.0}% of per-op budget)",
json_parse_ns / total * 100.0
);
println!(
" * TSON overhead = compile ({:.0}%) + encode/decode ({:.0}%+{:.0}%)",
compile_ns / total * 100.0,
encode_ns / total * 100.0,
decode_ns / total * 100.0
);
println!(" * Streaming reader loads defs+dict once, then O(1) per entry");
println!(
" * TSON binary is {:.1}% the size of the original JSON",
tson_size as f64 / json_size as f64 * 100.0
);
if dict_count > 0 {
println!(
" * Dict has {} entries - string interning saved repeated strings",
dict_count
);
}
println!();
println!(" -- Field Access Performance --");
let start = Instant::now();
for _ in 0..ITERS {
for entry in doc.entries() {
let _ = entry.data.values();
}
}
let vals_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
println!(" * data.values(): {:.1} ns", vals_ns);
let start = Instant::now();
for _ in 0..ITERS {
let _ = doc.first_entry().map(|e| e.data.len());
}
let len_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
println!(" * first_entry().data.len(): {:.1} ns", len_ns);
if let Some(_name_val) = doc.get("name") {
let start = Instant::now();
for _ in 0..ITERS {
let _ = doc.get("name");
}
let get_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
println!(" * doc.get(\"name\"): {:.1} ns", get_ns);
if let Some(addr) = doc.get("address") {
let start = Instant::now();
for _ in 0..ITERS {
let _ = addr.field("city", &doc.definitions);
}
let field_ns = start.elapsed().as_nanos() as f64 / ITERS as f64;
println!(" * data.field(\"city\", defs): {:.1} ns", field_ns);
}
}
}