Skip to content

Commit a147d0f

Browse files
committed
refactor(crates): Remove obsolete kernel crate and update dependencies
- Deleted crates/kernel/* (consolidated to root src/) - Updated Cargo.toml dependencies in cli, demo-generator, persistence - Fixed import paths in cli commands (diff.rs, replay_query.rs) - Added new bin/ folder in cli crate - Updated fixture paths in persistence crate
1 parent 0146485 commit a147d0f

19 files changed

Lines changed: 604 additions & 990 deletions

File tree

crates/cli/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@ path = "src/lib.rs"
1111
name = "valori"
1212
path = "src/main.rs"
1313

14+
[[bin]]
15+
name = "bench_ingest"
16+
path = "src/bin/bench_ingest.rs"
17+
1418
[dependencies]
1519
clap = { version = "4.5", features = ["derive"] }
1620
valori-persistence = { path = "../persistence" }
1721
anyhow = "1.0"
1822
comfy-table = "7.1"
1923
chrono = "0.4"
2024
crc64fast = "1.0"
21-
valori-kernel = { path = "../kernel" }
25+
valori-kernel = { path = "../.." }
2226
serde = { version = "1.0", features = ["derive"] }
2327
serde_json = "1.0"
28+
memmap2 = "0.9"
29+
bytemuck = "1.14"
2430

2531

2632
[dev-dependencies]
2733
tempfile = "3.24.0"
34+

crates/cli/src/bin/bench_1m.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use anyhow::Result;
2+
use memmap2::Mmap;
3+
use std::fs::File;
4+
use std::time::Instant;
5+
use valori_kernel::adapters::sift_batch::SiftBatchLoader;
6+
use bytemuck::cast_slice; // The "Senior" way to cast types
7+
8+
const Q16_SCALE: f32 = 65536.0;
9+
10+
fn main() -> Result<()> {
11+
println!("🚀 Starting SIFT1M Granular Benchmark...");
12+
13+
let path = "data/sift/sift/sift_base.fvecs";
14+
let file = File::open(path).expect("Failed to open SIFT file.");
15+
let mmap = unsafe { Mmap::map(&file)? };
16+
17+
// Initialize Loader
18+
let mut loader = SiftBatchLoader::new(&mmap)
19+
.ok_or_else(|| anyhow::anyhow!("Invalid SIFT format"))?;
20+
21+
let dim = loader.dim();
22+
let total = loader.len();
23+
let batch_size = 10_000;
24+
25+
// We calculate stride manually to inline the logic and avoid function overhead
26+
// Header (4B) + Data (dim * 4B)
27+
let stride = 4 + (dim * 4);
28+
29+
println!("📊 Dataset: {} Vectors | Dim: {}", total, dim);
30+
31+
// ==========================================================
32+
// TEST 1: RAW I/O (Baseline)
33+
// ==========================================================
34+
println!("\nTest 1: Raw Memory Bandwidth (No Parsing)...");
35+
loader = SiftBatchLoader::new(&mmap).unwrap(); // Reset cursor
36+
let start_io = Instant::now();
37+
let mut bytes_checksum: u64 = 0;
38+
39+
while let Some((raw_bytes, _count)) = loader.next_batch(batch_size) {
40+
// Force OS to page-in data by reading every byte.
41+
// We use a simple sum which the compiler can SIMD optimize,
42+
// ensuring we hit memory bandwidth limits, not CPU limits.
43+
let chunk_sum: u64 = raw_bytes.iter().map(|&b| b as u64).sum();
44+
bytes_checksum = bytes_checksum.wrapping_add(chunk_sum);
45+
}
46+
std::hint::black_box(bytes_checksum); // Ensure calculation isn't deleted
47+
48+
let time_io = start_io.elapsed();
49+
// approximate bytes read (total file size)
50+
let total_bytes = mmap.len();
51+
println!(" -> Time: {:.4}s | {:.2} GB/s",
52+
time_io.as_secs_f64(),
53+
(total_bytes as f64 / 1_024.0 / 1_024.0 / 1_024.0) / time_io.as_secs_f64()
54+
);
55+
56+
// ==========================================================
57+
// TEST 2: PARSING COST (Bytemuck Cast)
58+
// ==========================================================
59+
println!("\nTest 2: Structure Cost (Bytes -> &[f32])...");
60+
loader = SiftBatchLoader::new(&mmap).unwrap();
61+
let start_parse = Instant::now();
62+
let mut _check_parse: f32 = 0.0;
63+
64+
while let Some((raw_bytes, count)) = loader.next_batch(batch_size) {
65+
for i in 0..count {
66+
let offset = i * stride;
67+
// Zero-Copy Slice: Skip 4 byte header, take the rest
68+
// Note: f32 requires 4-byte alignment. SIFT stride is (4 + 128*4) = 516.
69+
// 516 is divisible by 4, so address alignment is preserved!
70+
let vec_bytes = &raw_bytes[offset + 4 .. offset + stride];
71+
72+
// bytemuck::cast_slice is SAFE. It checks alignment and length.
73+
// If this panics, your data is corrupt.
74+
let vec_f32: &[f32] = cast_slice(vec_bytes);
75+
76+
77+
// Sum ALL floats to ensure we read all memory, making this comparable to Test 1.
78+
for &val in vec_f32 {
79+
_check_parse += val;
80+
}
81+
}
82+
}
83+
84+
let time_parse = start_parse.elapsed();
85+
println!(" - Checksum (f32): {:.2} (Ignore)", _check_parse);
86+
println!(" -> Time: {:.4}s | Overhead: {:.4}s",
87+
time_parse.as_secs_f64(),
88+
time_parse.checked_sub(time_io).unwrap_or(std::time::Duration::ZERO).as_secs_f64()
89+
);
90+
91+
// ==========================================================
92+
// TEST 3: MATH COST (f32 -> Q16.16)
93+
// ==========================================================
94+
println!("\nTest 3: Determinism Cost (Math Ops)...");
95+
loader = SiftBatchLoader::new(&mmap).unwrap();
96+
let start_math = Instant::now();
97+
let mut check_math: i64 = 0;
98+
99+
while let Some((raw_bytes, count)) = loader.next_batch(batch_size) {
100+
for i in 0..count {
101+
let offset = i * stride;
102+
let vec_bytes = &raw_bytes[offset + 4 .. offset + stride];
103+
let vec_f32: &[f32] = cast_slice(vec_bytes);
104+
105+
// THE HOT LOOP
106+
for &val in vec_f32 {
107+
let fixed = (val * Q16_SCALE) as i32;
108+
check_math = check_math.wrapping_add(fixed as i64);
109+
}
110+
}
111+
}
112+
113+
let time_math = start_math.elapsed();
114+
115+
// Fix: Don't subtract Test 2 if Test 3 is faster (due to SIMD)
116+
// Just report the raw math time, which is the "Hot Cache" performance.
117+
println!(" -> Time: {:.4}s", time_math.as_secs_f64());
118+
119+
println!("--------------------------------------------------");
120+
println!("📉 COST ANALYSIS:");
121+
println!(" - Cold I/O (Disk): {:.4}s", time_io.as_secs_f64());
122+
println!(" - Hot Math (Memory): {:.4}s", time_math.as_secs_f64());
123+
println!("--------------------------------------------------");
124+
125+
let total_ops = total as f64 * dim as f64;
126+
println!("⚡ Hot Throughput: {:.2} Billion ops/sec",
127+
total_ops / time_math.as_secs_f64() / 1_000_000_000.0
128+
);
129+
130+
Ok(())
131+
}

crates/cli/src/bin/bench_filter.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use anyhow::Result;
2+
use valori_kernel::{ValoriKernel, types::FixedPointVector};
3+
4+
fn main() -> Result<()> {
5+
println!("🚀 Starting Metadata Filter Benchmark...");
6+
let mut kernel = ValoriKernel::new();
7+
8+
let dim = 128;
9+
10+
// 1. Ingest Data with Tags
11+
// Even IDs -> Tag 1 (Red)
12+
// Odd IDs -> Tag 2 (Blue)
13+
println!("📥 Ingesting 10,000 tagged vectors...");
14+
for i in 0..10_000u64 {
15+
let vec = vec![0; dim]; // Dummy vector
16+
let tag = if i % 2 == 0 { 1 } else { 2 };
17+
18+
// Use the new insert helper
19+
kernel.insert(i, vec, tag)?;
20+
}
21+
22+
// 2. Search with Filter (Tag 1)
23+
println!("🔎 Searching for Tag 1 (Evens)...");
24+
let query = vec![0; dim];
25+
// None = No filter, Some(1) = Filter for Tag 1
26+
let results = kernel.search(&query, 10, Some(1))?;
27+
28+
// 3. Verify
29+
println!("📊 Got {} results", results.len());
30+
for (id, _) in results {
31+
if id % 2 != 0 {
32+
panic!("❌ FAILED: Found Odd ID {} inside Tag 1 results!", id);
33+
}
34+
}
35+
36+
println!("✅ SUCCESS: Filter strictly enforced. All results have Tag 1.");
37+
38+
// 4. Search with Filter (Tag 2)
39+
println!("🔎 Searching for Tag 2 (Odds)...");
40+
let results2 = kernel.search(&query, 10, Some(2))?;
41+
for (id, _) in results2 {
42+
if id % 2 == 0 {
43+
panic!("❌ FAILED: Found Even ID {} inside Tag 2 results!", id);
44+
}
45+
}
46+
println!("✅ SUCCESS: Filter strictly enforced for Tag 2.");
47+
48+
println!("✅ Architecture supports Hybrid Search (Vector + Metadata).");
49+
Ok(())
50+
}

crates/cli/src/bin/bench_ingest.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use anyhow::Result;
2+
use memmap2::Mmap;
3+
use std::fs::File;
4+
use std::time::Instant;
5+
use valori_kernel::ValoriKernel; // The Engine
6+
use valori_kernel::adapters::sift_batch::SiftBatchLoader;
7+
use bytemuck::cast_slice;
8+
9+
// Standard SIFT1M is 128 dims
10+
const DIM: usize = 128;
11+
const Q16_SCALE: f32 = 65536.0;
12+
13+
fn main() -> Result<()> {
14+
println!("🚀 Starting Kernel Ingestion Benchmark (End-to-End)...");
15+
16+
// 1. Setup Data
17+
let path = "data/sift/sift/sift_base.fvecs";
18+
let file = File::open(path).expect("Failed to open SIFT file");
19+
let mmap = unsafe { Mmap::map(&file)? };
20+
let mut loader = SiftBatchLoader::new(&mmap)
21+
.ok_or_else(|| anyhow::anyhow!("Invalid SIFT format"))?;
22+
23+
println!("📊 Dataset: {} Vectors", loader.len());
24+
25+
// 2. Initialize Kernel
26+
// This is the "System Under Test"
27+
let mut kernel = ValoriKernel::new();
28+
println!("🤖 Kernel Initialized. Ready for Ingest.");
29+
30+
// 3. Setup Reusable Buffer (Zero-Alloc Loop)
31+
// Payload: [CMD(1)] + [ID(8)] + [DIM(2)] + [VALUES(128*4)]
32+
// Based on `crates/kernel/src/types.rs`
33+
let packet_size = 1 + 8 + 2 + (DIM * 4);
34+
let mut packet_buffer = vec![0u8; packet_size];
35+
36+
// Constant Header Fields
37+
// Offset 0: CMD_INSERT = 1
38+
// Buffer: CMD(1) + ID(8) + DIM(2) + VEC(DIM*4) + TAG(8)
39+
let buffer_size = 1 + 8 + 2 + (DIM * 4) + 8;
40+
let mut packet_buffer = vec![0u8; buffer_size];
41+
packet_buffer[0] = 1; // CMD_INSERT
42+
packet_buffer[9..11].copy_from_slice(&(DIM as u16).to_le_bytes());
43+
44+
let ingest_limit = loader.len(); // Ingest all available vectors
45+
let mut id_counter = 0;
46+
47+
println!("🏁 Ingestion Started...");
48+
let start = Instant::now();
49+
50+
while let Some((raw_bytes, count)) = loader.next_batch(1000) {
51+
let stride = 4 + (DIM * 4);
52+
for i in 0..count {
53+
if id_counter >= ingest_limit { break; }
54+
let offset = i * stride; // fvecs format
55+
let vec_f32: &[f32] = cast_slice(&raw_bytes[offset+4 .. offset+stride]);
56+
57+
// Construct Payload
58+
// ID
59+
let id = id_counter as u64;
60+
packet_buffer[1..9].copy_from_slice(&id.to_le_bytes());
61+
62+
// Vector
63+
let vec_start = 11;
64+
let vec_end = 11 + (DIM * 4);
65+
let payload_vec = &mut packet_buffer[vec_start..vec_end];
66+
for (j, &val) in vec_f32.iter().enumerate() {
67+
let fixed = (val * Q16_SCALE) as i32;
68+
payload_vec[j*4..(j+1)*4].copy_from_slice(&fixed.to_le_bytes());
69+
}
70+
71+
// Tag (Default 0)
72+
packet_buffer[vec_end..vec_end+8].copy_from_slice(&0u64.to_le_bytes());
73+
74+
// The Critical Call (Apply to State)
75+
// This tests the Kernel's locking, hashing, and storage logic.
76+
kernel.apply_event(&packet_buffer)?;
77+
id_counter += 1;
78+
79+
if id_counter % 5000 == 0 {
80+
// simple println to avoid cursor jump issues in automation
81+
println!("Ingesting: {} ...", id_counter);
82+
}
83+
}
84+
}
85+
println!();
86+
87+
let duration = start.elapsed();
88+
let seconds = duration.as_secs_f64();
89+
let eps = id_counter as f64 / seconds;
90+
91+
println!("--------------------------------------------------");
92+
println!("✅ INGESTION COMPLETE.");
93+
println!(" - Events: {}", id_counter);
94+
println!(" - Time: {:.4} seconds", seconds);
95+
println!(" - Throughput: {:.2} EPS (Events Per Second)", eps);
96+
println!("--------------------------------------------------");
97+
98+
// Check if we hit the target
99+
if eps > 10_000.0 {
100+
println!("🚀 SUCCESS: Speed > 10k EPS");
101+
} else {
102+
println!("⚠️ WARNING: Speed < 10k EPS. Optimization needed.");
103+
}
104+
105+
Ok(())
106+
}

0 commit comments

Comments
 (0)