Files
Oxicloud/examples/bench_round23_micro.rs
Claude 1ec7030cc7 perf: round 23 — Postgres query-shape pass: typed JSONB decode, drive-policy borrow-deserialize, user-profile join!, subject-group CTE reuse, dedup unzip
Benchmark-gated, same rule as ROUND2-22: BEFORE/AFTER with a value-equivalence
gate and rollback-on-regression. Two harnesses — bench_round23_micro (no
Postgres; deterministic allocation gate) and bench_round23_queries (live
Postgres; p50 latency + strict equivalence gate against seeded fixtures). See
benches/ROUND23.md.

- J1: contact_pg_repository::row_to_contact (+ the inlined contact_group sibling)
  decode the 3 JSONB columns via sqlx::types::Json<T> (one from_slice pass)
  instead of row.get::<serde_json::Value> + from_value (a throwaway Value DOM
  per column, walked a second time). Per contact row of every list / multiget /
  CardDAV sync. Micro 84 -> 33 allocs/op (2.15x); PG 3794 -> 2360 ns/contact
  (1.61x) on 500 real rows.
- J2: DrivePolicies::from_value deserializes straight from the borrow
  (T::deserialize(&Value)) instead of from_value(value.clone()) — dropping the
  full-DOM clone on every drive-policy read (move/copy, share, grant); one-line
  body change, all 7 callers unchanged. Micro 5 -> 0 allocs/op (11.51x).
- P1: get_user_profile overlaps the two independent caller+target reads with
  tokio::join! (self-case still a single fetch; caller-error precedence
  preserved via caller_res? first) instead of two serial round-trips. PG
  577 -> 312 us/call (1.85x).
- G1: subject_group remove_member computes the child's transitive-user recursive
  CTE once and reuses it for both the would-empty pre-check and the cache
  invalidation, instead of running the identical CTE twice (the edge delete is
  above the child, so its descendants can't change). PG 829 -> 412 us/removal
  (2.01x).
- U1: dedup_service (store_loose_chunks final registration + the ingest
  run_rollback) reshapes the owned, dead-after Vec<(String,i64)> via
  into_iter().unzip() instead of cloning every 64-byte hash for the
  sync_blobs(&[String]) + UNNEST bind. Micro 256 -> 0 hash clones.

Verified: cargo clippy --features bench --all-targets -D warnings clean, cargo
fmt --all --check clean, cargo test --lib --features bench = 529 passed / 0
failed. The PG benches run against a local PostgreSQL 16 (schema applied from
migrations/); every equivalence gate passes.

The download_zip per-item N+1 (the audit's highest raw-latency candidate) is
deferred to a dedicated pass: its fix moves the sole authorization inside the
stream call, so it needs an AuthZ-ordering + anti-enumeration proof, not a perf
banner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DKyQ4AnYtgp1JtjzweyMeo
2026-07-20 15:25:42 +00:00

364 lines
13 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Round-23 CPU/alloc micro-pack (no Postgres) — the deterministic alloc gates
//! for the decode / clone candidates. The end-to-end PostgreSQL latency +
//! equivalence evidence lives in `bench_round23_queries.rs`.
//!
//! Same rule as ROUND2–22: each section is BEFORE (verbatim replica of the
//! shipped-before shape) vs AFTER (verbatim replica of the shipped-after shape,
//! which the source is then made to match), with a byte/-value equivalence gate
//! and a `GATE FAIL … rollback` check that `std::process::exit(1)`s if the AFTER
//! arm fails to beat its BEFORE.
//!
//! [J1] `contact_pg_repository::row_to_contact` (+ the `contact_group`
//! sibling) decoded each JSONB column with `row.get::<serde_json::Value>`
//! + `serde_json::from_value::<Vec<Dto>>` — a throwaway `Value` DOM per
//! column, walked a second time. AFTER decodes straight into the typed
//! Vec via `sqlx::types::Json<T>` (one `from_slice` pass). Modeled here
//! as `from_slice::<Value>` + `from_value` vs `from_slice::<Vec<Dto>>`.
//!
//! [J2] `DrivePolicies::from_value` did `serde_json::from_value(value.clone())`
//! — cloning the ENTIRE policies DOM per drive-policy read. AFTER
//! deserializes from the borrow (`T::deserialize(&Value)`), no clone.
//!
//! [U1] `dedup_service` (`store_loose_chunks` final registration + the ingest
//! `run_rollback`) built `Vec<String>`/`Vec<i64>` by CLONING every hash
//! out of an owned, dead-after `Vec<(String,i64)>` purely to reshape for
//! `sync_blobs(&[String])` + the UNNEST bind. AFTER moves via
//! `into_iter().unzip()`.
//!
//! Run:
//! cargo run --release --features bench --example bench_round23_micro
//! Tunables (env): BENCH_ITERS (200000), J1_ROWS (3), U1_CHUNKS (256)
use std::alloc::{GlobalAlloc, Layout, System};
use std::env;
use std::hint::black_box;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
use serde::{Deserialize, Serialize};
use serde_json::Value;
static ALLOC_CALLS: AtomicU64 = AtomicU64::new(0);
struct CountingAlloc;
unsafe impl GlobalAlloc for CountingAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
ALLOC_CALLS.fetch_add(1, Ordering::Relaxed);
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
ALLOC_CALLS.fetch_add(1, Ordering::Relaxed);
unsafe { System.realloc(ptr, layout, new_size) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
ALLOC_CALLS.fetch_add(1, Ordering::Relaxed);
unsafe { System.alloc_zeroed(layout) }
}
}
#[global_allocator]
static GLOBAL: CountingAlloc = CountingAlloc;
fn env_or<T: std::str::FromStr>(key: &str, default: T) -> T {
env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
struct Measured {
wall_ns_per_op: f64,
allocs_per_op: f64,
}
fn measure<F: FnMut()>(iters: usize, mut f: F) -> Measured {
for _ in 0..(iters / 20).max(1) {
f();
}
let a0 = ALLOC_CALLS.load(Ordering::Relaxed);
let t = Instant::now();
for _ in 0..iters {
f();
}
let wall = t.elapsed().as_nanos() as f64 / iters as f64;
let allocs = (ALLOC_CALLS.load(Ordering::Relaxed) - a0) as f64 / iters as f64;
Measured {
wall_ns_per_op: wall,
allocs_per_op: allocs,
}
}
fn print_row(label: &str, m: &Measured) {
println!(
"| {:<52} | {:>12.1} | {:>10.2} |",
label, m.wall_ns_per_op, m.allocs_per_op
);
}
fn header_footer(name: &str, before: &Measured, after: &Measured) {
println!("| arm | ns/op | allocs/op |");
print_row(&format!("BEFORE {name}"), before);
print_row(&format!("AFTER {name}"), after);
println!(
"# {:.2}x wall, {:.2} fewer allocs/op",
before.wall_ns_per_op / after.wall_ns_per_op,
before.allocs_per_op - after.allocs_per_op
);
}
fn gate_allocs(tag: &str, before: &Measured, after: &Measured) {
if after.allocs_per_op >= before.allocs_per_op {
eprintln!("GATE FAIL [{tag}]: AFTER did not reduce allocations — rollback");
std::process::exit(1);
}
}
// ────────────────────────────────────────────────────────────────────────────
// [J1] Contact JSONB decode — Value DOM + from_value vs Json<T> from_slice.
// Verbatim replicas of the persistence DTOs (contact_persistence_dto.rs).
// ────────────────────────────────────────────────────────────────────────────
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct EmailDto {
email: String,
r#type: String,
is_primary: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct PhoneDto {
number: String,
r#type: String,
is_primary: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct AddressDto {
street: Option<String>,
city: Option<String>,
state: Option<String>,
postal_code: Option<String>,
country: Option<String>,
r#type: String,
is_primary: bool,
}
/// BEFORE: `row.get::<Value>` (sqlx JSONB→Value DOM) then `from_value::<Vec<T>>`
/// (a second walk of the DOM). Modeled with `from_slice::<Value>` (what sqlx's
/// Value decoder does) + `from_value`.
fn j1_before(
email: &[u8],
phone: &[u8],
addr: &[u8],
) -> (Vec<EmailDto>, Vec<PhoneDto>, Vec<AddressDto>) {
let ev: Value = serde_json::from_slice(email).unwrap();
let pv: Value = serde_json::from_slice(phone).unwrap();
let av: Value = serde_json::from_slice(addr).unwrap();
let emails = serde_json::from_value::<Vec<EmailDto>>(ev).unwrap_or_default();
let phones = serde_json::from_value::<Vec<PhoneDto>>(pv).unwrap_or_default();
let addrs = serde_json::from_value::<Vec<AddressDto>>(av).unwrap_or_default();
(emails, phones, addrs)
}
/// AFTER: `sqlx::types::Json<Vec<T>>` decodes the JSONB bytes straight into the
/// typed Vec (one `from_slice::<Vec<T>>`), no intermediate DOM.
fn j1_after(
email: &[u8],
phone: &[u8],
addr: &[u8],
) -> (Vec<EmailDto>, Vec<PhoneDto>, Vec<AddressDto>) {
let emails = serde_json::from_slice::<Vec<EmailDto>>(email).unwrap_or_default();
let phones = serde_json::from_slice::<Vec<PhoneDto>>(phone).unwrap_or_default();
let addrs = serde_json::from_slice::<Vec<AddressDto>>(addr).unwrap_or_default();
(emails, phones, addrs)
}
fn section_j1() {
let iters: usize = env_or("BENCH_ITERS", 200_000);
let n: usize = env_or("J1_ROWS", 3); // entries per column, realistic contact
let mk_emails = |n: usize| -> Vec<EmailDto> {
(0..n)
.map(|i| EmailDto {
email: format!("user{i}@example.com"),
r#type: if i == 0 { "home" } else { "work" }.to_string(),
is_primary: i == 0,
})
.collect()
};
let mk_phones = |n: usize| -> Vec<PhoneDto> {
(0..n)
.map(|i| PhoneDto {
number: format!("+1-555-010{i}"),
r#type: "cell".to_string(),
is_primary: i == 0,
})
.collect()
};
let mk_addrs = |n: usize| -> Vec<AddressDto> {
(0..n)
.map(|i| AddressDto {
street: Some(format!("{} Main St", 100 + i)),
city: Some("Springfield".to_string()),
state: Some("IL".to_string()),
postal_code: Some("62704".to_string()),
country: Some("US".to_string()),
r#type: "home".to_string(),
is_primary: i == 0,
})
.collect()
};
let email_b = serde_json::to_vec(&mk_emails(n)).unwrap();
let phone_b = serde_json::to_vec(&mk_phones(n)).unwrap();
let addr_b = serde_json::to_vec(&mk_addrs(n)).unwrap();
// Equivalence: identical decoded Vecs.
assert_eq!(
j1_before(&email_b, &phone_b, &addr_b),
j1_after(&email_b, &phone_b, &addr_b),
"J1 decoded contacts differ"
);
let before = measure(iters, || {
black_box(j1_before(
black_box(&email_b),
black_box(&phone_b),
black_box(&addr_b),
));
});
let after = measure(iters, || {
black_box(j1_after(
black_box(&email_b),
black_box(&phone_b),
black_box(&addr_b),
));
});
println!(
"\n## [J1] Contact JSONB decode ({n} entries/col — per contact row of every list/multiget/sync)"
);
header_footer(
"Value DOM + from_value vs Json<T> from_slice",
&before,
&after,
);
gate_allocs("J1", &before, &after);
}
// ────────────────────────────────────────────────────────────────────────────
// [J2] Drive policies decode — from_value(value.clone()) vs deserialize(&value).
// ────────────────────────────────────────────────────────────────────────────
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(default)]
struct Policies {
forbid_public_links: bool,
read_only: bool,
}
/// BEFORE: clone the whole `Value` DOM, then `from_value`.
fn j2_before(value: &Value) -> Policies {
serde_json::from_value(value.clone()).unwrap_or_default()
}
/// AFTER: deserialize straight from the borrow — no DOM clone.
fn j2_after(value: &Value) -> Policies {
Policies::deserialize(value).unwrap_or_default()
}
fn section_j2() {
let iters: usize = env_or("BENCH_ITERS", 200_000);
// A realistic on-disk policies bag with an unknown key preserved on disk
// (the lenient contract) so the DOM isn't trivially tiny.
let value: Value = serde_json::from_str(
r#"{"forbid_public_links":true,"read_only":false,"x_future_flag":"kept-on-disk"}"#,
)
.unwrap();
assert_eq!(
j2_before(&value),
j2_after(&value),
"J2 decoded policies differ"
);
assert!(j2_after(&value).forbid_public_links);
let before = measure(iters, || {
black_box(j2_before(black_box(&value)));
});
let after = measure(iters, || {
black_box(j2_after(black_box(&value)));
});
println!("\n## [J2] Drive policies decode (per move/copy/share/grant drive-policy read)");
header_footer(
"from_value(value.clone()) vs deserialize(&value)",
&before,
&after,
);
gate_allocs("J2", &before, &after);
}
// ────────────────────────────────────────────────────────────────────────────
// [U1] dedup hash reshape — clone-collect vs into_iter().unzip().
// ────────────────────────────────────────────────────────────────────────────
fn u1_build(n: usize) -> Vec<(String, i64)> {
(0..n)
.map(|i| {
(
format!("{:064x}", i as u128 * 0x9E37_79B9_7F4A_7C15),
i as i64,
)
})
.collect()
}
/// BEFORE: clone every hash out of the owned (dead-after) Vec to reshape.
fn u1_before(rows: Vec<(String, i64)>) -> (Vec<String>, Vec<i64>) {
let hashes: Vec<String> = rows.iter().map(|(h, _)| h.clone()).collect();
let sizes: Vec<i64> = rows.iter().map(|(_, s)| *s).collect();
(hashes, sizes)
}
/// AFTER: move via unzip — no per-hash content copy.
fn u1_after(rows: Vec<(String, i64)>) -> (Vec<String>, Vec<i64>) {
rows.into_iter().unzip()
}
fn section_u1() {
let n: usize = env_or("U1_CHUNKS", 256);
let iters: usize = env_or("BENCH_ITERS", 200_000) / 20; // heavier op
// Equivalence: identical hashes + sizes.
assert_eq!(
u1_before(u1_build(n)),
u1_after(u1_build(n)),
"U1 reshape differs"
);
let before = measure(iters, || {
black_box(u1_before(black_box(u1_build(n))));
});
let after = measure(iters, || {
black_box(u1_after(black_box(u1_build(n))));
});
println!(
"\n## [U1] dedup hash reshape ({n} distinct new chunks — per delta-upload registration)"
);
header_footer("clone-collect vs into_iter().unzip()", &before, &after);
gate_allocs("U1", &before, &after);
}
fn main() {
println!("# Round-23 micro-pack — BEFORE/AFTER (counting allocator, release)");
println!("# allocs/op is the deterministic gate; a non-winning AFTER exits 1 (rollback).");
section_j1();
section_j2();
section_u1();
println!("\nAll Round-23 micro sections passed their allocation gate.");
}