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::config::Config;
use smith::message::{Message, Role};
use smith::structured::StructuredData;
use smith_alloc::{Counting, Scope, measure};
use smith_core::content_codec::{Blocks, EntryContentView};
use smith_core::frame::Frame;
use smith_core::session::{EntryContent, EntryFrame, Session};
use smith_core::store::{SessionReader, SessionWriter, open_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 selected_context_allocation_is_independent_of_old_and_unrelated_payloads() {
fn cost(old_bytes: usize, tag: &str) -> (u64, u64) {
let path =
std::env::temp_dir().join(format!("smith-effective-{tag}-{}.smh", std::process::id()));
let _ = std::fs::remove_file(&path);
let cfg = Config::default_valid();
let mut opened = open_session(&path, cfg.clone()).unwrap();
let old = EntryFrame::new(
None,
&EntryContent::Message(Message::with_text(Role::User, "x".repeat(old_bytes))),
)
.unwrap();
let compact = EntryFrame::new(
Some(old.id),
&EntryContent::Meta {
kind: smith_core::session::COMPACTION_KIND.into(),
detail: StructuredData::from_serializable(&serde_json::json!({"summary": ""}))
.unwrap(),
},
)
.unwrap();
let after = EntryFrame::new(
Some(compact.id),
&EntryContent::Message(Message::with_text(Role::User, "after")),
)
.unwrap();
let unrelated = EntryFrame::new(
Some(old.id),
&EntryContent::Message(Message::with_text(Role::User, "y".repeat(old_bytes))),
)
.unwrap();
for entry in [&old, &compact, &after, &unrelated] {
opened
.writer
.append_frame(&Frame::Known {
entry: entry.clone(),
})
.unwrap();
}
opened.writer.select_entry(after.id).unwrap();
drop(opened);
let reader = SessionReader::open(&path).unwrap();
let (selected, cost) = measure(Scope::Session, || {
reader.selected_session(cfg.frame_bytes_max).unwrap()
});
assert_eq!(selected.active_branch().entries().len(), 2);
drop(reader);
let _ = std::fs::remove_file(path);
(cost.allocations, cost.bytes)
}
let _lock = MEASURING.lock().unwrap();
let small = cost(100, "small");
let large = cost(400_000, "large");
assert_eq!(
small, large,
"pre-compaction and sibling payloads must not be retained or decoded"
);
}
#[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)));
}
#[cfg(target_arch = "x86_64")]
#[test]
fn decoded_content_view_fits_one_cache_line_and_blocks_three_words() {
assert_eq!(std::mem::size_of::<EntryContentView<'_>>(), 56);
assert_eq!(std::mem::size_of::<Blocks<'_>>(), 24);
}
#[test]
fn text_only_content_decode_and_scan_allocate_nothing() {
let content = EntryFrame::new(
None,
&EntryContent::Message(Message::with_text(Role::User, "needle")),
)
.unwrap()
.content;
let (cost, found) = session_cost(|| {
let view = EntryContentView::decode(&content).unwrap();
view.contains("needle")
});
assert!(found);
assert_eq!(cost.0, 0, "text inspection allocated {} times", cost.0);
}
#[test]
fn nested_json_inspection_borrows_without_allocating() {
let content = EntryFrame::new(
None,
&EntryContent::Meta {
kind: "event".into(),
detail: StructuredData::from_serializable(
&serde_json::json!({"outer": [{"value": "needle"}, 0, false, null]}),
)
.unwrap(),
},
)
.unwrap()
.content;
let (cost, found) = session_cost(|| {
let view = EntryContentView::decode(&content).unwrap();
view.contains("needle")
});
assert!(found);
assert_eq!(cost.0, 0, "JSON inspection allocated {} times", cost.0);
}
#[test]
fn unrelated_history_does_not_become_resident_selected_context() {
// SMH-SPEC-SPEC0001: Sessions, context memory ≠ complete file size.
let path = std::env::temp_dir().join(format!("smith-paged-memory-{}.smh", std::process::id()));
let _ = std::fs::remove_file(&path);
let cfg = Config::default_valid();
let mut writer = SessionWriter::open(&path, cfg.clone()).unwrap();
writer
.append_frame(&Frame::Header {
header: Session::new().header(),
})
.unwrap();
let root = EntryFrame::new(
None,
&EntryContent::Message(Message::with_text(Role::User, "root")),
)
.unwrap();
writer
.append_frame(&Frame::Known {
entry: root.clone(),
})
.unwrap();
for _ in 0..128 {
let sibling = EntryFrame::new(
Some(root.id),
&EntryContent::Message(Message::with_text(Role::User, "x".repeat(32 * 1024))),
)
.unwrap();
writer
.append_frame(&Frame::Known { entry: sibling })
.unwrap();
}
writer.select_entry(root.id).unwrap();
let reader = SessionReader::open(&path).unwrap();
let _serial = MEASURING
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let (selected, cost) = measure(Scope::IoRequest, || {
reader.selected_session(cfg.frame_bytes_max).unwrap()
});
assert_eq!(selected.selected_id(), Some(root.id));
assert_eq!(selected.active_branch().entries().len(), 1);
assert!(
cost.peak < 256 * 1024,
"selected context peak {} bytes for {}-byte file",
cost.peak,
reader.file_size()
);
assert!(reader.file_size() > 4 * 1024 * 1024);
drop(reader);
drop(writer);
let _ = std::fs::remove_file(path);
}
#[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
);
}