mirror of
https://github.com/zama-ai/concrete.git
synced 2026-02-09 03:55:04 -05:00
feat(cpp): option to have a stateless cache
This commit is contained in:
@@ -43,7 +43,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
complexity_model: &CpuComplexity::default(),
|
||||
};
|
||||
|
||||
let cache = decomposition::cache(security_level, processing_unit, None);
|
||||
let cache = decomposition::cache(security_level, processing_unit, None, true);
|
||||
|
||||
let solutions: Vec<_> = log_norm2s
|
||||
.clone()
|
||||
|
||||
@@ -43,7 +43,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
complexity_model: &CpuComplexity::default(),
|
||||
};
|
||||
|
||||
let cache = decomposition::cache(security_level, processing_unit, None);
|
||||
let cache = decomposition::cache(security_level, processing_unit, None, true);
|
||||
|
||||
let solutions: Vec<_> = precisions
|
||||
.clone()
|
||||
|
||||
@@ -10,6 +10,7 @@ use concrete_optimizer::optimization::dag::solo_key::optimize_generic::{
|
||||
Encoding, Solution as DagSolution,
|
||||
};
|
||||
use concrete_optimizer::optimization::decomposition;
|
||||
use concrete_optimizer::utils::cache::persistent::default_cache_dir;
|
||||
|
||||
fn no_solution() -> ffi::Solution {
|
||||
ffi::Solution {
|
||||
@@ -25,6 +26,21 @@ fn no_dag_solution() -> ffi::DagSolution {
|
||||
}
|
||||
}
|
||||
|
||||
fn caches_from(options: ffi::Options) -> decomposition::PersistDecompCaches {
|
||||
if !options.cache_on_disk {
|
||||
println!("optimizer: Using stateless cache.");
|
||||
let cache_dir = default_cache_dir();
|
||||
println!("optimizer: To clear the cache, remove directory {cache_dir}");
|
||||
}
|
||||
let processing_unit = processing_unit(options);
|
||||
decomposition::cache(
|
||||
options.security_level,
|
||||
processing_unit,
|
||||
Some(ProcessingUnit::Cpu.complexity_model()),
|
||||
options.cache_on_disk,
|
||||
)
|
||||
}
|
||||
|
||||
fn optimize_bootstrap(precision: u64, noise_factor: f64, options: ffi::Options) -> ffi::Solution {
|
||||
let processing_unit = processing_unit(options);
|
||||
|
||||
@@ -45,11 +61,7 @@ fn optimize_bootstrap(precision: u64, noise_factor: f64, options: ffi::Options)
|
||||
config,
|
||||
noise_factor,
|
||||
&search_space,
|
||||
&decomposition::cache(
|
||||
options.security_level,
|
||||
processing_unit,
|
||||
Some(ProcessingUnit::Cpu.complexity_model()),
|
||||
),
|
||||
&caches_from(options),
|
||||
);
|
||||
result
|
||||
.best_solution
|
||||
@@ -232,11 +244,7 @@ impl OperationDag {
|
||||
&self.0,
|
||||
config,
|
||||
&search_space,
|
||||
&decomposition::cache(
|
||||
options.security_level,
|
||||
processing_unit,
|
||||
Some(ProcessingUnit::Cpu.complexity_model()),
|
||||
),
|
||||
&caches_from(options),
|
||||
);
|
||||
result
|
||||
.best_solution
|
||||
@@ -253,11 +261,7 @@ impl OperationDag {
|
||||
};
|
||||
|
||||
let search_space = SearchSpace::default(processing_unit);
|
||||
let cache = decomposition::cache(
|
||||
options.security_level,
|
||||
processing_unit,
|
||||
Some(ProcessingUnit::Cpu.complexity_model()),
|
||||
);
|
||||
|
||||
let encoding = options.encoding.into();
|
||||
let result = concrete_optimizer::optimization::dag::solo_key::optimize_generic::optimize(
|
||||
&self.0,
|
||||
@@ -265,7 +269,7 @@ impl OperationDag {
|
||||
&search_space,
|
||||
encoding,
|
||||
options.default_log_norm2_woppbs,
|
||||
&cache,
|
||||
&caches_from(options),
|
||||
);
|
||||
result.map_or_else(no_dag_solution, |solution| solution.into())
|
||||
}
|
||||
@@ -430,6 +434,7 @@ mod ffi {
|
||||
pub default_log_norm2_woppbs: f64,
|
||||
pub use_gpu_constraints: bool,
|
||||
pub encoding: Encoding,
|
||||
pub cache_on_disk: bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1064,6 +1064,7 @@ struct Options final {
|
||||
double default_log_norm2_woppbs;
|
||||
bool use_gpu_constraints;
|
||||
::concrete_optimizer::Encoding encoding;
|
||||
bool cache_on_disk;
|
||||
|
||||
using IsRelocatable = ::std::true_type;
|
||||
};
|
||||
|
||||
@@ -1045,6 +1045,7 @@ struct Options final {
|
||||
double default_log_norm2_woppbs;
|
||||
bool use_gpu_constraints;
|
||||
::concrete_optimizer::Encoding encoding;
|
||||
bool cache_on_disk;
|
||||
|
||||
using IsRelocatable = ::std::true_type;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,8 @@ concrete_optimizer::Options default_options() {
|
||||
.maximum_acceptable_error_probability = P_ERROR,
|
||||
.default_log_norm2_woppbs = WOP_FALLBACK_LOG_NORM,
|
||||
.use_gpu_constraints = false,
|
||||
.encoding = concrete_optimizer::Encoding::Auto
|
||||
.encoding = concrete_optimizer::Encoding::Auto,
|
||||
.cache_on_disk = true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ mod tests {
|
||||
|
||||
static SHARED_CACHES: Lazy<PersistDecompCaches> = Lazy::new(|| {
|
||||
let processing_unit = config::ProcessingUnit::Cpu;
|
||||
decomposition::cache(128, processing_unit, None)
|
||||
decomposition::cache(128, processing_unit, None, true)
|
||||
});
|
||||
|
||||
fn optimize(dag: &unparametrized::OperationDag) -> OptimizationState {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::computing_cost::complexity_model::ComplexityModel;
|
||||
use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern;
|
||||
use crate::parameters::{BrDecompositionParameters, GlweParameters, LweDimension, PbsParameters};
|
||||
use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache};
|
||||
use crate::utils::cache::persistent::PersistentCacheHashMap;
|
||||
use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap};
|
||||
use crate::{config, security};
|
||||
|
||||
use super::common::{MacroParam, VERSION};
|
||||
@@ -115,18 +115,12 @@ pub fn cache(
|
||||
processing_unit: config::ProcessingUnit,
|
||||
complexity_model: Arc<dyn ComplexityModel>,
|
||||
) -> PersistDecompCache {
|
||||
let max_log2_base = processing_unit.max_br_base_log();
|
||||
|
||||
let cache_dir: String = default_cache_dir();
|
||||
let ciphertext_modulus_log = 64;
|
||||
let tmp: String = std::env::temp_dir()
|
||||
.to_str()
|
||||
.expect("Invalid tmp dir")
|
||||
.into();
|
||||
|
||||
let hardware = processing_unit.br_to_string();
|
||||
let path = format!("{cache_dir}/br-decomp-{hardware}-64-{security_level}");
|
||||
|
||||
let path = format!("{tmp}/optimizer/cache/br-decomp-{hardware}-64-{security_level}");
|
||||
|
||||
let max_log2_base = processing_unit.max_br_base_log();
|
||||
let function = move |(glwe_params, internal_dim): MacroParam| {
|
||||
pareto_quantities(
|
||||
complexity_model.as_ref(),
|
||||
@@ -137,5 +131,5 @@ pub fn cache(
|
||||
max_log2_base,
|
||||
)
|
||||
};
|
||||
PersistentCacheHashMap::new(&path, VERSION, function)
|
||||
PersistentCacheHashMap::new_no_read(&path, VERSION, function)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::config;
|
||||
use crate::noise_estimator::operators::atomic_pattern::variance_cmux;
|
||||
use crate::parameters::{BrDecompositionParameters, CmuxParameters, GlweParameters};
|
||||
use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache};
|
||||
use crate::utils::cache::persistent::PersistentCacheHashMap;
|
||||
use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap};
|
||||
use crate::utils::square;
|
||||
|
||||
use super::common::VERSION;
|
||||
@@ -97,15 +97,11 @@ pub fn cache(
|
||||
processing_unit: config::ProcessingUnit,
|
||||
complexity_model: Arc<dyn ComplexityModel>,
|
||||
) -> PersistDecompCache {
|
||||
let cache_dir: String = default_cache_dir();
|
||||
let ciphertext_modulus_log = 64;
|
||||
let tmp: String = std::env::temp_dir()
|
||||
.to_str()
|
||||
.expect("Invalid tmp dir")
|
||||
.into();
|
||||
let hardware = processing_unit.br_to_string();
|
||||
let path = format!(
|
||||
"{tmp}/optimizer/cache/cb-decomp-{hardware}-{ciphertext_modulus_log}-{security_level}"
|
||||
);
|
||||
let path =
|
||||
format!("{cache_dir}/cb-decomp-{hardware}-{ciphertext_modulus_log}-{security_level}");
|
||||
let function = move |glwe_params| {
|
||||
pareto_quantities(
|
||||
complexity_model.as_ref(),
|
||||
@@ -113,5 +109,5 @@ pub fn cache(
|
||||
glwe_params,
|
||||
)
|
||||
};
|
||||
PersistentCacheHashMap::new(&path, VERSION, function)
|
||||
PersistentCacheHashMap::new_no_read(&path, VERSION, function)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::parameters::{
|
||||
GlweParameters, KeyswitchParameters, KsDecompositionParameters, LweDimension,
|
||||
};
|
||||
use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache};
|
||||
use crate::utils::cache::persistent::PersistentCacheHashMap;
|
||||
use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap};
|
||||
|
||||
use super::common::{MacroParam, VERSION};
|
||||
|
||||
@@ -118,15 +118,10 @@ pub fn cache(
|
||||
processing_unit: config::ProcessingUnit,
|
||||
complexity_model: Arc<dyn ComplexityModel>,
|
||||
) -> PersistDecompCache {
|
||||
let cache_dir: String = default_cache_dir();
|
||||
let ciphertext_modulus_log = 64;
|
||||
let tmp: String = std::env::temp_dir()
|
||||
.to_str()
|
||||
.expect("Invalid tmp dir")
|
||||
.into();
|
||||
|
||||
let hardware = processing_unit.ks_to_string();
|
||||
|
||||
let path = format!("{tmp}/optimizer/cache/ks-decomp-{hardware}-64-{security_level}");
|
||||
let path = format!("{cache_dir}/ks-decomp-{hardware}-64-{security_level}");
|
||||
|
||||
let function = move |(glwe_params, internal_dim): MacroParam| {
|
||||
pareto_quantities(
|
||||
@@ -137,5 +132,5 @@ pub fn cache(
|
||||
glwe_params,
|
||||
)
|
||||
};
|
||||
PersistentCacheHashMap::new(&path, VERSION, function)
|
||||
PersistentCacheHashMap::new_no_read(&path, VERSION, function)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ pub struct PersistDecompCaches {
|
||||
pub br: blind_rotate::PersistDecompCache,
|
||||
pub pp: pp_switch::PersistDecompCache,
|
||||
pub cb: circuit_bootstrap::PersistDecompCache,
|
||||
pub cache_on_disk: bool,
|
||||
}
|
||||
|
||||
pub struct DecompCaches {
|
||||
@@ -29,8 +30,14 @@ pub fn cache(
|
||||
security_level: u64,
|
||||
processing_unit: config::ProcessingUnit,
|
||||
complexity_model: Option<Arc<dyn ComplexityModel>>,
|
||||
cache_on_disk: bool,
|
||||
) -> PersistDecompCaches {
|
||||
PersistDecompCaches::new(security_level, processing_unit, complexity_model)
|
||||
PersistDecompCaches::new(
|
||||
security_level,
|
||||
processing_unit,
|
||||
complexity_model,
|
||||
cache_on_disk,
|
||||
)
|
||||
}
|
||||
|
||||
impl PersistDecompCaches {
|
||||
@@ -38,18 +45,30 @@ impl PersistDecompCaches {
|
||||
security_level: u64,
|
||||
processing_unit: config::ProcessingUnit,
|
||||
complexity_model: Option<Arc<dyn ComplexityModel>>,
|
||||
cache_on_disk: bool,
|
||||
) -> Self {
|
||||
let complexity_model =
|
||||
complexity_model.unwrap_or_else(|| processing_unit.complexity_model());
|
||||
Self {
|
||||
let res = Self {
|
||||
ks: keyswitch::cache(security_level, processing_unit, complexity_model.clone()),
|
||||
br: blind_rotate::cache(security_level, processing_unit, complexity_model.clone()),
|
||||
pp: pp_switch::cache(security_level, processing_unit, complexity_model.clone()),
|
||||
cb: circuit_bootstrap::cache(security_level, processing_unit, complexity_model.clone()),
|
||||
cache_on_disk,
|
||||
};
|
||||
if cache_on_disk {
|
||||
res.ks.read();
|
||||
res.br.read();
|
||||
res.pp.read();
|
||||
res.cb.read();
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn backport(&self, cache: DecompCaches) {
|
||||
if !self.cache_on_disk {
|
||||
return;
|
||||
}
|
||||
self.ks.backport(cache.keyswitch);
|
||||
self.br.backport(cache.blind_rotate);
|
||||
self.pp.backport(cache.pp_switch);
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::parameters::{
|
||||
KsDecompositionParameters, LweDimension,
|
||||
};
|
||||
use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache};
|
||||
use crate::utils::cache::persistent::PersistentCacheHashMap;
|
||||
use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap};
|
||||
use crate::{config, security};
|
||||
|
||||
use super::blind_rotate;
|
||||
@@ -90,15 +90,12 @@ pub fn cache(
|
||||
processing_unit: config::ProcessingUnit,
|
||||
complexity_model: Arc<dyn ComplexityModel>,
|
||||
) -> PersistDecompCache {
|
||||
let max_log2_base = processing_unit.max_br_base_log();
|
||||
let cache_dir: String = default_cache_dir();
|
||||
let ciphertext_modulus_log = 64;
|
||||
let tmp: String = std::env::temp_dir()
|
||||
.to_str()
|
||||
.expect("Invalid tmp dir")
|
||||
.into();
|
||||
let hardware = processing_unit.br_to_string();
|
||||
let path = format!("{tmp}/optimizer/cache/bc-decomp-{hardware}-64-{security_level}");
|
||||
let path = format!("{cache_dir}/bc-decomp-{hardware}-64-{security_level}");
|
||||
|
||||
let max_log2_base = processing_unit.max_br_base_log();
|
||||
let function = move |(glwe_params, internal_dim): MacroParam| {
|
||||
let br = blind_rotate::pareto_quantities(
|
||||
complexity_model.as_ref(),
|
||||
@@ -117,5 +114,5 @@ pub fn cache(
|
||||
&br,
|
||||
)
|
||||
};
|
||||
PersistentCacheHashMap::new(&path, VERSION, function)
|
||||
PersistentCacheHashMap::new_no_read(&path, VERSION, function)
|
||||
}
|
||||
|
||||
44
concrete-optimizer/src/utils/cache/persistent.rs
vendored
44
concrete-optimizer/src/utils/cache/persistent.rs
vendored
@@ -50,18 +50,18 @@ where
|
||||
version: u64,
|
||||
function: impl 'static + Send + Sync + Fn(ROC::K) -> ROC::V,
|
||||
) -> Self {
|
||||
let t0 = Instant::now();
|
||||
let content = Self::read_from_disk(path, version).unwrap_or_default();
|
||||
if SHOW_DISK_ACCESS {
|
||||
println!(
|
||||
"PersistentCache: {}, reading time {} msec, {} entries",
|
||||
path,
|
||||
t0.elapsed().as_millis(),
|
||||
content.len()
|
||||
);
|
||||
}
|
||||
let cache = Self::new_no_read(path, version, function);
|
||||
cache.read();
|
||||
cache
|
||||
}
|
||||
|
||||
pub fn new_no_read(
|
||||
path: &str,
|
||||
version: u64,
|
||||
function: impl 'static + Send + Sync + Fn(ROC::K) -> ROC::V,
|
||||
) -> Self {
|
||||
let path = path.into();
|
||||
let content = RwLock::new(Arc::new(content));
|
||||
let content = RwLock::new(Arc::new(ROC::default()));
|
||||
let content_changed = AtomicBool::new(false);
|
||||
Self {
|
||||
path,
|
||||
@@ -72,6 +72,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self) {
|
||||
let t0 = Instant::now();
|
||||
let content = Self::read_from_disk(&self.path, self.version).unwrap_or_default();
|
||||
if SHOW_DISK_ACCESS {
|
||||
println!(
|
||||
"PersistentCache: {}, reading time {} msec, {} entries",
|
||||
self.path,
|
||||
t0.elapsed().as_millis(),
|
||||
content.len()
|
||||
);
|
||||
}
|
||||
self.update_with(|_| content);
|
||||
self.content_changed.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn cache(&self) -> ephemeral::Cache<ROC> {
|
||||
let initial_content = self.content.read().unwrap().clone();
|
||||
ephemeral::Cache::<ROC>::new(initial_content, self.function.clone())
|
||||
@@ -269,6 +284,13 @@ where
|
||||
|
||||
pub type PersistentCacheHashMap<K, V> = PersistentCache<Map<K, V>>;
|
||||
|
||||
pub fn default_cache_dir() -> String {
|
||||
let mut cache_dir = std::env::temp_dir();
|
||||
cache_dir.push("optimizer");
|
||||
cache_dir.push("cache");
|
||||
cache_dir.to_str().expect("Invalid tmp dir").into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::ephemeral::CacheHashMap;
|
||||
|
||||
@@ -17,6 +17,7 @@ fn pbs_benchmark(c: &mut Criterion) {
|
||||
no_parallelize: true,
|
||||
wop_pbs: false,
|
||||
simulate_dag: true,
|
||||
cache_on_disk: true,
|
||||
};
|
||||
|
||||
c.bench_function("PBS table generation", |b| {
|
||||
@@ -40,6 +41,7 @@ fn wop_pbs_benchmark(c: &mut Criterion) {
|
||||
no_parallelize: true,
|
||||
wop_pbs: true,
|
||||
simulate_dag: false,
|
||||
cache_on_disk: true,
|
||||
};
|
||||
|
||||
c.bench_function("WoP-PBS table generation", |b| {
|
||||
|
||||
@@ -36,6 +36,7 @@ pub const MAX_LWE_DIM: u64 = DEFAUT_DOMAINS.free_glwe.glwe_dimension.end - 1;
|
||||
/// Find parameters for classical PBS and new WoP-PBS
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Args {
|
||||
#[clap(long, default_value_t = 1, help = "1..16")]
|
||||
pub min_precision: u64,
|
||||
@@ -82,6 +83,9 @@ pub struct Args {
|
||||
|
||||
#[clap(long)]
|
||||
pub simulate_dag: bool,
|
||||
|
||||
#[clap(long, default_value_t = true)]
|
||||
pub cache_on_disk: bool,
|
||||
}
|
||||
|
||||
pub fn all_results(args: &Args) -> Vec<Vec<Option<Solution>>> {
|
||||
@@ -89,6 +93,7 @@ pub fn all_results(args: &Args) -> Vec<Vec<Option<Solution>>> {
|
||||
let sum_size = args.sum_size;
|
||||
let maximum_acceptable_error_probability = args.p_error;
|
||||
let security_level = args.security_level;
|
||||
let cache_on_disk = args.cache_on_disk;
|
||||
|
||||
let search_space = SearchSpace {
|
||||
glwe_log_polynomial_sizes: (args.min_log_poly_size..=args.max_log_poly_size).collect(),
|
||||
@@ -110,7 +115,7 @@ pub fn all_results(args: &Args) -> Vec<Vec<Option<Solution>>> {
|
||||
complexity_model: &CpuComplexity::default(),
|
||||
};
|
||||
|
||||
let cache = decomposition::cache(config.security_level, processing_unit, None);
|
||||
let cache = decomposition::cache(security_level, processing_unit, None, cache_on_disk);
|
||||
|
||||
precisions_iter
|
||||
.map(|precision| {
|
||||
@@ -272,6 +277,7 @@ mod tests {
|
||||
no_parallelize: false,
|
||||
wop_pbs: false,
|
||||
simulate_dag,
|
||||
cache_on_disk: true,
|
||||
};
|
||||
|
||||
let mut actual_output = Vec::<u8>::new();
|
||||
@@ -313,6 +319,7 @@ mod tests {
|
||||
no_parallelize: false,
|
||||
wop_pbs: true,
|
||||
simulate_dag: false,
|
||||
cache_on_disk: true,
|
||||
};
|
||||
|
||||
let mut actual_output = Vec::<u8>::new();
|
||||
|
||||
Reference in New Issue
Block a user