#[allow(
dead_code,
unused_imports,
clippy::all,
mismatched_lifetime_syntaxes,
unsafe_op_in_unsafe_fn
)]
#[path = "logevent_generated.rs"]
mod logevent_generated;
#[allow(dead_code, unused, clippy::all)]
#[path = "logevent_capnp.rs"]
mod logevent_capnp;
#[rustfmt::skip]
#[path = "verit_gen.rs"]
mod verit_gen;
use std::hint::black_box;
use std::time::{Duration, Instant};
use capnp::message::ReaderOptions;
use capnp::serialize;
use logevent_capnp::log_event as cp;
use logevent_generated::bench as fb;
use prost::Message as ProstMessage;
use serde::{Deserialize, Serialize};
use verit::{encode, Budget, Message, Ref, Resolver, Schema, SchemaMode, Value};
use verit_gen as vg;
const SERVICE: &str = "checkout-service";
const MESSAGE: &str = "payment gateway timed out after 3 retries";
const LEVEL: u32 = 2;
const LATENCY: u32 = 2_500_000;
const N_POINTS: usize = 10;
const N_CODES: usize = 16;
#[derive(Clone, Copy)]
struct Shape {
name: &'static str,
n_points: usize,
n_codes: usize,
}
const SHAPES: [Shape; 3] = [
Shape {
name: "small (1 pt, 2 codes)",
n_points: 1,
n_codes: 2,
},
Shape {
name: "medium (10 pt, 16 codes)",
n_points: N_POINTS,
n_codes: N_CODES,
},
Shape {
name: "large (200 pt, 500 codes)",
n_points: 200,
n_codes: 500,
},
];
fn point_x(i: usize) -> f64 {
i as f64 * 1.5
}
fn point_y(i: usize) -> f64 {
i as f64 * -0.5
}
fn code(i: usize) -> u32 {
(i as u32) * 7 + 1
}
#[derive(Serialize, Deserialize)]
struct JPoint {
x: f64,
y: f64,
}
#[derive(Serialize, Deserialize)]
struct JEvent {
service: String,
level: u32,
message: String,
latency_us: u32,
points: Vec<JPoint>,
codes: Vec<u32>,
}
#[derive(Clone, PartialEq, prost::Message)]
struct PbPoint {
#[prost(double, tag = "1")]
x: f64,
#[prost(double, tag = "2")]
y: f64,
}
#[derive(Clone, PartialEq, prost::Message)]
struct PbEvent {
#[prost(string, tag = "1")]
service: String,
#[prost(uint32, tag = "2")]
level: u32,
#[prost(string, tag = "3")]
message: String,
#[prost(uint32, tag = "4")]
latency_us: u32,
#[prost(message, repeated, tag = "5")]
points: Vec<PbPoint>,
#[prost(uint32, repeated, tag = "6")]
codes: Vec<u32>,
}
fn veritate_schema() -> Schema {
vg::schema()
}
fn vg_points(n: usize) -> Vec<vg::PointArgs> {
(0..n)
.map(|i| vg::PointArgs {
x: point_x(i),
y: point_y(i),
})
.collect()
}
fn vg_scan(root: &vg::LogEventRef) -> u64 {
let mut acc = 0u64;
acc = acc.wrapping_add(root.service().unwrap().unwrap().len() as u64);
acc = acc.wrapping_add(root.level().unwrap().unwrap() as u64);
acc = acc.wrapping_add(root.message().unwrap().unwrap().len() as u64);
acc = acc.wrapping_add(root.latency_us().unwrap().unwrap() as u64);
let points = root.points().unwrap().unwrap();
let mut fsum = 0.0f64;
if let Some(blocks) = points.blocks() {
for p in blocks {
fsum += p.x() + p.y();
}
} else {
for p in points.iter() {
let p = p.unwrap();
fsum += p.x().unwrap().unwrap() + p.y().unwrap().unwrap();
}
}
acc = acc.wrapping_add(fsum as i64 as u64);
let codes = root.codes().unwrap().unwrap();
if let Some(values) = codes.values() {
for c in values {
acc = acc.wrapping_add(c as u64);
}
} else {
for c in codes.iter() {
acc = acc.wrapping_add(c.unwrap() as u64);
}
}
acc
}
fn veritate_value(np: usize, nc: usize) -> Value {
Value::Struct(vec![
(1, Value::str(SERVICE)),
(2, Value::U32(LEVEL)),
(3, Value::str(MESSAGE)),
(4, Value::U32(LATENCY)),
(
5,
Value::List(
(0..np)
.map(|i| {
Value::Struct(vec![
(1, Value::F64(point_x(i))),
(2, Value::F64(point_y(i))),
])
})
.collect(),
),
),
(
6,
Value::List((0..nc).map(|i| Value::U32(code(i))).collect()),
),
])
}
fn json_event(np: usize, nc: usize) -> JEvent {
JEvent {
service: SERVICE.into(),
level: LEVEL,
message: MESSAGE.into(),
latency_us: LATENCY,
points: (0..np)
.map(|i| JPoint {
x: point_x(i),
y: point_y(i),
})
.collect(),
codes: (0..nc).map(code).collect(),
}
}
fn pb_event(np: usize, nc: usize) -> PbEvent {
PbEvent {
service: SERVICE.into(),
level: LEVEL,
message: MESSAGE.into(),
latency_us: LATENCY,
points: (0..np)
.map(|i| PbPoint {
x: point_x(i),
y: point_y(i),
})
.collect(),
codes: (0..nc).map(code).collect(),
}
}
fn fb_encode(np: usize, nc: usize) -> Vec<u8> {
let mut b = flatbuffers::FlatBufferBuilder::new();
let service = b.create_string(SERVICE);
let message = b.create_string(MESSAGE);
let points: Vec<_> = (0..np)
.map(|i| {
fb::Point::create(
&mut b,
&fb::PointArgs {
x: point_x(i),
y: point_y(i),
},
)
})
.collect();
let points = b.create_vector(&points);
let codes: Vec<u32> = (0..nc).map(code).collect();
let codes = b.create_vector(&codes);
let ev = fb::LogEvent::create(
&mut b,
&fb::LogEventArgs {
service: Some(service),
level: LEVEL,
message: Some(message),
latency_us: LATENCY,
points: Some(points),
codes: Some(codes),
},
);
b.finish(ev, None);
b.finished_data().to_vec()
}
fn capnp_encode(np: usize, nc: usize) -> Vec<u8> {
let mut message = capnp::message::Builder::new_default();
{
let mut ev = message.init_root::<cp::Builder>();
ev.set_service(SERVICE);
ev.set_level(LEVEL);
ev.set_message(MESSAGE);
ev.set_latency_us(LATENCY);
{
let mut points = ev.reborrow().init_points(np as u32);
for i in 0..np {
let mut p = points.reborrow().get(i as u32);
p.set_x(point_x(i));
p.set_y(point_y(i));
}
}
{
let mut codes = ev.reborrow().init_codes(nc as u32);
for i in 0..nc {
codes.set(i as u32, code(i));
}
}
}
let mut buf = Vec::new();
serialize::write_message(&mut buf, &message).unwrap();
buf
}
fn capnp_scan(ev: cp::Reader) -> u64 {
let mut acc = 0u64;
acc = acc.wrapping_add(ev.get_service().unwrap().len() as u64);
acc = acc.wrapping_add(ev.get_level() as u64);
acc = acc.wrapping_add(ev.get_message().unwrap().len() as u64);
acc = acc.wrapping_add(ev.get_latency_us() as u64);
let mut fsum = 0.0f64;
let points = ev.get_points().unwrap();
for p in points.iter() {
fsum += p.get_x() + p.get_y();
}
acc = acc.wrapping_add(fsum as i64 as u64);
let codes = ev.get_codes().unwrap();
for c in codes.iter() {
acc = acc.wrapping_add(c as u64);
}
acc
}
fn veritate_scan(root: &verit::StructReader) -> u64 {
let mut acc = 0u64;
acc = acc.wrapping_add(root.get_str(1).unwrap().unwrap().len() as u64);
acc = acc.wrapping_add(root.get_u32(2).unwrap().unwrap() as u64);
acc = acc.wrapping_add(root.get_str(3).unwrap().unwrap().len() as u64);
acc = acc.wrapping_add(root.get_u32(4).unwrap().unwrap() as u64);
let points = root.get_list(5).unwrap().unwrap();
let mut fsum = 0.0f64;
for i in 0..points.len() {
if let Ref::Struct(p) = points.get(i).unwrap() {
fsum += p.get_f64(1).unwrap().unwrap() + p.get_f64(2).unwrap().unwrap();
}
}
acc = acc.wrapping_add(fsum as i64 as u64);
let codes = root.get_list(6).unwrap().unwrap();
for i in 0..codes.len() {
if let Ref::U32(c) = codes.get(i).unwrap() {
acc = acc.wrapping_add(c as u64);
}
}
acc
}
fn fb_scan(ev: &fb::LogEvent) -> u64 {
let mut acc = 0u64;
acc = acc.wrapping_add(ev.service().map(|s| s.len()).unwrap_or(0) as u64);
acc = acc.wrapping_add(ev.level() as u64);
acc = acc.wrapping_add(ev.message().map(|s| s.len()).unwrap_or(0) as u64);
acc = acc.wrapping_add(ev.latency_us() as u64);
let mut fsum = 0.0f64;
if let Some(points) = ev.points() {
for p in points.iter() {
fsum += p.x() + p.y();
}
}
acc = acc.wrapping_add(fsum as i64 as u64);
if let Some(codes) = ev.codes() {
for c in codes.iter() {
acc = acc.wrapping_add(c as u64);
}
}
acc
}
fn typed_scan(
service: &str,
level: u32,
message: &str,
latency: u32,
points_sum: f64,
codes_sum: u64,
) -> u64 {
(service.len() as u64)
.wrapping_add(level as u64)
.wrapping_add(message.len() as u64)
.wrapping_add(latency as u64)
.wrapping_add(points_sum as i64 as u64)
.wrapping_add(codes_sum)
}
fn bench(name: &str, mut f: impl FnMut() -> u64) -> f64 {
let mut n: u64 = 1;
let mut elapsed;
loop {
let t = Instant::now();
let mut acc = 0u64;
for _ in 0..n {
acc = acc.wrapping_add(f());
}
black_box(acc);
elapsed = t.elapsed();
if elapsed >= Duration::from_millis(200) || n >= 1 << 30 {
break;
}
n *= 2;
}
let mut best = elapsed.as_nanos() as f64 / n as f64;
for _ in 0..3 {
let t = Instant::now();
let mut acc = 0u64;
for _ in 0..n {
acc = acc.wrapping_add(f());
}
black_box(acc);
best = best.min(t.elapsed().as_nanos() as f64 / n as f64);
}
println!(" {name:<42} {best:>12.1} ns/op");
best
}
fn main() {
let schema = veritate_schema();
let resolver = Resolver::identity(&schema).unwrap();
let v_bytes = encode(
&schema,
&veritate_value(N_POINTS, N_CODES),
SchemaMode::HashOnly,
)
.unwrap();
let v_inline = encode(
&schema,
&veritate_value(N_POINTS, N_CODES),
SchemaMode::Inline,
)
.unwrap();
let j_bytes = serde_json::to_vec(&json_event(N_POINTS, N_CODES)).unwrap();
let p_bytes = pb_event(N_POINTS, N_CODES).encode_to_vec();
let f_bytes = fb_encode(N_POINTS, N_CODES);
let c_bytes = capnp_encode(N_POINTS, N_CODES);
println!("workload: 2 strings, 2 u32, {N_POINTS} point structs, {N_CODES} u32 codes\n");
println!("--- wire size (bytes) ---");
println!(" veritate (hash-only) {:>6}", v_bytes.len());
println!(
" veritate (inline schema, self-contained) {:>6}",
v_inline.len()
);
println!(" protobuf (prost) {:>6}", p_bytes.len());
println!(" capnp {:>6}", c_bytes.len());
println!(" flatbuffers {:>6}", f_bytes.len());
println!(" json {:>6}", j_bytes.len());
println!("\n--- encode (build + serialize, fresh allocations) ---");
bench("veritate (dynamic Value tree)", || {
encode(
&schema,
&veritate_value(N_POINTS, N_CODES),
SchemaMode::HashOnly,
)
.unwrap()
.len() as u64
});
let cg_points = vg_points(N_POINTS);
let cg_codes: Vec<u32> = (0..N_CODES).map(code).collect();
bench("veritate (codegen, direct)", || {
let args = vg::LogEventArgs {
service: Some(SERVICE),
level: Some(LEVEL),
message: Some(MESSAGE),
latency_us: Some(LATENCY),
points: Some(&cg_points),
codes: Some(&cg_codes),
};
vg::encode_log_event(&args, SchemaMode::HashOnly)
.unwrap()
.len() as u64
});
bench("protobuf (prost)", || {
pb_event(N_POINTS, N_CODES).encode_to_vec().len() as u64
});
bench("capnp", || capnp_encode(N_POINTS, N_CODES).len() as u64);
bench("flatbuffers", || fb_encode(N_POINTS, N_CODES).len() as u64);
bench("json (serde)", || {
serde_json::to_vec(&json_event(N_POINTS, N_CODES))
.unwrap()
.len() as u64
});
println!("\n--- partial access: bytes -> 2 fields (service.len + latency) ---");
bench("veritate (dynamic)", || {
let msg = Message::parse(&v_bytes).unwrap();
let root = msg.root(&resolver).unwrap();
root.get_str(1).unwrap().unwrap().len() as u64 + root.get_u32(4).unwrap().unwrap() as u64
});
bench("veritate (codegen identity fast path)", || {
let msg = Message::parse(&v_bytes).unwrap();
let root = vg::LogEventRef::read(&msg, &resolver).unwrap();
root.service().unwrap().unwrap().len() as u64 + root.latency_us().unwrap().unwrap() as u64
});
bench("veritate (codegen bounded, untrusted)", || {
let msg = Message::parse(&v_bytes).unwrap();
let budget = Budget::new(msg.suggested_budget());
let root = vg::LogEventRef::read_bounded(&msg, &resolver, &budget).unwrap();
root.service().unwrap().unwrap().len() as u64 + root.latency_us().unwrap().unwrap() as u64
});
bench("protobuf (prost, must decode all)", || {
let ev = PbEvent::decode(&p_bytes[..]).unwrap();
ev.service.len() as u64 + ev.latency_us as u64
});
bench("capnp (safe, bounds-checked)", || {
let reader =
serialize::read_message_from_flat_slice(&mut &c_bytes[..], ReaderOptions::new())
.unwrap();
let ev = reader.get_root::<cp::Reader>().unwrap();
ev.get_service().unwrap().len() as u64 + ev.get_latency_us() as u64
});
bench("flatbuffers (verified root)", || {
let ev = flatbuffers::root::<fb::LogEvent>(&f_bytes).unwrap();
ev.service().map(|s| s.len()).unwrap_or(0) as u64 + ev.latency_us() as u64
});
bench("flatbuffers (root_unchecked, unsafe)", || {
let ev = unsafe { flatbuffers::root_unchecked::<fb::LogEvent>(&f_bytes) };
ev.service().map(|s| s.len()).unwrap_or(0) as u64 + ev.latency_us() as u64
});
bench("json (serde, must decode all)", || {
let ev: JEvent = serde_json::from_slice(&j_bytes).unwrap();
ev.service.len() as u64 + ev.latency_us as u64
});
println!("\n--- full scan: bytes -> every field ---");
bench("veritate (dynamic)", || {
let msg = Message::parse(&v_bytes).unwrap();
let root = msg.root(&resolver).unwrap();
veritate_scan(&root)
});
bench("veritate (codegen identity fast path)", || {
let msg = Message::parse(&v_bytes).unwrap();
let root = vg::LogEventRef::read(&msg, &resolver).unwrap();
vg_scan(&root)
});
bench("veritate (codegen bounded, untrusted)", || {
let msg = Message::parse(&v_bytes).unwrap();
let budget = Budget::new(msg.suggested_budget());
let root = vg::LogEventRef::read_bounded(&msg, &resolver, &budget).unwrap();
vg_scan(&root)
});
bench("veritate (verify once + codegen scan)", || {
let msg = Message::parse(&v_bytes).unwrap();
msg.verify(&resolver, &Budget::new(msg.suggested_budget()))
.unwrap();
let root = vg::LogEventRef::read(&msg, &resolver).unwrap();
vg_scan(&root)
});
bench("protobuf (prost)", || {
let ev = PbEvent::decode(&p_bytes[..]).unwrap();
let fsum: f64 = ev.points.iter().map(|p| p.x + p.y).sum();
let csum: u64 = ev.codes.iter().map(|&c| c as u64).sum();
typed_scan(
&ev.service,
ev.level,
&ev.message,
ev.latency_us,
fsum,
csum,
)
});
bench("capnp (safe, bounds-checked)", || {
let reader =
serialize::read_message_from_flat_slice(&mut &c_bytes[..], ReaderOptions::new())
.unwrap();
capnp_scan(reader.get_root::<cp::Reader>().unwrap())
});
bench("flatbuffers (verified root)", || {
let ev = flatbuffers::root::<fb::LogEvent>(&f_bytes).unwrap();
fb_scan(&ev)
});
bench("flatbuffers (root_unchecked, unsafe)", || {
let ev = unsafe { flatbuffers::root_unchecked::<fb::LogEvent>(&f_bytes) };
fb_scan(&ev)
});
bench("json (serde)", || {
let ev: JEvent = serde_json::from_slice(&j_bytes).unwrap();
let fsum: f64 = ev.points.iter().map(|p| p.x + p.y).sum();
let csum: u64 = ev.codes.iter().map(|&c| c as u64).sum();
typed_scan(
&ev.service,
ev.level,
&ev.message,
ev.latency_us,
fsum,
csum,
)
});
let msg = Message::parse(&v_bytes).unwrap();
let root = msg.root(&resolver).unwrap();
let v = veritate_scan(&root);
let cg = {
let args = vg::LogEventArgs {
service: Some(SERVICE),
level: Some(LEVEL),
message: Some(MESSAGE),
latency_us: Some(LATENCY),
points: Some(&cg_points),
codes: Some(&cg_codes),
};
let bytes = vg::encode_log_event(&args, SchemaMode::HashOnly).unwrap();
let m = Message::parse(&bytes).unwrap();
let r = m.root(&resolver).unwrap();
veritate_scan(&r)
};
assert_eq!(v, cg, "codegen-encoded bytes must scan identically");
let ev = PbEvent::decode(&p_bytes[..]).unwrap();
let fsum: f64 = ev.points.iter().map(|p| p.x + p.y).sum();
let csum: u64 = ev.codes.iter().map(|&c| c as u64).sum();
let p = typed_scan(
&ev.service,
ev.level,
&ev.message,
ev.latency_us,
fsum,
csum,
);
let f = fb_scan(&flatbuffers::root::<fb::LogEvent>(&f_bytes).unwrap());
let c = {
let reader =
serialize::read_message_from_flat_slice(&mut &c_bytes[..], ReaderOptions::new())
.unwrap();
capnp_scan(reader.get_root::<cp::Reader>().unwrap())
};
let jev: JEvent = serde_json::from_slice(&j_bytes).unwrap();
let jf: f64 = jev.points.iter().map(|p| p.x + p.y).sum();
let jc: u64 = jev.codes.iter().map(|&c| c as u64).sum();
let j = typed_scan(
&jev.service,
jev.level,
&jev.message,
jev.latency_us,
jf,
jc,
);
assert!(
v == p && p == c && c == f && f == j,
"checksum mismatch: {v} {p} {c} {f} {j}"
);
println!("\nchecksums agree across all formats ({v})");
shapes_sweep(&schema, &resolver);
}
fn shapes_sweep(schema: &Schema, resolver: &Resolver) {
println!("\n=== message-shape sweep (safe formats) ===");
for s in SHAPES {
let (np, nc) = (s.n_points, s.n_codes);
let v_bytes = encode(schema, &veritate_value(np, nc), SchemaMode::HashOnly).unwrap();
let p_bytes = pb_event(np, nc).encode_to_vec();
let c_bytes = capnp_encode(np, nc);
let f_bytes = fb_encode(np, nc);
let j_bytes = serde_json::to_vec(&json_event(np, nc)).unwrap();
let cg_points = vg_points(np);
let cg_codes: Vec<u32> = (0..nc).map(code).collect();
println!("\n--- shape: {} ---", s.name);
println!(
" wire size (B): veritate {} protobuf {} capnp {} flatbuffers {} json {}",
v_bytes.len(),
p_bytes.len(),
c_bytes.len(),
f_bytes.len(),
j_bytes.len()
);
println!(" encode (ns/op):");
bench(" veritate (codegen)", || {
let args = vg::LogEventArgs {
service: Some(SERVICE),
level: Some(LEVEL),
message: Some(MESSAGE),
latency_us: Some(LATENCY),
points: Some(&cg_points),
codes: Some(&cg_codes),
};
vg::encode_log_event(&args, SchemaMode::HashOnly)
.unwrap()
.len() as u64
});
bench(" protobuf (prost)", || {
pb_event(np, nc).encode_to_vec().len() as u64
});
bench(" capnp", || capnp_encode(np, nc).len() as u64);
bench(" flatbuffers", || fb_encode(np, nc).len() as u64);
println!(" full scan (ns/op):");
bench(" veritate (codegen)", || {
let msg = Message::parse(&v_bytes).unwrap();
let root = vg::LogEventRef::read(&msg, resolver).unwrap();
vg_scan(&root)
});
bench(" protobuf (prost)", || {
let ev = PbEvent::decode(&p_bytes[..]).unwrap();
let fsum: f64 = ev.points.iter().map(|p| p.x + p.y).sum();
let csum: u64 = ev.codes.iter().map(|&c| c as u64).sum();
typed_scan(
&ev.service,
ev.level,
&ev.message,
ev.latency_us,
fsum,
csum,
)
});
bench(" capnp (safe)", || {
let reader =
serialize::read_message_from_flat_slice(&mut &c_bytes[..], ReaderOptions::new())
.unwrap();
capnp_scan(reader.get_root::<cp::Reader>().unwrap())
});
bench(" flatbuffers (verified)", || {
let ev = flatbuffers::root::<fb::LogEvent>(&f_bytes).unwrap();
fb_scan(&ev)
});
println!(" partial access, 2 of 6 fields (ns/op):");
bench(" veritate (codegen)", || {
let msg = Message::parse(&v_bytes).unwrap();
let root = vg::LogEventRef::read(&msg, resolver).unwrap();
root.service().unwrap().unwrap().len() as u64
+ root.latency_us().unwrap().unwrap() as u64
});
bench(" protobuf (prost)", || {
let ev = PbEvent::decode(&p_bytes[..]).unwrap();
ev.service.len() as u64 + ev.latency_us as u64
});
bench(" capnp (safe)", || {
let reader =
serialize::read_message_from_flat_slice(&mut &c_bytes[..], ReaderOptions::new())
.unwrap();
let ev = reader.get_root::<cp::Reader>().unwrap();
ev.get_service().unwrap().len() as u64 + ev.get_latency_us() as u64
});
bench(" flatbuffers (verified)", || {
let ev = flatbuffers::root::<fb::LogEvent>(&f_bytes).unwrap();
ev.service().map(|s| s.len()).unwrap_or(0) as u64 + ev.latency_us() as u64
});
}
}