use crate::error::{Result, ZiporaError};
use std::fmt;
#[derive(Debug, Clone)]
pub struct SeparatedStorageConfig {
pub block_size: usize,
pub enable_select_acceleration: bool,
pub select_sample_rate: usize,
pub enable_hardware_acceleration: bool,
pub enable_bit_packed_ranks: bool,
pub superblock_size: usize,
pub relative_rank_bits: usize,
pub storage_layout: StorageLayout,
pub memory_strategy: MemoryStrategy,
pub cache_alignment: CacheAlignment,
pub multi_dimensional: Option<MultiDimensionalConfig>,
pub hardware_optimizations: HardwareOptimizations,
pub performance_tuning: PerformanceTuning,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StorageLayout {
Interleaved,
Separated,
Mixed,
Hierarchical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryStrategy {
MinimizeSpace,
Balanced,
MaximizePerformance,
Adaptive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheAlignment {
CacheLine32,
CacheLine64,
CacheLine128,
Adaptive,
}
#[derive(Debug, Clone)]
pub struct MultiDimensionalConfig {
pub arity: usize,
pub dimension_hints: Vec<DimensionHint>,
pub enable_correlation_analysis: bool,
pub cache_sharing_strategy: CacheSharingStrategy,
}
#[derive(Debug, Clone)]
pub struct DimensionHint {
pub access_frequency: AccessFrequency,
pub data_density: DataDensity,
pub select_cache_density: SelectCacheDensity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessFrequency {
VeryHigh,
High,
Medium,
Low,
VeryLow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataDensity {
VeryDense,
Dense,
Balanced,
Sparse,
VerySparse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelectCacheDensity {
None,
Sparse,
Normal,
Dense,
VeryDense,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheSharingStrategy {
Separate,
SharedRank,
FullyShared,
Adaptive,
}
#[derive(Debug, Clone)]
pub struct HardwareOptimizations {
pub enable_bmi2: bool,
pub enable_bmi1: bool,
pub enable_simd: bool,
pub enable_avx512: bool,
pub enable_prefetch: bool,
pub feature_detection: FeatureDetection,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureDetection {
Runtime,
CompileTime,
ForceEnable,
Disable,
}
#[derive(Debug, Clone)]
pub struct PerformanceTuning {
pub rank_prefetch_distance: usize,
pub select_prefetch_distance: usize,
pub optimize_branch_prediction: bool,
pub enable_loop_unrolling: bool,
pub target_cache_level: CacheLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheLevel {
L1,
L2,
L3,
Adaptive,
}
impl SeparatedStorageConfig {
pub fn new() -> SeparatedStorageConfigBuilder {
SeparatedStorageConfigBuilder::new()
}
pub fn high_performance() -> SeparatedStorageConfigBuilder {
SeparatedStorageConfigBuilder::new()
.block_size(512)
.enable_select_acceleration(true)
.enable_hardware_acceleration(true)
.memory_strategy(MemoryStrategy::MaximizePerformance)
.enable_bit_packed_ranks(true)
.superblock_size(32)
.select_sample_rate(256)
}
pub fn low_memory() -> SeparatedStorageConfigBuilder {
SeparatedStorageConfigBuilder::new()
.block_size(1024)
.enable_select_acceleration(false)
.memory_strategy(MemoryStrategy::MinimizeSpace)
.enable_bit_packed_ranks(true)
.superblock_size(64)
.select_sample_rate(1024)
}
pub fn multi_dimensional(arity: usize) -> SeparatedStorageConfigBuilder {
SeparatedStorageConfigBuilder::new()
.block_size(256)
.enable_select_acceleration(true)
.enable_bit_packed_ranks(true)
.superblock_size(16)
.multi_dimensional_arity(arity)
.memory_strategy(MemoryStrategy::Balanced)
}
pub fn validate(&self) -> Result<()> {
if !matches!(self.block_size, 256 | 512 | 1024 | 2048) {
return Err(ZiporaError::invalid_data(format!(
"Invalid block size {}. Must be 256, 512, 1024, or 2048",
self.block_size
)));
}
if self.enable_bit_packed_ranks && (self.relative_rank_bits < 7 || self.relative_rank_bits > 12) {
return Err(ZiporaError::invalid_data(format!(
"Invalid relative_rank_bits {}. Must be between 7 and 12",
self.relative_rank_bits
)));
}
if self.superblock_size == 0 || self.superblock_size > 1024 {
return Err(ZiporaError::invalid_data(format!(
"Invalid superblock_size {}. Must be between 1 and 1024",
self.superblock_size
)));
}
if let Some(ref multi_config) = self.multi_dimensional {
if multi_config.arity < 2 || multi_config.arity > 4 {
return Err(ZiporaError::invalid_data(format!(
"Invalid multi-dimensional arity {}. Must be between 2 and 4",
multi_config.arity
)));
}
if multi_config.dimension_hints.len() != multi_config.arity {
return Err(ZiporaError::invalid_data(format!(
"Dimension hints count {} does not match arity {}",
multi_config.dimension_hints.len(),
multi_config.arity
)));
}
}
Ok(())
}
pub fn estimated_memory_overhead(&self) -> f64 {
let mut overhead = 0.0;
overhead += match self.block_size {
256 => 25.0,
512 => 12.5,
1024 => 6.25,
2048 => 3.125,
_ => 25.0,
};
if self.enable_select_acceleration {
overhead += 100.0 / self.select_sample_rate as f64;
}
if self.enable_bit_packed_ranks {
overhead *= 0.6; }
if let Some(ref multi_config) = self.multi_dimensional {
overhead *= multi_config.arity as f64;
}
overhead
}
pub fn recommended_select_sample_rate(&self) -> usize {
match self.memory_strategy {
MemoryStrategy::MinimizeSpace => 1024,
MemoryStrategy::Balanced => 512,
MemoryStrategy::MaximizePerformance => 256,
MemoryStrategy::Adaptive => {
if self.enable_bit_packed_ranks { 512 } else { 256 }
}
}
}
}
impl Default for SeparatedStorageConfig {
fn default() -> Self {
Self {
block_size: 256,
enable_select_acceleration: true,
select_sample_rate: 512,
enable_hardware_acceleration: true,
enable_bit_packed_ranks: false,
superblock_size: 16,
relative_rank_bits: 9,
storage_layout: StorageLayout::Separated,
memory_strategy: MemoryStrategy::Balanced,
cache_alignment: CacheAlignment::CacheLine64,
multi_dimensional: None,
hardware_optimizations: HardwareOptimizations::default(),
performance_tuning: PerformanceTuning::default(),
}
}
}
impl Default for HardwareOptimizations {
fn default() -> Self {
Self {
enable_bmi2: true,
enable_bmi1: true,
enable_simd: true,
enable_avx512: false,
enable_prefetch: true,
feature_detection: FeatureDetection::Runtime,
}
}
}
impl Default for PerformanceTuning {
fn default() -> Self {
Self {
rank_prefetch_distance: 2,
select_prefetch_distance: 1,
optimize_branch_prediction: true,
enable_loop_unrolling: true,
target_cache_level: CacheLevel::L2,
}
}
}
impl Default for DimensionHint {
fn default() -> Self {
Self {
access_frequency: AccessFrequency::Medium,
data_density: DataDensity::Balanced,
select_cache_density: SelectCacheDensity::Normal,
}
}
}
pub struct SeparatedStorageConfigBuilder {
config: SeparatedStorageConfig,
}
impl SeparatedStorageConfigBuilder {
pub fn new() -> Self {
Self {
config: SeparatedStorageConfig::default(),
}
}
pub fn block_size(mut self, size: usize) -> Self {
self.config.block_size = size;
self
}
pub fn enable_select_acceleration(mut self, enable: bool) -> Self {
self.config.enable_select_acceleration = enable;
self
}
pub fn select_sample_rate(mut self, rate: usize) -> Self {
self.config.select_sample_rate = rate;
self
}
pub fn enable_hardware_acceleration(mut self, enable: bool) -> Self {
self.config.enable_hardware_acceleration = enable;
self
}
pub fn enable_bit_packed_ranks(mut self, enable: bool) -> Self {
self.config.enable_bit_packed_ranks = enable;
self
}
pub fn superblock_size(mut self, size: usize) -> Self {
self.config.superblock_size = size;
self
}
pub fn relative_rank_bits(mut self, bits: usize) -> Self {
self.config.relative_rank_bits = bits;
self
}
pub fn storage_layout(mut self, layout: StorageLayout) -> Self {
self.config.storage_layout = layout;
self
}
pub fn memory_strategy(mut self, strategy: MemoryStrategy) -> Self {
self.config.memory_strategy = strategy;
self
}
pub fn cache_alignment(mut self, alignment: CacheAlignment) -> Self {
self.config.cache_alignment = alignment;
self
}
pub fn multi_dimensional_arity(mut self, arity: usize) -> Self {
let hints = vec![DimensionHint::default(); arity];
self.config.multi_dimensional = Some(MultiDimensionalConfig {
arity,
dimension_hints: hints,
enable_correlation_analysis: false,
cache_sharing_strategy: CacheSharingStrategy::Separate,
});
self
}
pub fn multi_dimensional_config(mut self, config: MultiDimensionalConfig) -> Self {
self.config.multi_dimensional = Some(config);
self
}
pub fn hardware_optimizations(mut self, opts: HardwareOptimizations) -> Self {
self.config.hardware_optimizations = opts;
self
}
pub fn performance_tuning(mut self, tuning: PerformanceTuning) -> Self {
self.config.performance_tuning = tuning;
self
}
pub fn optimize_for_space(mut self) -> Self {
self.config.memory_strategy = MemoryStrategy::MinimizeSpace;
self.config.enable_select_acceleration = false;
self.config.select_sample_rate = 1024;
self.config.enable_bit_packed_ranks = true;
self.config.superblock_size = 64;
self
}
pub fn optimize_for_performance(mut self) -> Self {
self.config.memory_strategy = MemoryStrategy::MaximizePerformance;
self.config.enable_select_acceleration = true;
self.config.select_sample_rate = 256;
self.config.enable_hardware_acceleration = true;
self.config.superblock_size = 32;
self.config.performance_tuning.enable_loop_unrolling = true;
self
}
pub fn build(self) -> SeparatedStorageConfig {
self.config
}
pub fn build_validated(self) -> Result<SeparatedStorageConfig> {
let config = self.config;
config.validate()?;
Ok(config)
}
}
impl Default for SeparatedStorageConfigBuilder {
fn default() -> Self {
Self::new()
}
}
impl SeparatedStorageConfig {
pub fn analyze_and_optimize(
bit_vector: &crate::succinct::BitVector,
) -> SeparatedStorageConfigBuilder {
let len = bit_vector.len();
let ones = bit_vector.count_ones();
let density = ones as f64 / len as f64;
let mut builder = SeparatedStorageConfigBuilder::new();
if len < 1_000_000 {
builder = builder.block_size(256).cache_alignment(CacheAlignment::CacheLine64);
} else if len < 100_000_000 {
builder = builder.block_size(512).cache_alignment(CacheAlignment::CacheLine64);
} else {
builder = builder
.block_size(1024)
.enable_bit_packed_ranks(true)
.superblock_size(64);
}
if density < 0.1 || density > 0.9 {
builder = builder
.memory_strategy(MemoryStrategy::MinimizeSpace)
.enable_bit_packed_ranks(true);
} else {
builder = builder
.memory_strategy(MemoryStrategy::Balanced)
.enable_select_acceleration(true);
}
builder
}
pub fn summary(&self) -> ConfigSummary {
ConfigSummary {
block_size: self.block_size,
storage_layout: self.storage_layout,
memory_strategy: self.memory_strategy,
estimated_overhead: self.estimated_memory_overhead(),
has_select_cache: self.enable_select_acceleration,
uses_bit_packing: self.enable_bit_packed_ranks,
multi_dimensional_arity: self.multi_dimensional.as_ref().map(|m| m.arity),
hardware_acceleration_enabled: self.enable_hardware_acceleration,
}
}
}
#[derive(Debug, Clone)]
pub struct ConfigSummary {
pub block_size: usize,
pub storage_layout: StorageLayout,
pub memory_strategy: MemoryStrategy,
pub estimated_overhead: f64,
pub has_select_cache: bool,
pub uses_bit_packing: bool,
pub multi_dimensional_arity: Option<usize>,
pub hardware_acceleration_enabled: bool,
}
impl fmt::Display for ConfigSummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SeparatedStorageConfig: {} blocks, {:?} layout, {:?} memory, {:.1}% overhead, select={}, bit_packing={}, hw_accel={}",
self.block_size,
self.storage_layout,
self.memory_strategy,
self.estimated_overhead,
self.has_select_cache,
self.uses_bit_packing,
self.hardware_acceleration_enabled
)?;
if let Some(arity) = self.multi_dimensional_arity {
write!(f, ", multi_dim={}", arity)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = SeparatedStorageConfig::default();
assert_eq!(config.block_size, 256);
assert!(config.enable_select_acceleration);
assert_eq!(config.select_sample_rate, 512);
assert!(config.enable_hardware_acceleration);
}
#[test]
fn test_config_builder() {
let config = SeparatedStorageConfig::new()
.block_size(512)
.enable_select_acceleration(false)
.superblock_size(32)
.build();
assert_eq!(config.block_size, 512);
assert!(!config.enable_select_acceleration);
assert_eq!(config.superblock_size, 32);
}
#[test]
fn test_high_performance_preset() {
let config = SeparatedStorageConfig::high_performance().build();
assert_eq!(config.block_size, 512);
assert!(config.enable_select_acceleration);
assert_eq!(config.memory_strategy, MemoryStrategy::MaximizePerformance);
}
#[test]
fn test_low_memory_preset() {
let config = SeparatedStorageConfig::low_memory().build();
assert_eq!(config.block_size, 1024);
assert!(!config.enable_select_acceleration);
assert_eq!(config.memory_strategy, MemoryStrategy::MinimizeSpace);
}
#[test]
fn test_multi_dimensional_config() {
let config = SeparatedStorageConfig::multi_dimensional(3).build();
assert!(config.multi_dimensional.is_some());
let multi = config.multi_dimensional.unwrap();
assert_eq!(multi.arity, 3);
assert_eq!(multi.dimension_hints.len(), 3);
}
#[test]
fn test_config_validation() {
let valid_config = SeparatedStorageConfig::default();
assert!(valid_config.validate().is_ok());
let mut invalid_config = SeparatedStorageConfig::default();
invalid_config.block_size = 123;
assert!(invalid_config.validate().is_err());
let mut invalid_config = SeparatedStorageConfig::default();
invalid_config.enable_bit_packed_ranks = true;
invalid_config.relative_rank_bits = 15;
assert!(invalid_config.validate().is_err());
}
#[test]
fn test_memory_overhead_estimation() {
let base_config = SeparatedStorageConfig::default();
let base_overhead = base_config.estimated_memory_overhead();
let large_block_config = SeparatedStorageConfig::new().block_size(1024).build();
assert!(large_block_config.estimated_memory_overhead() < base_overhead);
let bit_packed_config = SeparatedStorageConfig::new()
.enable_bit_packed_ranks(true)
.build();
assert!(bit_packed_config.estimated_memory_overhead() < base_overhead);
}
}