repositories / smith
smith
There are many coding harnesses - but this one is fast
owned by admin
smith-core/tests/alloc_bounds.rs
Raw//! Allocation-count bounds on session hot paths (`SMH-SPEC-SPEC0001`,
//! memory accounting). Bounds only tighten.
use smith::message::{Message, Role};
use smith_alloc::{Counting, Scope, measure};
use smith_core::frame::Frame;
use smith_core::session::{EntryContent, EntryFrame, Session};
use std::sync::Mutex;
#[global_allocator]
static ALLOC: Counting = Counting;
/// Scope counters are process-wide; measurements must not overlap.
static MEASURING: Mutex<()> = Mutex::new(());
/// `fork_at` over 1000 entries: the branch list, the record `Vec`, and one
/// arena slice. Content bytes are copied, never decoded.
const FORK_AT_1000_ALLOCATIONS: u64 = 3;
/// Encoding a header frame: one buffer sized by the payload hint holds the
/// prefix and the whole payload.
const HEADER_ENCODE_ALLOCATIONS: u64 = 1;
/// `from_frames` over 1000 decoded frames: arena, stored records, the
/// parent-link table, the branch list, and one branch's records, each sized
/// up front. Independent of entry count.
const FROM_FRAMES_1000_ALLOCATIONS: u64 = 5;
fn linear_session(len: usize) -> Session {
let mut session = Session::new();
for turn in 0..len {
let content = EntryContent::Message(Message::with_text(Role::User, format!("turn {turn}")));
session.append(&content).unwrap();
}
session
}
/// Allocations and bytes charged to [`Scope::Session`] while `run` executes.
fn session_cost<T>(run: impl FnOnce() -> T) -> ((u64, u64), T) {
let _serial = MEASURING
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let (out, cost) = measure(Scope::Session, run);
((cost.allocations, cost.bytes), out)
}
/// Cost of one `fork_at` of the leaf.
fn fork_cost(session: &Session) -> (u64, u64) {
let leaf = session.selected_id().expect("non-empty session");
let (cost, fork) = session_cost(|| session.fork_at(leaf).expect("leaf is known"));
assert_eq!(
fork.active_branch().entries().len(),
session.active_branch().entries().len()
);
let last = fork.active_branch().entries()[fork.active_branch().entries().len() - 1];
assert_eq!(fork.bytes(&last), session.bytes(&last));
cost
}
/// Cost of restoring `session` from its decoded frames; frame decoding
/// itself happens before the measured scope.
fn from_frames_cost(session: &Session) -> (u64, u64) {
let mut frames = vec![Frame::Header {
header: session.header(),
}];
frames.extend(
session
.active_branch()
.entries()
.iter()
.map(|entry| Frame::Known {
entry: EntryFrame {
id: entry.id,
parent: entry.parent,
timestamp_ms: entry.timestamp_ms,
kind: entry.kind,
content: session.bytes(entry).to_vec(),
},
}),
);
let (cost, restored) = session_cost(|| Session::from_frames(&frames).expect("frames restore"));
assert_eq!(&restored, session);
cost
}
#[test]
fn fork_at_over_1000_entries_is_bounded_and_reproducible() {
let session = linear_session(1000);
let first = fork_cost(&session);
let second = fork_cost(&session);
assert_eq!(
first.0, FORK_AT_1000_ALLOCATIONS,
"fork_at allocated {} times, bound is {FORK_AT_1000_ALLOCATIONS}",
first.0
);
assert_eq!(first, second);
}
#[test]
fn from_frames_allocates_independently_of_entry_count() {
let small = from_frames_cost(&linear_session(100));
let large = from_frames_cost(&linear_session(1000));
assert_eq!(
large.0, FROM_FRAMES_1000_ALLOCATIONS,
"from_frames allocated {} times, bound is {FROM_FRAMES_1000_ALLOCATIONS}",
large.0
);
assert_eq!(small.0, large.0);
assert_eq!(large, from_frames_cost(&linear_session(1000)));
}
#[test]
fn header_frame_encodes_into_one_allocation() {
let header = Frame::Header {
header: Session::new().header(),
};
let (cost, encoded) =
session_cost(|| header.encode(smith_core::frame::DEFAULT_MAX_FRAME_BYTES));
assert!(encoded.is_ok());
assert_eq!(
cost.0, HEADER_ENCODE_ALLOCATIONS,
"header encode allocated {} times, bound is {HEADER_ENCODE_ALLOCATIONS}",
cost.0
);
}