Files
core/test/rust/performance/json.rs
T
2026-11-03 03:32:07 +00:00

183 lines
8.9 KiB
Rust

// 两侧共享相同的 wire 库和预算,隔离 Dever 生成代码开销,不代替独立 JSON 引擎对比。
use dever_runtime::wire::{json::Json, serde::{Serialize, Serializer, ser::SerializeStruct}};
fn json_fault(_: impl std::fmt::Display) -> Fault {
&FaultInfo { message: "JSON failure", source: "json-reference", line: 1, column: 1 }
}
struct Payload<'value> { values: &'value [i64] }
impl Serialize for Payload<'_> {
fn serialize<Encoder: Serializer>(&self, encoder: Encoder) -> Result<Encoder::Ok, Encoder::Error> {
let mut record = encoder.serialize_struct("Payload", 1)?;
record.serialize_field("values", self.values)?;
record.end()
}
}
fn generated_encode(values: &[i64]) -> Result<String, Fault> {
GENERATED_ENCODE(values).map_err(json_fault)
}
fn reference_encode(values: &[i64]) -> Result<String, Fault> {
Json::encode(&Payload { values }).map(|value| value.as_str().to_owned()).map_err(json_fault)
}
// 独立 serde 输出不执行 Dever 的三个预算,只作为额外编码成本的下界。
fn reference_serde_encode(values: &[i64]) -> Result<String, Fault> {
dever_runtime::wire::serde_json::to_string(&Payload { values }).map_err(json_fault)
}
// 独立 Rust 的同合同实现:两层容器,根和数组占两个值,i64 最长 20 字节。
// 值数合格时,含分隔符的最大输出不足 16 MiB,无须逐片段再次检查。
fn reference_bounded_encode(values: &[i64]) -> Result<String, Fault> {
if values.len() > dever_runtime::wire::document::MAX_VALUES - 2 {
return Err(json_fault("JSON value budget"));
}
reference_serde_encode(values)
}
fn generated_lookup(values: &[String]) -> Result<i64, Fault> {
values.first().map_or(Ok(-1), |value| GENERATED_LOOKUP(value).map_err(json_fault))
}
fn reference_lookup(values: &[String]) -> Result<i64, Fault> {
let Some(source) = values.first() else { return Ok(-1); };
let json = Json::parse(source).map_err(json_fault)?;
json.property("count").map_err(json_fault)?.map_or(Ok(-1), |value| value.as_integer().map_err(json_fault))
}
fn generated_update(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| GENERATED_UPDATE(value).map_err(json_fault))
}
fn reference_update(values: &[String]) -> Result<String, Fault> {
let Some(source) = values.first() else { return Ok(String::new()); };
let value = Json::parse(source).map_err(json_fault)?;
let child = Json::integer(43).map_err(json_fault)?;
value.set_property("count", &child).map(|value| value.as_str().to_owned()).map_err(json_fault)
}
fn generated_scalar_text(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| GENERATED_SCALAR_TEXT(value).map_err(json_fault))
}
fn reference_scalar_text(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| Json::string(value).map(|value| value.as_str().to_owned()).map_err(json_fault))
}
fn generated_scalar_float(values: &[f64]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| GENERATED_SCALAR_FLOAT(*value).map_err(json_fault))
}
fn reference_scalar_float(values: &[f64]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| Json::float(*value).map(|value| value.as_str().to_owned()).map_err(json_fault))
}
#[derive(Serialize)]
#[serde(crate = "dever_runtime::wire::serde")]
struct Fixed<'value> {
text: &'value str,
amount: i64,
ratio: f64,
}
fn reference_fixed_value(text: &str, ratio: f64) -> Result<String, Fault> {
if !ratio.is_finite() { return Err(json_fault("non-finite Float")); }
Json::encode(&Fixed { text, amount: 7, ratio })
.map(Json::into_text).map_err(json_fault)
}
fn generated_fixed(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| GENERATED_FIXED_RECORD(value, 4.25).map_err(json_fault))
}
fn reference_fixed(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| reference_fixed_value(value, 4.25))
}
fn main() {
println!("workload,sample,implementation,nanos,checksum");
for count in [0, 8, 256] {
let input = [Json::parse(&serde_json::to_string(&vec!["雪山 "; count]).unwrap()).unwrap()];
compare(&format!("json_typed_decode_{count}"), &input, 2000, generated_decode, reference_decode, |values| values.iter().map(|value| value.len() as u64).sum());
compare(&format!("json_captured_decode_{count}"), &input, 2000, generated_captured_decode, reference_captured_decode, |value| *value as u64);
}
let invalid = [Json::parse("[1]").unwrap()];
compare("json_captured_decode_invalid", &invalid, 2000, generated_captured_decode, reference_captured_decode, |value| *value as u64);
for state in ["Ready", "Busy"] {
let input = [Json::parse(&format!("\"{state}\"")).unwrap()];
compare(&format!("json_enum_decode_{state}"), &input, 100_000, generated_enum_decode, reference_enum_decode, |value| u64::from(*value));
}
property_costs();
raw_output_costs();
let values = (0..256).collect::<Vec<i64>>();
compare("json_record", &values, 2000, generated_encode, reference_encode, |value| value.len() as u64);
for count in [16, 256, 4096] {
let input = (0..count).collect::<Vec<i64>>();
compare_named(&format!("json_record_serde_{count}"), &input, 2000, generated_encode, reference_serde_encode, |value| value.len() as u64, "rust_serde");
compare_named(&format!("json_record_bounded_{count}"), &input, 2000, generated_encode, reference_bounded_encode, |value| value.len() as u64, "rust_bounded");
}
let maximum = vec![0; dever_runtime::wire::document::MAX_VALUES - 2];
assert_eq!(generated_encode(&maximum).unwrap(), reference_bounded_encode(&maximum).unwrap());
let excessive = vec![0; dever_runtime::wire::document::MAX_VALUES - 1];
assert!(generated_encode(&excessive).is_err());
assert!(reference_bounded_encode(&excessive).is_err());
let source = format!(r#"{{"count":42,"ratio":1e9999,"text":"雪","values":{values:?}}}"#);
compare("json_lookup", std::slice::from_ref(&source), 2000, generated_lookup, reference_lookup, |value| *value as u64);
compare("json_update", &[source], 2000, generated_update, reference_update, |value| value.len() as u64);
compare("json_scalar_text", &["雪\"\\".repeat(512)], 2000, generated_scalar_text, reference_scalar_text, |value| value.len() as u64);
compare("json_scalar_float", &[12345.6789], 2000, generated_scalar_float, reference_scalar_float, |value| value.len() as u64);
for bytes in [64, 4096, 65536] {
let input = ["a".repeat(bytes)];
assert_eq!(generated_fixed(&input).unwrap(), reference_fixed(&input).unwrap());
compare(&format!("json_fixed_record_{bytes}"), &input, 2000, generated_fixed, reference_fixed, |value| value.len() as u64);
}
for ratio in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
assert!(GENERATED_FIXED_RECORD("", ratio).is_err());
assert!(reference_fixed_value("", ratio).is_err());
}
let overhead = reference_fixed_value("", 4.25).unwrap().len();
let maximum = "a".repeat(dever_runtime::wire::document::MAX_BYTES - overhead);
assert_eq!(GENERATED_FIXED_RECORD(&maximum, 4.25).unwrap().len(), dever_runtime::wire::document::MAX_BYTES);
assert!(GENERATED_FIXED_RECORD(&(maximum + "a"), 4.25).is_err());
}
fn generated_decode(values: &[Json]) -> Result<Vec<String>, Fault> {
values.first().map_or(Ok(Vec::new()), |value| GENERATED_DECODE(value).map_err(json_fault))
}
fn reference_decode(values: &[Json]) -> Result<Vec<String>, Fault> {
// Dever 保留解码失败文本;两侧构造错误后再统一映射为基准 Fault。
values.first().map_or(Ok(Vec::new()), |value| serde_json::from_str(value.as_str()).map_err(|error| json_fault(error.to_string())))
}
fn generated_captured_decode(values: &[Json]) -> Result<i64, Fault> {
values.first().map_or(Ok(0), |value| GENERATED_CAPTURED_DECODE(value).map_err(json_fault))
}
fn reference_captured_decode(values: &[Json]) -> Result<i64, Fault> {
values.first().map_or(Ok(0), |value| {
Ok(match serde_json::from_str::<Vec<String>>(value.as_str()) {
Ok(values) => values.len() as i64,
Err(error) => { let _message = error.to_string(); -1 }
})
})
}
#[derive(dever_runtime::wire::serde::Deserialize)]
#[serde(crate = "dever_runtime::wire::serde")]
enum WireState { Ready, Busy }
fn generated_enum_decode(values: &[Json]) -> Result<bool, Fault> {
values.first().map_or(Ok(false), |value| GENERATED_ENUM_DECODE(value).map_err(json_fault))
}
fn reference_enum_decode(values: &[Json]) -> Result<bool, Fault> {
values.first().map_or(Ok(false), |value| {
serde_json::from_str::<WireState>(value.as_str())
.map(|state| matches!(state, WireState::Ready))
.map_err(|error| json_fault(error.to_string()))
})
}