use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct BlockKey {
pub tensor_id: u128,
pub block_index: u32,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[repr(u8)]
pub enum Tier {
Tier0 = 0,
Tier1 = 1,
Tier2 = 2,
Tier3 = 3,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum DType {
F32 = 0,
F16 = 1,
BF16 = 2,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum ReconstructPolicy {
None = 0,
Delta = 1,
Factor = 2,
}
#[derive(Clone, Debug)]
pub struct BlockMeta {
pub key: BlockKey,
pub dtype: DType,
pub tier: Tier,
pub bits: u8,
pub scale: f32,
pub zero_point: i16,
pub created_at: u64,
pub last_access_at: u64,
pub access_count: u32,
pub ema_rate: f32,
pub window: u64,
pub checksum: u32,
pub reconstruct: ReconstructPolicy,
pub tier_age: u32,
pub lineage_parent: Option<u128>,
pub block_bytes: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StoreError {
TensorEvicted,
BlockNotFound,
ChecksumMismatch,
IOError,
BudgetExhausted,
InvalidBlock,
DeltaChainTooLong,
ReconstructionFailed,
InvalidData,
ChainFull,
}
pub trait Clock {
fn now_ticks(&self) -> u64;
}
pub trait BlockIO {
fn read_block(&self, tier: Tier, key: BlockKey, dst: &mut [u8]) -> Result<usize, StoreError>;
fn write_block(&mut self, tier: Tier, key: BlockKey, src: &[u8]) -> Result<(), StoreError>;
fn delete_block(&mut self, tier: Tier, key: BlockKey) -> Result<(), StoreError>;
}
pub trait MetaLog {
fn append(&mut self, rec: &BlockMeta) -> Result<(), StoreError>;
fn get(&self, key: BlockKey) -> Option<&BlockMeta>;
fn iter(&self) -> Box<dyn Iterator<Item = &BlockMeta> + '_>;
}
pub fn crc32(data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFF_FFFF;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ 0xEDB8_8320;
} else {
crc >>= 1;
}
}
}
!crc
}
fn bits_for_tier(tier: Tier) -> u8 {
match tier {
Tier::Tier0 => 0,
Tier::Tier1 => 8,
Tier::Tier2 => 7,
Tier::Tier3 => 3,
}
}
#[inline]
fn qmax(bits: u8) -> i32 {
if bits == 0 || bits > 8 {
return 0;
}
(1i32 << (bits - 1)) - 1
}
struct BlockData {
element_count: u32,
packed: Vec<u8>,
}
fn quantize_block(data: &[f32], bits: u8) -> (Vec<u8>, f32) {
let qm = qmax(bits);
if qm == 0 || data.is_empty() {
return (Vec::new(), 0.0);
}
let qm_f = qm as f32;
let max_abs = data
.iter()
.filter(|v| v.is_finite())
.fold(0.0f32, |acc, v| acc.max(v.abs()));
let scale = if max_abs == 0.0 { 0.0 } else { max_abs / qm_f };
let inv_scale = if scale == 0.0 { 0.0 } else { 1.0 / scale };
let bits_u32 = bits as u32;
let needed = (data.len() * bits as usize).div_ceil(8);
let mut packed = Vec::with_capacity(needed);
let mut acc: u64 = 0;
let mut acc_bits: u32 = 0;
for &v in data {
let q = if v.is_finite() {
(v * inv_scale).round() as i32
} else {
0
}
.clamp(-qm, qm);
let u = (q + qm) as u32;
acc |= (u as u64) << acc_bits;
acc_bits += bits_u32;
while acc_bits >= 8 {
packed.push((acc & 0xFF) as u8);
acc >>= 8;
acc_bits -= 8;
}
}
if acc_bits > 0 {
packed.push((acc & 0xFF) as u8);
}
(packed, scale)
}
fn dequantize_block(packed: &[u8], scale: f32, bits: u8, count: usize, out: &mut [f32]) -> usize {
let qm = qmax(bits);
if qm == 0 || packed.is_empty() {
return 0;
}
let bits_u32 = bits as u32;
let mask = (1u64 << bits_u32) - 1;
let limit = count.min(out.len());
let mut acc: u64 = 0;
let mut acc_bits: u32 = 0;
let mut byte_idx: usize = 0;
let mut written: usize = 0;
while written < limit {
while acc_bits < bits_u32 && byte_idx < packed.len() {
acc |= (packed[byte_idx] as u64) << acc_bits;
acc_bits += 8;
byte_idx += 1;
}
if acc_bits < bits_u32 {
break;
}
let u = (acc & mask) as i32;
acc >>= bits_u32;
acc_bits -= bits_u32;
out[written] = (u - qm) as f32 * scale;
written += 1;
}
written
}
fn block_checksum(packed: &[u8], scale: f32) -> u32 {
let scale_bytes = scale.to_le_bytes();
let total = packed.len() + scale_bytes.len();
let mut buf = Vec::with_capacity(total);
buf.extend_from_slice(packed);
buf.extend_from_slice(&scale_bytes);
crc32(&buf)
}
#[derive(Debug, Default)]
pub struct TickResult {
pub upgrades: u32,
pub downgrades: u32,
pub evictions: u32,
pub bytes_freed: usize,
pub ops_used: u32,
pub candidates_found: u32,
}
fn to_tiering_tier(tier: Tier) -> crate::tiering::Tier {
match tier {
Tier::Tier0 => crate::tiering::Tier::Tier0,
Tier::Tier1 => crate::tiering::Tier::Tier1,
Tier::Tier2 => crate::tiering::Tier::Tier2,
Tier::Tier3 => crate::tiering::Tier::Tier3,
}
}
fn from_tiering_tier(tier: crate::tiering::Tier) -> Tier {
match tier {
crate::tiering::Tier::Tier0 => Tier::Tier0,
crate::tiering::Tier::Tier1 => Tier::Tier1,
crate::tiering::Tier::Tier2 => Tier::Tier2,
crate::tiering::Tier::Tier3 => Tier::Tier3,
}
}
fn to_tiering_meta(meta: &BlockMeta, now: u64) -> crate::tiering::BlockMeta {
crate::tiering::BlockMeta {
ema_rate: meta.ema_rate,
access_window: meta.window,
last_access: meta.last_access_at,
access_count: meta.access_count as u64,
current_tier: to_tiering_tier(meta.tier),
tier_since: now.saturating_sub(meta.tier_age as u64),
}
}
pub struct TieredStore {
block_bytes: usize,
index: HashMap<BlockKey, BlockMeta>,
tier1_data: HashMap<BlockKey, BlockData>,
tier2_data: HashMap<BlockKey, BlockData>,
tier3_data: HashMap<BlockKey, BlockData>,
tier1_keys: Vec<BlockKey>,
tier2_keys: Vec<BlockKey>,
tier3_keys: Vec<BlockKey>,
witness_log: crate::metrics::WitnessLog,
coherence: Option<crate::coherence::CoherenceCheck>,
epoch_tracker: crate::coherence::EpochTracker,
metrics_series: crate::metrics::MetricsSeries,
}
const EMA_ALPHA: f32 = 0.1;
impl TieredStore {
pub fn new(block_bytes: usize) -> Self {
Self {
block_bytes,
index: HashMap::new(),
tier1_data: HashMap::new(),
tier2_data: HashMap::new(),
tier3_data: HashMap::new(),
tier1_keys: Vec::new(),
tier2_keys: Vec::new(),
tier3_keys: Vec::new(),
witness_log: crate::metrics::WitnessLog::new(10_000),
coherence: None,
epoch_tracker: crate::coherence::EpochTracker::new(),
metrics_series: crate::metrics::MetricsSeries::new(256),
}
}
#[inline]
pub fn block_bytes(&self) -> usize {
self.block_bytes
}
pub fn witness_log(&self) -> &crate::metrics::WitnessLog {
&self.witness_log
}
pub fn witness_log_mut(&mut self) -> &mut crate::metrics::WitnessLog {
&mut self.witness_log
}
pub fn enable_coherence(&mut self, check: crate::coherence::CoherenceCheck) {
self.coherence = Some(check);
}
pub fn disable_coherence(&mut self) {
self.coherence = None;
}
pub fn epoch_tracker(&self) -> &crate::coherence::EpochTracker {
&self.epoch_tracker
}
pub fn epoch_tracker_mut(&mut self) -> &mut crate::coherence::EpochTracker {
&mut self.epoch_tracker
}
pub fn metrics_series(&self) -> &crate::metrics::MetricsSeries {
&self.metrics_series
}
pub fn metrics_series_mut(&mut self) -> &mut crate::metrics::MetricsSeries {
&mut self.metrics_series
}
pub fn coherence_check(
&mut self,
key: BlockKey,
original_data: &[f32],
now: u64,
) -> Option<Result<crate::coherence::CoherenceResult, StoreError>> {
let check = self.coherence.clone()?;
Some(check.check_coherence(self, key, original_data, now))
}
pub fn metrics(&self) -> crate::metrics::StoreMetrics {
let mut m = crate::metrics::StoreMetrics::new();
m.total_blocks = self.index.len() as u64;
m.tier0_blocks = self
.index
.values()
.filter(|b| b.tier == Tier::Tier0)
.count() as u64;
m.tier1_blocks = self.tier1_keys.len() as u64;
m.tier2_blocks = self.tier2_keys.len() as u64;
m.tier3_blocks = self.tier3_keys.len() as u64;
m.tier1_bytes = self
.tier1_data
.values()
.map(|d| d.packed.len() as u64)
.sum();
m.tier2_bytes = self
.tier2_data
.values()
.map(|d| d.packed.len() as u64)
.sum();
m.tier3_bytes = self
.tier3_data
.values()
.map(|d| d.packed.len() as u64)
.sum();
m.total_evictions = self.witness_log.count_evictions() as u64;
m.tier_flips_last_minute = self.witness_log.tier_flip_rate(60, self.index.len() as u64);
m
}
pub fn put(
&mut self,
key: BlockKey,
data: &[f32],
tier: Tier,
now: u64,
) -> Result<(), StoreError> {
if tier == Tier::Tier0 {
return Err(StoreError::InvalidBlock);
}
let bits = bits_for_tier(tier);
let (packed, scale) = quantize_block(data, bits);
let checksum = block_checksum(&packed, scale);
if let Some(old_meta) = self.index.get(&key) {
let old_tier = old_meta.tier;
self.remove_data(old_tier, key);
self.remove_from_bucket(old_tier, key);
}
let byte_count = packed.len() as u32;
let block = BlockData {
element_count: data.len() as u32,
packed,
};
match tier {
Tier::Tier1 => {
self.tier1_data.insert(key, block);
}
Tier::Tier2 => {
self.tier2_data.insert(key, block);
}
Tier::Tier3 => {
self.tier3_data.insert(key, block);
}
Tier::Tier0 => unreachable!(),
}
self.add_to_bucket(tier, key);
let meta = BlockMeta {
key,
dtype: DType::F32,
tier,
bits,
scale,
zero_point: 0,
created_at: now,
last_access_at: now,
access_count: 1,
ema_rate: 0.0,
window: 1,
checksum,
reconstruct: ReconstructPolicy::None,
tier_age: 0,
lineage_parent: None,
block_bytes: byte_count,
};
self.index.insert(key, meta);
self.witness_log.record(
now,
crate::metrics::WitnessEvent::Access {
key,
score: 0.0,
tier,
},
);
self.epoch_tracker.record_write(key);
Ok(())
}
pub fn get(&mut self, key: BlockKey, out: &mut [f32], now: u64) -> Result<usize, StoreError> {
let meta = self.index.get(&key).ok_or(StoreError::BlockNotFound)?;
if meta.tier == Tier::Tier0 {
return Err(StoreError::TensorEvicted);
}
let tier = meta.tier;
let scale = meta.scale;
let bits = meta.bits;
let checksum = meta.checksum;
let block = self
.data_map(tier)
.and_then(|m| m.get(&key))
.ok_or(StoreError::BlockNotFound)?;
let actual_crc = block_checksum(&block.packed, scale);
if actual_crc != checksum {
return Err(StoreError::ChecksumMismatch);
}
let n = dequantize_block(
&block.packed,
scale,
bits,
block.element_count as usize,
out,
);
self.touch(key, now);
self.witness_log.record(
now,
crate::metrics::WitnessEvent::Access {
key,
score: 0.0, tier,
},
);
Ok(n)
}
pub fn touch(&mut self, key: BlockKey, now: u64) {
if let Some(meta) = self.index.get_mut(&key) {
let delta = now.saturating_sub(meta.last_access_at);
if delta >= 64 {
meta.window = 1;
} else if delta > 0 {
meta.window = (meta.window << delta) | 1;
}
if delta > 0 {
let instant_rate = 1.0 / delta as f32;
meta.ema_rate = EMA_ALPHA * instant_rate + (1.0 - EMA_ALPHA) * meta.ema_rate;
}
meta.last_access_at = now;
meta.access_count = meta.access_count.saturating_add(1);
}
}
pub fn meta(&self, key: BlockKey) -> Option<&BlockMeta> {
self.index.get(&key)
}
pub fn block_count(&self) -> usize {
self.index.len()
}
pub fn tier_count(&self, tier: Tier) -> usize {
match tier {
Tier::Tier0 => self
.index
.values()
.filter(|m| m.tier == Tier::Tier0)
.count(),
Tier::Tier1 => self.tier1_keys.len(),
Tier::Tier2 => self.tier2_keys.len(),
Tier::Tier3 => self.tier3_keys.len(),
}
}
pub fn total_bytes(&self) -> usize {
let sum = |map: &HashMap<BlockKey, BlockData>| -> usize {
map.values().map(|b| b.packed.len()).sum()
};
sum(&self.tier1_data) + sum(&self.tier2_data) + sum(&self.tier3_data)
}
pub fn blocks_in_tier(&self, tier: Tier) -> &[BlockKey] {
match tier {
Tier::Tier0 => &[],
Tier::Tier1 => &self.tier1_keys,
Tier::Tier2 => &self.tier2_keys,
Tier::Tier3 => &self.tier3_keys,
}
}
pub fn evict(&mut self, key: BlockKey, policy: ReconstructPolicy) -> Result<(), StoreError> {
let meta = self.index.get_mut(&key).ok_or(StoreError::BlockNotFound)?;
let old_tier = meta.tier;
if old_tier == Tier::Tier0 {
meta.reconstruct = policy;
return Ok(());
}
let bytes_freed = meta.block_bytes as usize;
let evict_ts = meta.last_access_at;
meta.tier = Tier::Tier0;
meta.reconstruct = policy;
meta.tier_age = 0;
meta.block_bytes = 0;
meta.bits = 0;
self.remove_data(old_tier, key);
self.remove_from_bucket(old_tier, key);
self.witness_log.record(
evict_ts,
crate::metrics::WitnessEvent::Eviction {
key,
score: 0.0,
bytes_freed,
},
);
Ok(())
}
fn data_map(&self, tier: Tier) -> Option<&HashMap<BlockKey, BlockData>> {
match tier {
Tier::Tier0 => None,
Tier::Tier1 => Some(&self.tier1_data),
Tier::Tier2 => Some(&self.tier2_data),
Tier::Tier3 => Some(&self.tier3_data),
}
}
fn remove_data(&mut self, tier: Tier, key: BlockKey) {
match tier {
Tier::Tier1 => {
self.tier1_data.remove(&key);
}
Tier::Tier2 => {
self.tier2_data.remove(&key);
}
Tier::Tier3 => {
self.tier3_data.remove(&key);
}
Tier::Tier0 => {}
}
}
fn remove_from_bucket(&mut self, tier: Tier, key: BlockKey) {
let bucket = match tier {
Tier::Tier1 => &mut self.tier1_keys,
Tier::Tier2 => &mut self.tier2_keys,
Tier::Tier3 => &mut self.tier3_keys,
Tier::Tier0 => return,
};
if let Some(pos) = bucket.iter().position(|k| *k == key) {
bucket.swap_remove(pos);
}
}
fn add_to_bucket(&mut self, tier: Tier, key: BlockKey) {
match tier {
Tier::Tier1 => self.tier1_keys.push(key),
Tier::Tier2 => self.tier2_keys.push(key),
Tier::Tier3 => self.tier3_keys.push(key),
Tier::Tier0 => {}
}
}
pub fn tick(
&mut self,
config: &crate::tiering::TierConfig,
now: u64,
budget_bytes: usize,
budget_ops: u32,
) -> TickResult {
let mut result = TickResult::default();
let store_keys: Vec<BlockKey> = self.index.keys().copied().collect();
if store_keys.is_empty() {
return result;
}
let tiering_blocks: Vec<(crate::tiering::BlockKey, crate::tiering::BlockMeta)> = store_keys
.iter()
.enumerate()
.map(|(idx, key)| {
let meta = &self.index[key];
(
crate::tiering::BlockKey(idx as u64),
to_tiering_meta(meta, now),
)
})
.collect();
let blocks_ref: Vec<(crate::tiering::BlockKey, &crate::tiering::BlockMeta)> =
tiering_blocks.iter().map(|(k, m)| (*k, m)).collect();
let candidates = crate::tiering::select_candidates(config, now, &blocks_ref);
result.candidates_found = candidates.len() as u32;
let mut remaining_bytes = budget_bytes;
let mut remaining_ops = budget_ops;
let mut migrated = std::collections::HashSet::new();
for candidate in &candidates {
if remaining_ops == 0 {
break;
}
let store_key = store_keys[candidate.key.0 as usize];
let target_tier = from_tiering_tier(candidate.target_tier);
let current_tier = from_tiering_tier(candidate.current_tier);
let old_bytes = self
.index
.get(&store_key)
.map(|m| m.block_bytes as usize)
.unwrap_or(0);
if old_bytes > remaining_bytes {
continue;
}
if target_tier == Tier::Tier0 {
if self.evict(store_key, ReconstructPolicy::None).is_ok() {
result.evictions += 1;
result.bytes_freed += old_bytes;
remaining_ops -= 1;
result.ops_used += 1;
remaining_bytes = remaining_bytes.saturating_sub(old_bytes);
migrated.insert(store_key);
}
} else {
let warm_bytes: usize = self.tier2_data.values().map(|b| b.packed.len()).sum();
let target_bits =
crate::tiering::bits_for_tier(config, to_tiering_tier(target_tier), warm_bytes);
let old_tier_u8 = current_tier as u8;
let new_tier_u8 = target_tier as u8;
if self
.migrate_block(store_key, target_tier, target_bits)
.is_ok()
{
let new_bytes = self
.index
.get(&store_key)
.map(|m| m.block_bytes as usize)
.unwrap_or(0);
if new_tier_u8 < old_tier_u8 {
result.upgrades += 1;
} else {
result.downgrades += 1;
result.bytes_freed += old_bytes.saturating_sub(new_bytes);
}
let reason = if new_tier_u8 < old_tier_u8 {
crate::metrics::TierChangeReason::ScoreUpgrade
} else {
crate::metrics::TierChangeReason::ScoreDowngrade
};
self.witness_log.record(
now,
crate::metrics::WitnessEvent::TierChange {
key: store_key,
from_tier: current_tier,
to_tier: target_tier,
score: candidate.score,
reason,
},
);
remaining_ops -= 1;
result.ops_used += 1;
remaining_bytes = remaining_bytes.saturating_sub(old_bytes);
migrated.insert(store_key);
}
}
}
for key in &store_keys {
if migrated.contains(key) {
continue;
}
if let Some(meta) = self.index.get_mut(key) {
meta.tier_age = meta.tier_age.saturating_add(1);
let mut tm = crate::tiering::BlockMeta {
ema_rate: meta.ema_rate,
access_window: meta.window,
last_access: meta.last_access_at,
access_count: meta.access_count as u64,
current_tier: to_tiering_tier(meta.tier),
tier_since: now.saturating_sub(meta.tier_age as u64),
};
crate::tiering::tick_decay(config, &mut tm);
meta.ema_rate = tm.ema_rate;
meta.window = tm.access_window;
}
}
self.witness_log.record(
now,
crate::metrics::WitnessEvent::Maintenance {
upgrades: result.upgrades,
downgrades: result.downgrades,
evictions: result.evictions,
bytes_freed: result.bytes_freed,
budget_remaining_bytes: remaining_bytes.min(u32::MAX as usize) as u32,
budget_remaining_ops: remaining_ops,
},
);
let snapshot_metrics = self.metrics();
self.metrics_series.record(now, snapshot_metrics);
result
}
fn migrate_block(
&mut self,
key: BlockKey,
target_tier: Tier,
target_bits: u8,
) -> Result<(), StoreError> {
let meta = self.index.get(&key).ok_or(StoreError::BlockNotFound)?;
let old_tier = meta.tier;
let old_bits = meta.bits;
let old_scale = meta.scale;
if old_tier == Tier::Tier0 {
return Err(StoreError::TensorEvicted);
}
if target_tier == Tier::Tier0 {
return Err(StoreError::InvalidBlock);
}
let (element_count, f32_data) = {
let block = self
.data_map(old_tier)
.and_then(|m| m.get(&key))
.ok_or(StoreError::BlockNotFound)?;
let ec = block.element_count;
let mut data = vec![0.0f32; ec as usize];
dequantize_block(&block.packed, old_scale, old_bits, ec as usize, &mut data);
(ec, data)
};
let (packed, scale) = quantize_block(&f32_data, target_bits);
let checksum = block_checksum(&packed, scale);
let byte_count = packed.len() as u32;
let new_block = BlockData {
element_count,
packed,
};
self.remove_data(old_tier, key);
self.remove_from_bucket(old_tier, key);
match target_tier {
Tier::Tier1 => {
self.tier1_data.insert(key, new_block);
}
Tier::Tier2 => {
self.tier2_data.insert(key, new_block);
}
Tier::Tier3 => {
self.tier3_data.insert(key, new_block);
}
Tier::Tier0 => unreachable!(),
}
self.add_to_bucket(target_tier, key);
let meta = self.index.get_mut(&key).unwrap();
meta.tier = target_tier;
meta.bits = target_bits;
meta.scale = scale;
meta.checksum = checksum;
meta.tier_age = 0;
meta.block_bytes = byte_count;
Ok(())
}
pub fn score_block(
&self,
key: BlockKey,
config: &crate::tiering::TierConfig,
now: u64,
) -> Option<f32> {
let meta = self.index.get(&key)?;
let tm = to_tiering_meta(meta, now);
Some(crate::tiering::compute_score(config, now, &tm))
}
pub fn touch_block(&mut self, key: BlockKey, config: &crate::tiering::TierConfig, now: u64) {
if let Some(meta) = self.index.get_mut(&key) {
let mut tm = crate::tiering::BlockMeta {
ema_rate: meta.ema_rate,
access_window: meta.window,
last_access: meta.last_access_at,
access_count: meta.access_count as u64,
current_tier: to_tiering_tier(meta.tier),
tier_since: now.saturating_sub(meta.tier_age as u64),
};
crate::tiering::touch(config, now, &mut tm);
meta.ema_rate = tm.ema_rate;
meta.window = tm.access_window;
meta.last_access_at = tm.last_access;
meta.access_count = tm.access_count.min(u32::MAX as u64) as u32;
}
}
}
impl BlockIO for TieredStore {
fn read_block(&self, tier: Tier, key: BlockKey, dst: &mut [u8]) -> Result<usize, StoreError> {
let map = self.data_map(tier).ok_or(StoreError::BlockNotFound)?;
let block = map.get(&key).ok_or(StoreError::BlockNotFound)?;
let n = block.packed.len().min(dst.len());
dst[..n].copy_from_slice(&block.packed[..n]);
Ok(n)
}
fn write_block(&mut self, tier: Tier, key: BlockKey, src: &[u8]) -> Result<(), StoreError> {
if tier == Tier::Tier0 {
return Err(StoreError::InvalidBlock);
}
let block = BlockData {
element_count: 0, packed: src.to_vec(),
};
match tier {
Tier::Tier1 => {
self.tier1_data.insert(key, block);
}
Tier::Tier2 => {
self.tier2_data.insert(key, block);
}
Tier::Tier3 => {
self.tier3_data.insert(key, block);
}
Tier::Tier0 => unreachable!(),
}
Ok(())
}
fn delete_block(&mut self, tier: Tier, key: BlockKey) -> Result<(), StoreError> {
let removed = match tier {
Tier::Tier1 => self.tier1_data.remove(&key).is_some(),
Tier::Tier2 => self.tier2_data.remove(&key).is_some(),
Tier::Tier3 => self.tier3_data.remove(&key).is_some(),
Tier::Tier0 => false,
};
if removed {
Ok(())
} else {
Err(StoreError::BlockNotFound)
}
}
}
impl MetaLog for TieredStore {
fn append(&mut self, rec: &BlockMeta) -> Result<(), StoreError> {
self.index.insert(rec.key, rec.clone());
Ok(())
}
fn get(&self, key: BlockKey) -> Option<&BlockMeta> {
self.index.get(&key)
}
fn iter(&self) -> Box<dyn Iterator<Item = &BlockMeta> + '_> {
Box::new(self.index.values())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn make_key(tid: u128, idx: u32) -> BlockKey {
BlockKey {
tensor_id: tid,
block_index: idx,
}
}
#[test]
fn test_crc32_known_vector() {
let data = b"123456789";
assert_eq!(crc32(data), 0xCBF4_3926);
}
#[test]
fn test_crc32_empty() {
assert_eq!(crc32(&[]), 0x0000_0000);
}
#[test]
fn test_crc32_single_byte() {
assert_eq!(crc32(&[0x00]), 0xD202_EF8D);
}
#[test]
fn test_block_key_equality() {
let a = make_key(1, 0);
let b = make_key(1, 0);
let c = make_key(1, 1);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_block_key_hash_differs() {
fn hash_of(k: &BlockKey) -> u64 {
let mut h = DefaultHasher::new();
k.hash(&mut h);
h.finish()
}
let a = make_key(1, 0);
let b = make_key(2, 0);
let c = make_key(1, 1);
assert_ne!(hash_of(&a), hash_of(&b));
assert_ne!(hash_of(&a), hash_of(&c));
}
#[test]
fn test_block_key_hash_stable() {
fn hash_of(k: &BlockKey) -> u64 {
let mut h = DefaultHasher::new();
k.hash(&mut h);
h.finish()
}
let a = make_key(42, 7);
let b = make_key(42, 7);
assert_eq!(hash_of(&a), hash_of(&b));
}
#[test]
fn test_qmax_values() {
assert_eq!(qmax(8), 127);
assert_eq!(qmax(7), 63);
assert_eq!(qmax(5), 15);
assert_eq!(qmax(3), 3);
assert_eq!(qmax(1), 0);
assert_eq!(qmax(0), 0);
assert_eq!(qmax(9), 0);
}
#[test]
fn test_quantize_roundtrip_8bit() {
let data: Vec<f32> = (0..128).map(|i| (i as f32 - 64.0) * 0.1).collect();
let (packed, scale) = quantize_block(&data, 8);
let mut out = vec![0.0f32; 128];
let n = dequantize_block(&packed, scale, 8, 128, &mut out);
assert_eq!(n, 128);
for (i, (&orig, &dec)) in data.iter().zip(out.iter()).enumerate() {
let err = (orig - dec).abs();
let tol = if orig.abs() > 0.01 {
orig.abs() * 0.02
} else {
0.1
};
assert!(err < tol, "i={i} orig={orig} dec={dec} err={err}");
}
}
#[test]
fn test_quantize_roundtrip_3bit() {
let data: Vec<f32> = (0..64).map(|i| (i as f32 - 32.0) * 0.5).collect();
let (packed, scale) = quantize_block(&data, 3);
let mut out = vec![0.0f32; 64];
let n = dequantize_block(&packed, scale, 3, 64, &mut out);
assert_eq!(n, 64);
let max_val = data.iter().map(|v| v.abs()).fold(0.0f32, f32::max);
for (&orig, &dec) in data.iter().zip(out.iter()) {
let err = (orig - dec).abs();
assert!(err < max_val * 0.35, "orig={orig} dec={dec} err={err}");
}
}
#[test]
fn test_quantize_zeros() {
let data = vec![0.0f32; 64];
let (packed, scale) = quantize_block(&data, 8);
assert_eq!(scale, 0.0);
let mut out = vec![1.0f32; 64];
let n = dequantize_block(&packed, scale, 8, 64, &mut out);
assert_eq!(n, 64);
for &v in &out {
assert_eq!(v, 0.0);
}
}
#[test]
fn test_store_put_get_roundtrip() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let data: Vec<f32> = (0..64).map(|i| i as f32 * 0.25).collect();
store.put(key, &data, Tier::Tier1, 0).unwrap();
let mut out = vec![0.0f32; 64];
let n = TieredStore::get(&mut store, key, &mut out, 1).unwrap();
assert_eq!(n, 64);
for (i, (&orig, &dec)) in data.iter().zip(out.iter()).enumerate() {
let err = (orig - dec).abs();
let tol = if orig.abs() > 0.01 {
orig.abs() * 0.02
} else {
0.15
};
assert!(err < tol, "i={i} orig={orig} dec={dec} err={err}");
}
}
#[test]
fn test_store_put_tier3_roundtrip() {
let mut store = TieredStore::new(4096);
let key = make_key(10, 5);
let data: Vec<f32> = (0..32).map(|i| (i as f32 - 16.0) * 0.5).collect();
store.put(key, &data, Tier::Tier3, 100).unwrap();
let meta = store.meta(key).unwrap();
assert_eq!(meta.tier, Tier::Tier3);
assert_eq!(meta.bits, 3);
assert_eq!(meta.created_at, 100);
let mut out = vec![0.0f32; 32];
let n = TieredStore::get(&mut store, key, &mut out, 101).unwrap();
assert_eq!(n, 32);
let max_val = data.iter().map(|v| v.abs()).fold(0.0f32, f32::max);
for (&orig, &dec) in data.iter().zip(out.iter()) {
let err = (orig - dec).abs();
assert!(err < max_val * 0.35, "orig={orig} dec={dec} err={err}");
}
}
#[test]
fn test_store_get_not_found() {
let mut store = TieredStore::new(4096);
let key = make_key(99, 0);
let mut out = vec![0.0f32; 8];
assert_eq!(
TieredStore::get(&mut store, key, &mut out, 0),
Err(StoreError::BlockNotFound)
);
}
#[test]
fn test_store_put_tier0_rejected() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let data = vec![1.0f32; 8];
assert_eq!(
store.put(key, &data, Tier::Tier0, 0),
Err(StoreError::InvalidBlock)
);
}
#[test]
fn test_eviction() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let data = vec![1.0f32; 64];
store.put(key, &data, Tier::Tier1, 0).unwrap();
assert_eq!(store.tier_count(Tier::Tier1), 1);
assert!(store.total_bytes() > 0);
store.evict(key, ReconstructPolicy::Delta).unwrap();
let meta = store.meta(key).unwrap();
assert_eq!(meta.tier, Tier::Tier0);
assert_eq!(meta.reconstruct, ReconstructPolicy::Delta);
assert_eq!(meta.block_bytes, 0);
assert_eq!(meta.bits, 0);
assert_eq!(meta.tier_age, 0);
let mut out = vec![0.0f32; 64];
assert_eq!(
TieredStore::get(&mut store, key, &mut out, 1),
Err(StoreError::TensorEvicted)
);
assert_eq!(store.tier_count(Tier::Tier1), 0);
assert_eq!(store.tier_count(Tier::Tier0), 1);
assert_eq!(store.block_count(), 1);
}
#[test]
fn test_eviction_not_found() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
assert_eq!(
store.evict(key, ReconstructPolicy::None),
Err(StoreError::BlockNotFound),
);
}
#[test]
fn test_eviction_idempotent() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
store.put(key, &[1.0; 16], Tier::Tier2, 0).unwrap();
store.evict(key, ReconstructPolicy::None).unwrap();
store.evict(key, ReconstructPolicy::Factor).unwrap();
let meta = store.meta(key).unwrap();
assert_eq!(meta.reconstruct, ReconstructPolicy::Factor);
}
#[test]
fn test_tier_counts() {
let mut store = TieredStore::new(4096);
let data = vec![1.0f32; 16];
store.put(make_key(1, 0), &data, Tier::Tier1, 0).unwrap();
store.put(make_key(2, 0), &data, Tier::Tier1, 0).unwrap();
store.put(make_key(3, 0), &data, Tier::Tier2, 0).unwrap();
store.put(make_key(4, 0), &data, Tier::Tier3, 0).unwrap();
store.put(make_key(5, 0), &data, Tier::Tier3, 0).unwrap();
store.put(make_key(6, 0), &data, Tier::Tier3, 0).unwrap();
assert_eq!(store.block_count(), 6);
assert_eq!(store.tier_count(Tier::Tier0), 0);
assert_eq!(store.tier_count(Tier::Tier1), 2);
assert_eq!(store.tier_count(Tier::Tier2), 1);
assert_eq!(store.tier_count(Tier::Tier3), 3);
assert_eq!(store.blocks_in_tier(Tier::Tier1).len(), 2);
assert_eq!(store.blocks_in_tier(Tier::Tier0).len(), 0);
}
#[test]
fn test_total_bytes() {
let mut store = TieredStore::new(4096);
assert_eq!(store.total_bytes(), 0);
let data = vec![1.0f32; 64];
store.put(make_key(1, 0), &data, Tier::Tier1, 0).unwrap();
let bytes_after_one = store.total_bytes();
assert!(bytes_after_one > 0);
store.put(make_key(2, 0), &data, Tier::Tier2, 0).unwrap();
assert!(store.total_bytes() > bytes_after_one);
}
#[test]
fn test_total_bytes_decreases_on_evict() {
let mut store = TieredStore::new(4096);
let data = vec![1.0f32; 64];
let key = make_key(1, 0);
store.put(key, &data, Tier::Tier1, 0).unwrap();
let before = store.total_bytes();
store.evict(key, ReconstructPolicy::None).unwrap();
assert_eq!(store.total_bytes(), before - before); }
#[test]
fn test_touch_updates_stats() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
store.put(key, &[1.0; 16], Tier::Tier1, 0).unwrap();
let meta = store.meta(key).unwrap();
assert_eq!(meta.access_count, 1);
assert_eq!(meta.last_access_at, 0);
assert_eq!(meta.window, 1);
store.touch(key, 5);
let meta = store.meta(key).unwrap();
assert_eq!(meta.access_count, 2);
assert_eq!(meta.last_access_at, 5);
assert_eq!(meta.window, (1u64 << 5) | 1);
assert!(meta.ema_rate > 0.0);
store.touch(key, 5);
let meta = store.meta(key).unwrap();
assert_eq!(meta.access_count, 3);
assert_eq!(meta.window, (1u64 << 5) | 1);
}
#[test]
fn test_touch_window_overflow() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
store.put(key, &[1.0; 16], Tier::Tier1, 0).unwrap();
store.touch(key, 100);
let meta = store.meta(key).unwrap();
assert_eq!(meta.window, 1);
assert_eq!(meta.last_access_at, 100);
}
#[test]
fn test_touch_nonexistent_noop() {
let mut store = TieredStore::new(4096);
store.touch(make_key(42, 0), 10);
}
#[test]
fn test_put_overwrite() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
store.put(key, &[1.0; 16], Tier::Tier1, 0).unwrap();
assert_eq!(store.tier_count(Tier::Tier1), 1);
store.put(key, &[2.0; 16], Tier::Tier3, 10).unwrap();
assert_eq!(store.block_count(), 1);
assert_eq!(store.tier_count(Tier::Tier1), 0);
assert_eq!(store.tier_count(Tier::Tier3), 1);
let meta = store.meta(key).unwrap();
assert_eq!(meta.tier, Tier::Tier3);
assert_eq!(meta.created_at, 10);
}
#[test]
fn test_checksum_stored_correctly() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let data: Vec<f32> = (0..32).map(|i| i as f32).collect();
store.put(key, &data, Tier::Tier1, 0).unwrap();
let meta = store.meta(key).unwrap();
assert_ne!(meta.checksum, 0);
let (packed, scale) = quantize_block(&data, 8);
let expected = block_checksum(&packed, scale);
assert_eq!(meta.checksum, expected);
}
#[test]
fn test_block_io_write_read() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let raw = vec![0xAA, 0xBB, 0xCC, 0xDD];
store.write_block(Tier::Tier1, key, &raw).unwrap();
let mut dst = vec![0u8; 8];
let n = store.read_block(Tier::Tier1, key, &mut dst).unwrap();
assert_eq!(n, 4);
assert_eq!(&dst[..4], &raw);
}
#[test]
fn test_block_io_delete() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
store.write_block(Tier::Tier2, key, &[1, 2, 3]).unwrap();
store.delete_block(Tier::Tier2, key).unwrap();
let mut dst = vec![0u8; 4];
assert_eq!(
store.read_block(Tier::Tier2, key, &mut dst),
Err(StoreError::BlockNotFound),
);
}
#[test]
fn test_block_io_write_tier0_rejected() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
assert_eq!(
store.write_block(Tier::Tier0, key, &[1]),
Err(StoreError::InvalidBlock),
);
}
#[test]
fn test_meta_log_append_get() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let meta = BlockMeta {
key,
dtype: DType::F32,
tier: Tier::Tier1,
bits: 8,
scale: 0.5,
zero_point: 0,
created_at: 42,
last_access_at: 42,
access_count: 1,
ema_rate: 0.0,
window: 1,
checksum: 0,
reconstruct: ReconstructPolicy::None,
tier_age: 0,
lineage_parent: None,
block_bytes: 64,
};
MetaLog::append(&mut store, &meta).unwrap();
let retrieved = MetaLog::get(&store, key).unwrap();
assert_eq!(retrieved.key, key);
assert_eq!(retrieved.created_at, 42);
}
#[test]
fn test_meta_log_iter() {
let mut store = TieredStore::new(4096);
let data = vec![1.0f32; 8];
store.put(make_key(1, 0), &data, Tier::Tier1, 0).unwrap();
store.put(make_key(2, 0), &data, Tier::Tier2, 0).unwrap();
store.put(make_key(3, 0), &data, Tier::Tier3, 0).unwrap();
let entries: Vec<_> = MetaLog::iter(&store).collect();
assert_eq!(entries.len(), 3);
}
#[test]
fn test_bits_for_tier() {
assert_eq!(bits_for_tier(Tier::Tier0), 0);
assert_eq!(bits_for_tier(Tier::Tier1), 8);
assert_eq!(bits_for_tier(Tier::Tier2), 7);
assert_eq!(bits_for_tier(Tier::Tier3), 3);
}
#[test]
fn test_tier_repr() {
assert_eq!(Tier::Tier0 as u8, 0);
assert_eq!(Tier::Tier1 as u8, 1);
assert_eq!(Tier::Tier2 as u8, 2);
assert_eq!(Tier::Tier3 as u8, 3);
}
#[test]
fn test_dtype_repr() {
assert_eq!(DType::F32 as u8, 0);
assert_eq!(DType::F16 as u8, 1);
assert_eq!(DType::BF16 as u8, 2);
}
#[test]
fn test_reconstruct_policy_repr() {
assert_eq!(ReconstructPolicy::None as u8, 0);
assert_eq!(ReconstructPolicy::Delta as u8, 1);
assert_eq!(ReconstructPolicy::Factor as u8, 2);
}
#[test]
fn test_multi_block_workflow() {
let mut store = TieredStore::new(4096);
for i in 0..10u32 {
let key = make_key(1, i);
let data: Vec<f32> = (0..32).map(|j| (i * 32 + j) as f32 * 0.1).collect();
let tier = match i % 3 {
0 => Tier::Tier1,
1 => Tier::Tier2,
_ => Tier::Tier3,
};
store.put(key, &data, tier, i as u64).unwrap();
}
assert_eq!(store.block_count(), 10);
assert_eq!(store.tier_count(Tier::Tier1), 4); assert_eq!(store.tier_count(Tier::Tier2), 3); assert_eq!(store.tier_count(Tier::Tier3), 3);
store.touch(make_key(1, 0), 20);
store.touch(make_key(1, 5), 25);
store
.evict(make_key(1, 8), ReconstructPolicy::Delta)
.unwrap();
assert_eq!(store.tier_count(Tier::Tier3), 2);
assert_eq!(store.tier_count(Tier::Tier0), 1);
assert_eq!(store.block_count(), 10);
let mut out = vec![0.0f32; 32];
let n = TieredStore::get(&mut store, make_key(1, 0), &mut out, 30).unwrap();
assert_eq!(n, 32);
}
#[test]
fn test_tick_empty_store() {
let mut store = TieredStore::new(4096);
let config = crate::tiering::TierConfig::default();
let result = store.tick(&config, 100, 1_000_000, 100);
assert_eq!(result.upgrades, 0);
assert_eq!(result.downgrades, 0);
assert_eq!(result.evictions, 0);
assert_eq!(result.bytes_freed, 0);
assert_eq!(result.ops_used, 0);
assert_eq!(result.candidates_found, 0);
}
#[test]
fn test_tick_migrates_cold_to_hot() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
let data: Vec<f32> = (0..64).map(|i| i as f32 * 0.1).collect();
store.put(key, &data, Tier::Tier3, 0).unwrap();
assert_eq!(store.tier_count(Tier::Tier3), 1);
if let Some(meta) = store.index.get_mut(&key) {
meta.ema_rate = 1.0;
meta.window = u64::MAX; meta.last_access_at = 100;
meta.access_count = 100;
meta.tier_age = 10; }
let config = crate::tiering::TierConfig::default();
let result = store.tick(&config, 100, 1_000_000, 100);
assert!(
result.upgrades > 0,
"expected at least one upgrade, got {}",
result.upgrades
);
assert_eq!(result.downgrades, 0);
assert!(result.candidates_found > 0);
let meta = store.meta(key).unwrap();
assert_eq!(
meta.tier,
Tier::Tier1,
"block should be in Tier1 after upgrade"
);
assert_eq!(meta.bits, 8, "Tier1 should use 8-bit quantization");
assert_eq!(meta.tier_age, 0, "tier_age should reset after migration");
let mut out = vec![0.0f32; 64];
let n = TieredStore::get(&mut store, key, &mut out, 101).unwrap();
assert_eq!(n, 64);
}
#[test]
fn test_tick_respects_budget_ops() {
let mut store = TieredStore::new(4096);
let data: Vec<f32> = (0..64).map(|i| i as f32 * 0.1).collect();
for i in 0..5u32 {
let key = make_key(i as u128 + 1, 0);
store.put(key, &data, Tier::Tier3, 0).unwrap();
if let Some(meta) = store.index.get_mut(&key) {
meta.ema_rate = 1.0;
meta.window = u64::MAX;
meta.last_access_at = 100;
meta.access_count = 100;
meta.tier_age = 10;
}
}
let config = crate::tiering::TierConfig::default();
let result = store.tick(&config, 100, 1_000_000, 2);
assert_eq!(result.ops_used, 2, "should use exactly 2 ops");
assert_eq!(result.upgrades, 2, "should upgrade only 2 blocks");
assert!(result.candidates_found >= 5, "should find all 5 candidates");
}
#[test]
fn test_touch_block_updates_ema_and_window() {
let mut store = TieredStore::new(4096);
let key = make_key(1, 0);
store.put(key, &[1.0; 16], Tier::Tier1, 0).unwrap();
let config = crate::tiering::TierConfig::default();
let meta = store.meta(key).unwrap();
assert_eq!(meta.ema_rate, 0.0);
store.touch_block(key, &config, 5);
let meta = store.meta(key).unwrap();
assert!(
(meta.ema_rate - config.alpha).abs() < 1e-6,
"ema_rate={}, expected={}",
meta.ema_rate,
config.alpha,
);
assert_eq!(meta.last_access_at, 5);
assert_ne!(meta.window & 1, 0, "bit 0 should be set");
assert_eq!(meta.window, (1u64 << 5) | 1);
}
#[test]
fn test_score_block_none_for_missing() {
let store = TieredStore::new(4096);
let config = crate::tiering::TierConfig::default();
let result = store.score_block(make_key(99, 0), &config, 100);
assert_eq!(result, None);
}
#[test]
fn test_epoch_tracker_wired_into_put() {
let mut store = TieredStore::new(4096);
let key = BlockKey {
tensor_id: 1,
block_index: 0,
};
let data = vec![1.0f32; 64];
assert_eq!(store.epoch_tracker().check_epoch(key), None);
store.put(key, &data, Tier::Tier1, 0).unwrap();
assert!(store.epoch_tracker().check_epoch(key).is_some());
let epoch1 = store.epoch_tracker().check_epoch(key).unwrap();
store.put(key, &data, Tier::Tier1, 1).unwrap();
let epoch2 = store.epoch_tracker().check_epoch(key).unwrap();
assert!(epoch2 > epoch1, "epoch should increment on overwrite");
}
#[test]
fn test_coherence_disabled_by_default() {
let mut store = TieredStore::new(4096);
let key = BlockKey {
tensor_id: 1,
block_index: 0,
};
let data = vec![1.0f32; 64];
store.put(key, &data, Tier::Tier1, 0).unwrap();
assert!(store.coherence_check(key, &data, 1).is_none());
}
#[test]
fn test_coherence_enabled_passes() {
let mut store = TieredStore::new(4096);
store.enable_coherence(crate::coherence::CoherenceCheck::default());
let key = BlockKey {
tensor_id: 1,
block_index: 0,
};
let data: Vec<f32> = (0..64).map(|i| (i as f32 + 1.0) * 0.25).collect();
store.put(key, &data, Tier::Tier1, 0).unwrap();
let result = store.coherence_check(key, &data, 1).unwrap().unwrap();
assert!(
result.passed,
"Tier1 coherence should pass; err={}",
result.max_error
);
}
#[test]
fn test_metrics_series_wired_into_tick() {
use crate::tiering::TierConfig;
let mut store = TieredStore::new(4096);
let config = TierConfig::default();
for i in 0..5u128 {
let key = BlockKey {
tensor_id: i,
block_index: 0,
};
store.put(key, &vec![1.0f32; 64], Tier::Tier1, 0).unwrap();
}
assert!(store.metrics_series().is_empty());
store.tick(&config, 100, 1_000_000, 100);
assert_eq!(store.metrics_series().len(), 1);
store.tick(&config, 200, 1_000_000, 100);
assert_eq!(store.metrics_series().len(), 2);
let (ts, m) = store.metrics_series().latest().unwrap();
assert_eq!(*ts, 200);
assert_eq!(m.total_blocks, 5);
}
#[test]
fn bench_batch_scoring_10k() {
use crate::tiering::{
compute_score, compute_scores_batch, BlockMeta as TBlockMeta, Tier as TTier, TierConfig,
};
use std::time::Instant;
let cfg = TierConfig::default();
let metas: Vec<TBlockMeta> = (0..10_000)
.map(|i| TBlockMeta {
ema_rate: (i as f32) * 0.0001,
access_window: 0x5555_5555_5555_5555,
last_access: 50 + (i as u64 % 100),
access_count: i as u64,
current_tier: TTier::Tier1,
tier_since: 0,
})
.collect();
let iters = 1000;
let start = Instant::now();
for _ in 0..iters {
for m in &metas {
std::hint::black_box(compute_score(&cfg, 100, m));
}
}
let individual = start.elapsed();
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(compute_scores_batch(&cfg, 100, &metas));
}
let batch = start.elapsed();
eprintln!(
"Individual scoring 10k x {iters}: {:?} ({:.0} ns/block)",
individual,
individual.as_nanos() as f64 / (iters * 10_000) as f64
);
eprintln!(
"Batch scoring 10k x {iters}: {:?} ({:.0} ns/block)",
batch,
batch.as_nanos() as f64 / (iters * 10_000) as f64
);
}
#[test]
fn bench_dequant_5bit_4096() {
use std::time::Instant;
let data: Vec<f32> = (0..4096).map(|i| (i as f32 - 2048.0) * 0.01).collect();
let (packed, scale) = quantize_block(&data, 5);
let mut out = vec![0.0f32; 4096];
let iters = 10_000;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(dequantize_block(&packed, scale, 5, 4096, &mut out));
}
let elapsed = start.elapsed();
let total_bytes = 4096u64 * 4 * iters as u64;
let gbs = total_bytes as f64 / elapsed.as_secs_f64() / 1e9;
eprintln!(
"Dequant 5-bit 4096 x {iters}: {:?} ({:.2} GB/s output throughput)",
elapsed, gbs
);
}
#[test]
fn bench_dequant_7bit_4096() {
use std::time::Instant;
let data: Vec<f32> = (0..4096).map(|i| (i as f32 - 2048.0) * 0.01).collect();
let (packed, scale) = quantize_block(&data, 7);
let mut out = vec![0.0f32; 4096];
let iters = 10_000;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(dequantize_block(&packed, scale, 7, 4096, &mut out));
}
let elapsed = start.elapsed();
let total_bytes = 4096u64 * 4 * iters as u64;
let gbs = total_bytes as f64 / elapsed.as_secs_f64() / 1e9;
eprintln!(
"Dequant 7-bit 4096 x {iters}: {:?} ({:.2} GB/s output throughput)",
elapsed, gbs
);
}
#[test]
fn bench_quant_5bit_4096() {
use std::time::Instant;
let data: Vec<f32> = (0..4096).map(|i| (i as f32 - 2048.0) * 0.01).collect();
let iters = 10_000;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(quantize_block(&data, 5));
}
let elapsed = start.elapsed();
let total_bytes = 4096u64 * 4 * iters as u64;
let gbs = total_bytes as f64 / elapsed.as_secs_f64() / 1e9;
eprintln!(
"Quant 5-bit 4096 x {iters}: {:?} ({:.2} GB/s input throughput)",
elapsed, gbs
);
}
#[test]
fn bench_svd_adaptive_64x64() {
use crate::delta::FactorSet;
use std::time::Instant;
let (rows, cols) = (64, 64);
let data: Vec<f32> = (0..rows * cols)
.map(|i| (i as f32 * 0.37).sin() + (i as f32 * 0.73).cos())
.collect();
let iters = 100;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(FactorSet::from_data_adaptive(&data, rows, cols, 16, 0.05));
}
let elapsed = start.elapsed();
eprintln!(
"SVD adaptive 64x64 (max_rank=16, target=0.05) x {iters}: {:?} ({:.2} ms/iter)",
elapsed,
elapsed.as_secs_f64() * 1000.0 / iters as f64
);
}
#[test]
fn bench_format_report() {
use crate::metrics::StoreMetrics;
use std::time::Instant;
let m = StoreMetrics {
total_blocks: 10_000,
tier0_blocks: 500,
tier1_blocks: 4000,
tier2_blocks: 3500,
tier3_blocks: 2000,
tier1_bytes: 4_000_000,
tier2_bytes: 2_500_000,
tier3_bytes: 750_000,
total_reads: 1_000_000,
total_writes: 500_000,
total_evictions: 5000,
total_upgrades: 12_000,
total_downgrades: 8000,
total_reconstructions: 200,
total_checksum_failures: 0,
total_compactions: 150,
tier_flips_last_minute: 0.023,
avg_score_tier1: 0.85,
avg_score_tier2: 0.45,
avg_score_tier3: 0.12,
};
let iters = 10_000;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(m.format_report());
}
let elapsed = start.elapsed();
eprintln!(
"format_report x {iters}: {:?} ({:.0} ns/call)",
elapsed,
elapsed.as_nanos() as f64 / iters as f64
);
}
#[test]
fn bench_format_json() {
use crate::metrics::StoreMetrics;
use std::time::Instant;
let m = StoreMetrics {
total_blocks: 10_000,
tier0_blocks: 500,
tier1_blocks: 4000,
tier2_blocks: 3500,
tier3_blocks: 2000,
tier1_bytes: 4_000_000,
tier2_bytes: 2_500_000,
tier3_bytes: 750_000,
total_reads: 1_000_000,
total_writes: 500_000,
total_evictions: 5000,
total_upgrades: 12_000,
total_downgrades: 8000,
total_reconstructions: 200,
total_checksum_failures: 0,
total_compactions: 150,
tier_flips_last_minute: 0.023,
avg_score_tier1: 0.85,
avg_score_tier2: 0.45,
avg_score_tier3: 0.12,
};
let iters = 10_000;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(m.format_json());
}
let elapsed = start.elapsed();
eprintln!(
"format_json x {iters}: {:?} ({:.0} ns/call)",
elapsed,
elapsed.as_nanos() as f64 / iters as f64
);
}
#[test]
fn bench_metrics_series_trend_100() {
use crate::metrics::{MetricsSeries, StoreMetrics};
use std::time::Instant;
let mut series = MetricsSeries::new(256);
for i in 0..100u64 {
series.record(
i,
StoreMetrics {
total_blocks: 1000 + i,
tier1_blocks: 400 + i % 50,
tier2_blocks: 350,
tier3_blocks: 250,
tier1_bytes: 400_000 + i * 100,
tier2_bytes: 250_000,
tier3_bytes: 75_000,
total_evictions: i * 3,
..Default::default()
},
);
}
let iters = 10_000;
let start = Instant::now();
for _ in 0..iters {
std::hint::black_box(series.trend());
}
let elapsed = start.elapsed();
eprintln!(
"MetricsSeries trend (100 snapshots) x {iters}: {:?} ({:.0} ns/call)",
elapsed,
elapsed.as_nanos() as f64 / iters as f64
);
}
}