diff --git a/charts/src/bin/norm2_complexity.rs b/charts/src/bin/norm2_complexity.rs index 8d918fc7a..123bf5b79 100644 --- a/charts/src/bin/norm2_complexity.rs +++ b/charts/src/bin/norm2_complexity.rs @@ -43,7 +43,7 @@ fn main() -> Result<(), Box> { 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() diff --git a/charts/src/bin/precision_complexity.rs b/charts/src/bin/precision_complexity.rs index 02af57c9b..cd0d5210f 100644 --- a/charts/src/bin/precision_complexity.rs +++ b/charts/src/bin/precision_complexity.rs @@ -43,7 +43,7 @@ fn main() -> Result<(), Box> { 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() diff --git a/concrete-optimizer-cpp/src/concrete-optimizer.rs b/concrete-optimizer-cpp/src/concrete-optimizer.rs index e90334620..9b03da795 100644 --- a/concrete-optimizer-cpp/src/concrete-optimizer.rs +++ b/concrete-optimizer-cpp/src/concrete-optimizer.rs @@ -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, } } diff --git a/concrete-optimizer-cpp/src/cpp/concrete-optimizer.cpp b/concrete-optimizer-cpp/src/cpp/concrete-optimizer.cpp index 02543d865..d8cecdfbc 100644 --- a/concrete-optimizer-cpp/src/cpp/concrete-optimizer.cpp +++ b/concrete-optimizer-cpp/src/cpp/concrete-optimizer.cpp @@ -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; }; diff --git a/concrete-optimizer-cpp/src/cpp/concrete-optimizer.hpp b/concrete-optimizer-cpp/src/cpp/concrete-optimizer.hpp index 1f86726ae..13bd8a78c 100644 --- a/concrete-optimizer-cpp/src/cpp/concrete-optimizer.hpp +++ b/concrete-optimizer-cpp/src/cpp/concrete-optimizer.hpp @@ -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; }; diff --git a/concrete-optimizer-cpp/tests/src/main.cpp b/concrete-optimizer-cpp/tests/src/main.cpp index c49d942cf..7387a46ce 100644 --- a/concrete-optimizer-cpp/tests/src/main.cpp +++ b/concrete-optimizer-cpp/tests/src/main.cpp @@ -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, }; } diff --git a/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs b/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs index 1c09b1612..f1fc401ea 100644 --- a/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs +++ b/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs @@ -392,7 +392,7 @@ mod tests { static SHARED_CACHES: Lazy = 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 { diff --git a/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs b/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs index ce90dd25b..2bbd0ec1d 100644 --- a/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs +++ b/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs @@ -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, ) -> 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) } diff --git a/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs b/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs index bced23b8a..7324352fe 100644 --- a/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs +++ b/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs @@ -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, ) -> 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) } diff --git a/concrete-optimizer/src/optimization/decomposition/keyswitch.rs b/concrete-optimizer/src/optimization/decomposition/keyswitch.rs index 15c60ed13..bc0e9c0b2 100644 --- a/concrete-optimizer/src/optimization/decomposition/keyswitch.rs +++ b/concrete-optimizer/src/optimization/decomposition/keyswitch.rs @@ -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, ) -> 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) } diff --git a/concrete-optimizer/src/optimization/decomposition/mod.rs b/concrete-optimizer/src/optimization/decomposition/mod.rs index e031459f3..e457682d7 100644 --- a/concrete-optimizer/src/optimization/decomposition/mod.rs +++ b/concrete-optimizer/src/optimization/decomposition/mod.rs @@ -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>, + 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>, + 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); diff --git a/concrete-optimizer/src/optimization/decomposition/pp_switch.rs b/concrete-optimizer/src/optimization/decomposition/pp_switch.rs index 303dc1ae3..be7ef6043 100644 --- a/concrete-optimizer/src/optimization/decomposition/pp_switch.rs +++ b/concrete-optimizer/src/optimization/decomposition/pp_switch.rs @@ -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, ) -> 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) } diff --git a/concrete-optimizer/src/utils/cache/persistent.rs b/concrete-optimizer/src/utils/cache/persistent.rs index e90c8bf27..008ea1c4c 100644 --- a/concrete-optimizer/src/utils/cache/persistent.rs +++ b/concrete-optimizer/src/utils/cache/persistent.rs @@ -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 { let initial_content = self.content.read().unwrap().clone(); ephemeral::Cache::::new(initial_content, self.function.clone()) @@ -269,6 +284,13 @@ where pub type PersistentCacheHashMap = PersistentCache>; +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; diff --git a/v0-parameters/benches/benchmark.rs b/v0-parameters/benches/benchmark.rs index 84c799f3e..a57ef558c 100644 --- a/v0-parameters/benches/benchmark.rs +++ b/v0-parameters/benches/benchmark.rs @@ -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| { diff --git a/v0-parameters/src/lib.rs b/v0-parameters/src/lib.rs index 8f8c18f17..39f94d850 100644 --- a/v0-parameters/src/lib.rs +++ b/v0-parameters/src/lib.rs @@ -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>> { @@ -89,6 +93,7 @@ pub fn all_results(args: &Args) -> Vec>> { 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>> { 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::::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::::new();