From 8075e0a00490a60b100a287967eac90ce87fd587 Mon Sep 17 00:00:00 2001 From: Joey Beauvais-Feisthauer <57161378+JoeyBF@users.noreply.github.com> Date: Fri, 24 Nov 2023 23:38:11 -0500 Subject: [PATCH] Clean up gating on `concurrent` feature (#140) * Introduce `maybe-rayon` crate * Adapt rest of codebase --- ext/Cargo.toml | 6 +- ext/crates/algebra/Cargo.toml | 5 +- .../algebra/src/module/homomorphism/mod.rs | 29 ++--- ext/crates/fp/Cargo.toml | 4 +- ext/crates/fp/src/matrix/matrix_inner.rs | 18 ++-- ext/crates/maybe-rayon/Cargo.toml | 13 +++ ext/crates/maybe-rayon/src/concurrent.rs | 64 +++++++++++ ext/crates/maybe-rayon/src/lib.rs | 9 ++ ext/crates/maybe-rayon/src/sequential.rs | 78 ++++++++++++++ ext/crates/once/Cargo.toml | 5 +- ext/crates/once/src/lib.rs | 31 +++--- ext/crates/sseq/Cargo.toml | 4 +- ext/crates/sseq/src/coordinates/mod.rs | 17 ++- ext/examples/secondary_massey.rs | 26 +---- ext/src/chain_complex/chain_homotopy.rs | 40 ++----- ext/src/nassau.rs | 18 +--- ext/src/resolution.rs | 34 +----- ext/src/resolution_homomorphism.rs | 23 +--- ext/src/secondary.rs | 102 +++++------------- 19 files changed, 264 insertions(+), 262 deletions(-) create mode 100644 ext/crates/maybe-rayon/Cargo.toml create mode 100644 ext/crates/maybe-rayon/src/concurrent.rs create mode 100644 ext/crates/maybe-rayon/src/lib.rs create mode 100644 ext/crates/maybe-rayon/src/sequential.rs diff --git a/ext/Cargo.toml b/ext/Cargo.toml index 9d677dd26..afdc3fa5c 100644 --- a/ext/Cargo.toml +++ b/ext/Cargo.toml @@ -19,6 +19,7 @@ algebra = { path = "crates/algebra", default-features = false, features = [ bivec = { path = "crates/bivec" } chart = { path = "crates/chart" } fp = { path = "crates/fp", default-features = false } +maybe-rayon = { path = "crates/maybe-rayon" } once = { path = "crates/once" } query = { path = "crates/query" } sseq = { path = "crates/sseq", default-features = false } @@ -30,7 +31,6 @@ dashmap = "4.0.0" itertools = { version = "0.10.0", default-features = false, features = [ "use_alloc", ] } -rayon = { version = "1.5", optional = true } rustc-hash = "1.1.0" serde_json = { version = "1.0.0", features = ["preserve_order"] } @@ -54,9 +54,9 @@ cache-multiplication = ["algebra/cache-multiplication"] concurrent = [ "algebra/concurrent", "fp/concurrent", + "maybe-rayon/concurrent", "once/concurrent", "sseq/concurrent", - "rayon", ] odd-primes = ["fp/odd-primes", "algebra/odd-primes", "sseq/odd-primes"] logging = [] @@ -68,7 +68,9 @@ members = [ "crates/bivec", "crates/chart", "crates/fp", + "crates/maybe-rayon", "crates/once", + "crates/query", "crates/sseq", ] diff --git a/ext/crates/algebra/Cargo.toml b/ext/crates/algebra/Cargo.toml index 1f54e50d0..59504e562 100644 --- a/ext/crates/algebra/Cargo.toml +++ b/ext/crates/algebra/Cargo.toml @@ -13,6 +13,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] bivec = { path = "../bivec" } fp = { path = "../fp", default-features = false } +maybe-rayon = { path = "../maybe-rayon" } once = { path = "../once" } anyhow = "1.0.0" @@ -26,8 +27,6 @@ rustc-hash = "1.1.0" serde = { version = "1.0.0", features = ["derive"], optional = true } serde_json = { version = "1.0.0", optional = true } -rayon = { version = "1.5", optional = true } - [dev-dependencies] bencher = "0.1.5" expect-test = "1.1.0" @@ -36,7 +35,7 @@ rstest = "0.17.0" [features] default = ["odd-primes", "json"] cache-multiplication = [] -concurrent = ["rayon", "fp/concurrent"] +concurrent = ["fp/concurrent", "maybe-rayon/concurrent"] json = ["serde", "serde_json", "bivec/json", "fp/json"] odd-primes = ["fp/odd-primes"] diff --git a/ext/crates/algebra/src/module/homomorphism/mod.rs b/ext/crates/algebra/src/module/homomorphism/mod.rs index e610d35c1..733c5f532 100644 --- a/ext/crates/algebra/src/module/homomorphism/mod.rs +++ b/ext/crates/algebra/src/module/homomorphism/mod.rs @@ -5,9 +5,6 @@ use fp::matrix::{AugmentedMatrix, Matrix, MatrixSliceMut, QuasiInverse, Subspace use fp::prime::ValidPrime; use fp::vector::{Slice, SliceMut}; -#[cfg(feature = "concurrent")] -use rayon::prelude::*; - mod free_module_homomorphism; mod full_module_homomorphism; mod generic_zero_homomorphism; @@ -22,6 +19,9 @@ pub use generic_zero_homomorphism::GenericZeroHomomorphism; pub use hom_pullback::HomPullback; pub use quotient_homomorphism::{QuotientHomomorphism, QuotientHomomorphismSource}; +#[allow(unused_imports)] +use maybe_rayon::prelude::*; + /// Each `ModuleHomomorphism` may come with auxiliary data, namely the kernel, image and /// quasi_inverse at each degree (the quasi-inverse is a map that is a right inverse when /// restricted to the image). These are computed via @@ -122,14 +122,8 @@ pub trait ModuleHomomorphism: Send + Sync { return; } - #[cfg(not(feature = "concurrent"))] - for (i, row) in matrix.iter_mut().enumerate() { - self.apply_to_basis_element(row, 1, degree, i); - } - - #[cfg(feature = "concurrent")] matrix - .par_iter_mut() + .maybe_par_iter_mut() .enumerate() .for_each(|(i, row)| self.apply_to_basis_element(row, 1, degree, i)); } @@ -142,15 +136,12 @@ pub trait ModuleHomomorphism: Send + Sync { return matrix; } - #[cfg(not(feature = "concurrent"))] - for (row, &v) in matrix.iter_mut().zip(inputs) { - self.apply_to_basis_element(row.as_slice_mut(), 1, degree, v); - } - - #[cfg(feature = "concurrent")] - matrix.par_iter_mut().enumerate().for_each(|(i, row)| { - self.apply_to_basis_element(row.as_slice_mut(), 1, degree, inputs[i]) - }); + matrix + .maybe_par_iter_mut() + .enumerate() + .for_each(|(i, row)| { + self.apply_to_basis_element(row.as_slice_mut(), 1, degree, inputs[i]) + }); matrix } diff --git a/ext/crates/fp/Cargo.toml b/ext/crates/fp/Cargo.toml index 30cb2209c..0fa19f0cd 100644 --- a/ext/crates/fp/Cargo.toml +++ b/ext/crates/fp/Cargo.toml @@ -13,7 +13,7 @@ itertools = { version = "0.10.0", default-features = false } serde = { version = "1.0.0", optional = true } serde_json = { version = "1.0.0", optional = true } -rayon = { version = "1.5", optional = true } +maybe-rayon = { path = "../maybe-rayon" } [dev-dependencies] criterion = { version = "0.3.5", features = ["html_reports"] } @@ -28,7 +28,7 @@ build_const = "0.2.2" [features] default = ["odd-primes"] -concurrent = ["rayon"] +concurrent = ["maybe-rayon/concurrent"] json = ["serde_json", "serde"] odd-primes = [] diff --git a/ext/crates/fp/src/matrix/matrix_inner.rs b/ext/crates/fp/src/matrix/matrix_inner.rs index a92c97184..7a703bdba 100644 --- a/ext/crates/fp/src/matrix/matrix_inner.rs +++ b/ext/crates/fp/src/matrix/matrix_inner.rs @@ -8,8 +8,8 @@ use std::io::{Read, Write}; use std::ops::{Index, IndexMut}; use itertools::Itertools; -#[cfg(feature = "concurrent")] -use rayon::prelude::*; + +use maybe_rayon::prelude::*; /// A matrix! In particular, a matrix with values in F_p. The way we store matrices means it is /// easier to perform row operations than column operations, and the way we use matrices means we @@ -281,9 +281,10 @@ impl Matrix { self.vectors.iter_mut() } - #[cfg(feature = "concurrent")] - pub fn par_iter_mut(&mut self) -> impl IndexedParallelIterator + '_ { - self.vectors.par_iter_mut() + pub fn maybe_par_iter_mut( + &mut self, + ) -> impl MaybeIndexedParallelIterator + '_ { + self.vectors.maybe_par_iter_mut() } } @@ -1160,12 +1161,13 @@ impl<'a> MatrixSliceMut<'a> { .map(move |x| x.slice_mut(start, end)) } - #[cfg(feature = "concurrent")] - pub fn par_iter_mut(&mut self) -> impl IndexedParallelIterator + '_ { + pub fn maybe_par_iter_mut( + &mut self, + ) -> impl MaybeIndexedParallelIterator + '_ { let start = self.col_start; let end = self.col_end; self.vectors - .par_iter_mut() + .maybe_par_iter_mut() .map(move |x| x.slice_mut(start, end)) } diff --git a/ext/crates/maybe-rayon/Cargo.toml b/ext/crates/maybe-rayon/Cargo.toml new file mode 100644 index 000000000..a49c3c0eb --- /dev/null +++ b/ext/crates/maybe-rayon/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "maybe-rayon" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rayon = { version = "1.8.0", optional = true } + +[features] +default = [] +concurrent = ["rayon"] diff --git a/ext/crates/maybe-rayon/src/concurrent.rs b/ext/crates/maybe-rayon/src/concurrent.rs new file mode 100644 index 000000000..c1c696096 --- /dev/null +++ b/ext/crates/maybe-rayon/src/concurrent.rs @@ -0,0 +1,64 @@ +pub mod prelude { + use rayon::prelude::*; + + pub use rayon::iter::{IndexedParallelIterator, ParallelIterator}; + + pub trait MaybeParallelIterator: ParallelIterator {} + + pub trait MaybeIndexedParallelIterator: IndexedParallelIterator {} + + pub trait MaybeIntoParallelIterator: IntoParallelIterator { + fn maybe_into_par_iter(self) -> Self::Iter; + } + + pub trait MaybeIntoParallelRefMutIterator<'data>: IntoParallelRefMutIterator<'data> { + fn maybe_par_iter_mut(&'data mut self) -> Self::Iter; + } + + // Implementations + + impl MaybeParallelIterator for I {} + + impl MaybeIndexedParallelIterator for I {} + + impl MaybeIntoParallelIterator for I { + fn maybe_into_par_iter(self) -> Self::Iter { + self.into_par_iter() + } + } + + impl<'data, I: IntoParallelRefMutIterator<'data> + ?Sized> + MaybeIntoParallelRefMutIterator<'data> for I + { + fn maybe_par_iter_mut(&'data mut self) -> Self::Iter { + self.par_iter_mut() + } + } +} + +pub fn join(oper_a: A, oper_b: B) -> (RA, RB) +where + A: FnOnce() -> RA + Send, + B: FnOnce() -> RB + Send, + RA: Send, + RB: Send, +{ + rayon::join(oper_a, oper_b) +} + +pub type Scope<'scope> = rayon::Scope<'scope>; + +pub fn scope<'scope, OP, R>(op: OP) -> R +where + OP: FnOnce(&Scope<'scope>) -> R + Send, + R: Send, +{ + rayon::scope(op) +} + +pub fn in_place_scope<'scope, OP, R>(op: OP) -> R +where + OP: FnOnce(&Scope<'scope>) -> R, +{ + rayon::in_place_scope(op) +} diff --git a/ext/crates/maybe-rayon/src/lib.rs b/ext/crates/maybe-rayon/src/lib.rs new file mode 100644 index 000000000..527671b62 --- /dev/null +++ b/ext/crates/maybe-rayon/src/lib.rs @@ -0,0 +1,9 @@ +#[cfg(feature = "concurrent")] +pub mod concurrent; +#[cfg(feature = "concurrent")] +pub use concurrent::*; + +#[cfg(not(feature = "concurrent"))] +pub mod sequential; +#[cfg(not(feature = "concurrent"))] +pub use sequential::*; diff --git a/ext/crates/maybe-rayon/src/sequential.rs b/ext/crates/maybe-rayon/src/sequential.rs new file mode 100644 index 000000000..1ef3e5d99 --- /dev/null +++ b/ext/crates/maybe-rayon/src/sequential.rs @@ -0,0 +1,78 @@ +pub mod prelude { + pub trait MaybeParallelIterator: Iterator {} + + pub trait MaybeIndexedParallelIterator: Iterator {} + + pub trait MaybeIntoParallelIterator: IntoIterator { + type Iter; + + fn maybe_into_par_iter(self) -> Self::Iter; + } + + pub trait MaybeIntoParallelRefMutIterator<'data> { + type Iter; + + fn maybe_par_iter_mut(&'data mut self) -> Self::Iter; + } + + // Implementations + + impl MaybeParallelIterator for I {} + + impl MaybeIndexedParallelIterator for I {} + + impl MaybeIntoParallelIterator for I { + type Iter = Self::IntoIter; + + fn maybe_into_par_iter(self) -> Self::Iter { + self.into_iter() + } + } + + impl<'data, I: 'data + ?Sized> MaybeIntoParallelRefMutIterator<'data> for I + where + &'data mut I: IntoIterator, + { + type Iter = <&'data mut I as IntoIterator>::IntoIter; + + fn maybe_par_iter_mut(&'data mut self) -> Self::Iter { + self.into_iter() + } + } +} + +pub struct Scope<'scope>(&'scope ()); + +impl<'scope> Scope<'scope> { + pub fn spawn(&self, body: BODY) + where + BODY: FnOnce(&Scope<'scope>) + Send + 'scope, + { + body(self) + } +} + +pub fn join(oper_a: A, oper_b: B) -> (RA, RB) +where + A: FnOnce() -> RA + Send, + B: FnOnce() -> RB + Send, + RA: Send, + RB: Send, +{ + (oper_a(), oper_b()) +} + +pub fn scope<'scope, OP, R>(op: OP) -> R +where + OP: FnOnce(&Scope<'scope>) -> R + Send, + R: Send, +{ + op(&Scope(&())) +} + +pub fn in_place_scope<'scope, OP, R>(op: OP) -> R +where + OP: FnOnce(&Scope<'scope>) -> R, +{ + op(&Scope(&())) +} diff --git a/ext/crates/once/Cargo.toml b/ext/crates/once/Cargo.toml index c320ba4f3..69a79a1df 100644 --- a/ext/crates/once/Cargo.toml +++ b/ext/crates/once/Cargo.toml @@ -10,15 +10,14 @@ edition = "2021" [dependencies] bivec = { path = "../bivec" } - -rayon = { version = "1", optional = true } +maybe-rayon = { path = "../maybe-rayon" } [dev-dependencies] criterion = "0.3" [features] default = [] -concurrent = ["rayon"] +concurrent = ["maybe-rayon/concurrent"] [[bench]] name = "criterion" diff --git a/ext/crates/once/src/lib.rs b/ext/crates/once/src/lib.rs index 95dbc23f6..f173904ed 100644 --- a/ext/crates/once/src/lib.rs +++ b/ext/crates/once/src/lib.rs @@ -10,8 +10,7 @@ use std::sync::{Mutex, MutexGuard}; use alloc::alloc::Layout; use std::ptr::NonNull; -#[cfg(feature = "concurrent")] -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; +use maybe_rayon::prelude::*; const USIZE_LEN: u32 = 0usize.count_zeros(); @@ -578,23 +577,22 @@ impl OnceVec { } } -#[cfg(feature = "concurrent")] impl OnceVec { - /// A parallel version of `extend`, where the function `f` is run for different indices - /// simultaneously using [`rayon`]. + /// A parallel version of `extend`. If the `concurrent` feature is enabled, the function `f` + /// will be run for different indices simultaneously using [`rayon`]. /// /// # Example #[cfg_attr(miri, doc = "```ignore")] #[cfg_attr(not(miri), doc = "```")] /// # use once::OnceVec; /// let v: OnceVec = OnceVec::new(); - /// v.par_extend(5, |i| i + 5); + /// v.maybe_par_extend(5, |i| i + 5); /// assert_eq!(v.len(), 6); /// for (i, &n) in v.iter().enumerate() { /// assert_eq!(n, i + 5); /// } /// ``` - pub fn par_extend(&self, new_max: usize, f: impl Fn(usize) -> T + Send + Sync) { + pub fn maybe_par_extend(&self, new_max: usize, f: impl Fn(usize) -> T + Send + Sync) { let ooo = self.lock(); assert!(ooo.0.is_empty()); @@ -607,7 +605,7 @@ impl OnceVec { // This is safe since we have taken the lock and have made no unsafe references. self.allocate_for(new_max); - (old_len..=new_max).into_par_iter().for_each(|i| { + (old_len..=new_max).maybe_into_par_iter().for_each(|i| { // These pointers are all non-aliasing so they can be written concurrently. std::ptr::write(self.entry_ptr(i), f(i)); }); @@ -809,35 +807,34 @@ impl OnceBiVec { } } -#[cfg(feature = "concurrent")] impl OnceBiVec { - /// A parallel version of `extend`, where the function `f` is run for different indices - /// simultaneously using [`rayon`]. + /// A parallel version of `extend`. If the `concurrent` feature is enabled, the function `f` + /// will be run for different indices simultaneously using [`rayon`]. /// /// # Example /// ``` /// # use once::OnceBiVec; /// let v: OnceBiVec = OnceBiVec::new(-4); - /// v.par_extend(5, |i| i + 5); + /// v.maybe_par_extend(5, |i| i + 5); /// assert_eq!(v.len(), 6); /// for (i, &n) in v.iter_enum() { /// assert_eq!(n, i + 5); /// } /// ``` - pub fn par_extend(&self, new_max: i32, f: impl (Fn(i32) -> T) + Send + Sync) { + pub fn maybe_par_extend(&self, new_max: i32, f: impl (Fn(i32) -> T) + Send + Sync) { if new_max < self.min_degree { return; } self.data - .par_extend((new_max - self.min_degree) as usize, |i| { + .maybe_par_extend((new_max - self.min_degree) as usize, |i| { f(i as i32 + self.min_degree) }); } - pub fn par_iter_enum( + pub fn maybe_par_iter_enum( &self, - ) -> impl ParallelIterator + IndexedParallelIterator { - self.range().into_par_iter().map(|i| (i, &self[i])) + ) -> impl MaybeParallelIterator + MaybeIndexedParallelIterator { + self.range().maybe_into_par_iter().map(|i| (i, &self[i])) } } diff --git a/ext/crates/sseq/Cargo.toml b/ext/crates/sseq/Cargo.toml index e6d4f2f6d..80bbd1aa4 100644 --- a/ext/crates/sseq/Cargo.toml +++ b/ext/crates/sseq/Cargo.toml @@ -10,9 +10,9 @@ algebra = { path = "../algebra/", default-features = false } bivec = { path = "../bivec/" } chart = { path = "../chart/" } fp = { path = "../fp/", default-features = false } +maybe-rayon = { path = "../maybe-rayon" } once = { path = "../once/" } -rayon = { version = "1.5", optional = true } serde = { version = "1.0.0", optional = true } serde_json = { version = "1.0.0", optional = true } @@ -22,6 +22,6 @@ rand = "0.8" [features] default = ["odd-primes"] -concurrent = ["rayon"] +concurrent = ["maybe-rayon/concurrent"] json = ["serde_json", "serde"] odd-primes = ["fp/odd-primes"] diff --git a/ext/crates/sseq/src/coordinates/mod.rs b/ext/crates/sseq/src/coordinates/mod.rs index d9b9ad36c..b92706189 100644 --- a/ext/crates/sseq/src/coordinates/mod.rs +++ b/ext/crates/sseq/src/coordinates/mod.rs @@ -8,6 +8,8 @@ pub use element::BidegreeElement; pub use generator::BidegreeGenerator; pub use range::BidegreeRange; +use maybe_rayon::prelude::*; + /// Given a function `f(s, t)`, compute it for every `s` in `[min_s, max_s]` and every `t` in /// `[min_t, max_t(s)]`. Further, we only compute `f(s, t)` when `f(s - 1, t')` has been computed /// for all `t' < t`. @@ -24,19 +26,16 @@ pub use range::BidegreeRange; /// # Arguments: /// - `max_s`: This is exclusive /// - `max_t`: This is exclusive -#[cfg(feature = "concurrent")] pub fn iter_s_t( f: &(impl Fn(Bidegree) -> std::ops::Range + Sync), min: Bidegree, max: BidegreeRange, ) { - use rayon::prelude::*; - - rayon::scope(|scope| { + maybe_rayon::scope(|scope| { // Rust does not support recursive closures, so we have to pass everything along as // arguments. fn run<'a, S: Sync>( - scope: &rayon::Scope<'a>, + scope: &maybe_rayon::Scope<'a>, f: &'a (impl Fn(Bidegree) -> std::ops::Range + Sync + 'a), max: BidegreeRange<'a, S>, current: Bidegree, @@ -49,22 +48,22 @@ pub fn iter_s_t( if !ret.is_empty() { // We spawn a new scope to avoid recursion, which may blow the stack scope.spawn(move |scope| { - ret.into_par_iter() + ret.maybe_into_par_iter() .for_each(|t| run(scope, f, max, Bidegree::s_t(current.s() + 1, t))); }); } } } - rayon::join( + maybe_rayon::join( || { (min.t()..max.t(min.s())) - .into_par_iter() + .maybe_into_par_iter() .for_each(|t| run(scope, f, max, Bidegree::s_t(min.s(), t))) }, || { (min.s() + 1..max.s()) - .into_par_iter() + .maybe_into_par_iter() .for_each(|s| run(scope, f, max, Bidegree::s_t(s, min.t()))) }, ); diff --git a/ext/examples/secondary_massey.rs b/ext/examples/secondary_massey.rs index 614683a1f..0b6608a8f 100644 --- a/ext/examples/secondary_massey.rs +++ b/ext/examples/secondary_massey.rs @@ -169,19 +169,11 @@ fn main() -> anyhow::Result<()> { if is_unit { res_lift.extend_all(); } else { - #[cfg(feature = "concurrent")] - rayon::join(|| res_lift.extend_all(), || unit_lift.extend_all()); - - #[cfg(not(feature = "concurrent"))] - { - res_lift.extend_all(); - unit_lift.extend_all(); - } + maybe_rayon::join(|| res_lift.extend_all(), || unit_lift.extend_all()); } // Now extend homomorphisms - #[cfg(feature = "concurrent")] - rayon::scope(|s| { + maybe_rayon::scope(|s| { s.spawn(|_| { a.underlying().extend_all(); a.extend_all(); @@ -198,20 +190,6 @@ fn main() -> anyhow::Result<()> { } }); - #[cfg(not(feature = "concurrent"))] - { - a.underlying().extend_all(); - a.extend_all(); - b.underlying().extend_all(); - b.extend_all(); - if let Some(a_tau) = &a_tau { - a_tau.extend_all(); - } - if let Some(b_tau) = &b_tau { - b_tau.extend_all(); - } - } - let res_sseq = Arc::new(res_lift.e3_page()); let unit_sseq = if is_unit { Arc::clone(&res_sseq) diff --git a/ext/src/chain_complex/chain_homotopy.rs b/ext/src/chain_complex/chain_homotopy.rs index 64457a540..1b7e59d82 100644 --- a/ext/src/chain_complex/chain_homotopy.rs +++ b/ext/src/chain_complex/chain_homotopy.rs @@ -12,8 +12,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::Mutex; -#[cfg(feature = "concurrent")] -use rayon::prelude::*; +use maybe_rayon::prelude::*; // Another instance of https://github.com/rust-lang/rust/issues/91380 /// A chain homotopy from $f to g$, or equivalently a null-homotopy of $h = f - g$. A chain map is @@ -140,30 +139,15 @@ impl< self.initialize_homotopies(max_source.s()); - #[cfg(not(feature = "concurrent"))] - { - for source_s in shift.s() - 1..max_source.s() { - for source_t in - self.homotopies[source_s as i32].next_degree()..max_source.t(source_s) - { - let source = Bidegree::s_t(source_s, source_t); - self.extend_step(source); - } - } - } - - #[cfg(feature = "concurrent")] - { - let min = Bidegree::s_t( - shift.s() - 1, - std::cmp::min( - self.left.source.min_degree(), - self.right.target.min_degree() + shift.t(), - ), - ); + let min = Bidegree::s_t( + shift.s() - 1, + std::cmp::min( + self.left.source.min_degree(), + self.right.target.min_degree() + shift.t(), + ), + ); - sseq::coordinates::iter_s_t(&|b| self.extend_step(b), min, max_source); - } + sseq::coordinates::iter_s_t(&|b| self.extend_step(b), min, max_source); } fn extend_step(&self, source: Bidegree) -> std::ops::Range { @@ -271,11 +255,7 @@ impl< scratch }; - #[cfg(not(feature = "concurrent"))] - let scratches: Vec = (0..num_gens).map(f).collect(); - - #[cfg(feature = "concurrent")] - let scratches: Vec = (0..num_gens).into_par_iter().map(f).collect(); + let scratches: Vec = (0..num_gens).maybe_into_par_iter().map(f).collect(); assert!(U::apply_quasi_inverse( &*self.right.target, diff --git a/ext/src/nassau.rs b/ext/src/nassau.rs index bb57bf132..71f7baf11 100644 --- a/ext/src/nassau.rs +++ b/ext/src/nassau.rs @@ -38,17 +38,14 @@ use itertools::Itertools; use once::OnceVec; use sseq::coordinates::Bidegree; -#[cfg(feature = "concurrent")] use std::sync::mpsc; -#[cfg(feature = "concurrent")] /// See [`resolution::SenderData`](../resolution/struct.SenderData.html). This differs by not having the `new` field. struct SenderData { b: Bidegree, sender: mpsc::Sender, } -#[cfg(feature = "concurrent")] impl SenderData { pub(crate) fn send(b: Bidegree, sender: mpsc::Sender) { sender @@ -896,20 +893,7 @@ impl> Resolution { self.extend_through_degree(max.s()); self.algebra().compute_basis(max.t()); - #[cfg(not(feature = "concurrent"))] - for t in 0..=max.t() { - let start_s = std::cmp::max(0, t - max.n()) as u32; - for s in start_s..=max.s() { - let b = Bidegree::s_t(s, t); - if self.has_computed_bidegree(b) { - continue; - } - self.step_resolution(b); - } - } - - #[cfg(feature = "concurrent")] - rayon::in_place_scope(|scope| { + maybe_rayon::in_place_scope(|scope| { // This algorithm is not optimal, as we compute (s, t) only after computing (s - 1, t) // and (s, t - 1). In theory, it suffices to wait for (s, t - 1) and (s - 1, t - 1), // but having the dimensions of the modules change halfway through the computation is diff --git a/ext/src/resolution.rs b/ext/src/resolution.rs index 1a773f6be..c515aeca6 100644 --- a/ext/src/resolution.rs +++ b/ext/src/resolution.rs @@ -22,13 +22,11 @@ use dashmap::DashMap; use itertools::Itertools; -#[cfg(feature = "concurrent")] use std::sync::mpsc; /// In [`MuResolution::compute_through_stem`] and [`MuResolution::compute_through_bidegree`], we pass /// this struct around to inform the supervisor what bidegrees have been computed. We use an /// explicit struct instead of a tuple to avoid an infinite type problem. -#[cfg(feature = "concurrent")] struct SenderData { b: Bidegree, /// Whether this bidegree was newly calculated or have already been calculated. @@ -40,7 +38,6 @@ struct SenderData { sender: mpsc::Sender, } -#[cfg(feature = "concurrent")] impl SenderData { fn send(b: Bidegree, new: bool, sender: mpsc::Sender) { sender @@ -713,20 +710,7 @@ where self.extend_through_degree(max.s()); self.algebra().compute_basis(max.t() - min_degree); - #[cfg(not(feature = "concurrent"))] - for t in min_degree..=max.t() { - for s in 0..=max.s() { - let b = Bidegree::s_t(s, t); - if self.has_computed_bidegree(b) { - continue; - } - self.step_resolution(b); - cb(b); - } - } - - #[cfg(feature = "concurrent")] - rayon::in_place_scope(|scope| { + maybe_rayon::in_place_scope(|scope| { // Things that we have finished computing. let mut progress: Vec = vec![min_degree - 1; max.s() as usize + 1]; // We will kickstart the process by pretending we have computed (0, min_degree - 1). So @@ -778,21 +762,7 @@ where self.extend_through_degree(max.s()); self.algebra().compute_basis(max.t() - min_degree); - #[cfg(not(feature = "concurrent"))] - for t in min_degree..=max.t() { - let start_s = std::cmp::max(0, t - max.n()) as u32; - for s in start_s..=max.s() { - let b = Bidegree::s_t(s, t); - if self.has_computed_bidegree(b) { - continue; - } - self.step_resolution(b); - cb(b); - } - } - - #[cfg(feature = "concurrent")] - rayon::in_place_scope(|scope| { + maybe_rayon::in_place_scope(|scope| { // Things that we have finished computing. let mut progress: Vec = vec![min_degree - 1; max.s() as usize + 1]; // We will kickstart the process by pretending we have computed (0, min_degree - 1). So diff --git a/ext/src/resolution_homomorphism.rs b/ext/src/resolution_homomorphism.rs index 6574dd596..629eadb6f 100644 --- a/ext/src/resolution_homomorphism.rs +++ b/ext/src/resolution_homomorphism.rs @@ -18,8 +18,7 @@ use sseq::coordinates::{Bidegree, BidegreeGenerator, BidegreeRange}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -#[cfg(feature = "concurrent")] -use rayon::prelude::*; +use maybe_rayon::prelude::*; pub type ResolutionHomomorphism = MuResolutionHomomorphism; pub type UnstableResolutionHomomorphism = MuResolutionHomomorphism; @@ -154,19 +153,6 @@ where )); } - // See the concurrent version for documentation - #[cfg(not(feature = "concurrent"))] - pub fn extend_profile(&self, max: BidegreeRange) { - self.get_map_ensure_length(max.s() - 1); - for s in self.shift.s()..max.s() { - let f_cur = self.get_map_ensure_length(s); - for t in f_cur.next_degree()..max.t(s) { - let b = Bidegree::s_t(s, t); - self.extend_step_raw(b, None); - } - } - } - /// Extends the resolution homomorphism up to a given range. This range is first specified by /// the maximum `s`, then the maximum `t` for each `s`. This should rarely be used directly; /// instead one should use [`MuResolutionHomomorphism::extend`], @@ -179,7 +165,6 @@ where /// This assumes in yet-uncomputed bidegrees, the homology of the source consists only of /// decomposables (e.g. it is trivial). More precisely, we assume /// [`MuResolutionHomomorphism::extend_step_raw`] can be called with `extra_images = None`. - #[cfg(feature = "concurrent")] pub fn extend_profile(&self, max: BidegreeRange) { self.get_map_ensure_length(max.s() - 1); @@ -309,12 +294,8 @@ where } }; - #[cfg(not(feature = "concurrent"))] - let fdx_vectors: Vec = (0..num_gens).filter_map(compute_fdx_vector).collect(); - - #[cfg(feature = "concurrent")] let fdx_vectors: Vec = (0..num_gens) - .into_par_iter() + .maybe_into_par_iter() .filter_map(compute_fdx_vector) .collect(); diff --git a/ext/src/secondary.rs b/ext/src/secondary.rs index 51afd1ec7..f2d3afd2c 100644 --- a/ext/src/secondary.rs +++ b/ext/src/secondary.rs @@ -21,8 +21,7 @@ use std::sync::Arc; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use dashmap::DashMap; use itertools::Itertools; -#[cfg(feature = "concurrent")] -use rayon::prelude::*; +use maybe_rayon::prelude::*; pub static TAU_BIDEGREE: Bidegree = Bidegree::n_s(0, 1); @@ -278,17 +277,9 @@ impl SecondaryHomotopy { composite }; - #[cfg(not(feature = "concurrent"))] - self.composites.extend(degree, |t| { + self.composites.maybe_par_extend(degree, |t| { (0..self.source.number_of_gens_in_degree(t)) - .map(|i| f(t, i)) - .collect() - }); - - #[cfg(feature = "concurrent")] - self.composites.par_extend(degree, |t| { - (0..self.source.number_of_gens_in_degree(t)) - .into_par_iter() + .maybe_into_par_iter() .map(|i| f(t, i)) .collect() }); @@ -401,11 +392,7 @@ pub trait SecondaryLift: Sync + Sized { ); }; - #[cfg(not(feature = "concurrent"))] - self.homotopies().range().for_each(f); - - #[cfg(feature = "concurrent")] - self.homotopies().range().into_par_iter().for_each(f); + self.homotopies().range().maybe_into_par_iter().for_each(f); } fn get_intermediate(&self, gen: BidegreeGenerator) -> FpVector { @@ -461,21 +448,15 @@ pub trait SecondaryLift: Sync + Sized { ); if let Some(homotopy) = homotopies.get(s as i32 + 1) { - #[cfg(not(feature = "concurrent"))] - for t in 0..self.max().t(s + 1) { - for i in 0..homotopy.source.number_of_gens_in_degree(t) { - self.get_intermediate(BidegreeGenerator::s_t(s + 1, t, i)); - } - } - - #[cfg(feature = "concurrent")] - (0..self.max().t(s + 1)).into_par_iter().for_each(|t| { - (0..homotopy.source.number_of_gens_in_degree(t)) - .into_par_iter() - .for_each(|i| { - self.get_intermediate(BidegreeGenerator::s_t(s + 1, t, i)); - }) - }); + (0..self.max().t(s + 1)) + .maybe_into_par_iter() + .for_each(|t| { + (0..homotopy.source.number_of_gens_in_degree(t)) + .maybe_into_par_iter() + .for_each(|i| { + self.get_intermediate(BidegreeGenerator::s_t(s + 1, t, i)); + }) + }); } } @@ -501,28 +482,21 @@ pub trait SecondaryLift: Sync + Sized { self.intermediates().insert(gen, self.get_intermediate(gen)); }; - #[cfg(not(feature = "concurrent"))] - for (s, homotopy) in self.homotopies().iter_enum().skip(1) { - let s = s as u32; - for t in homotopy.composites.range() { - for i in 0..homotopy.source.number_of_gens_in_degree(t) { - f(BidegreeGenerator::s_t(s, t, i)); - } - } - } - - #[cfg(feature = "concurrent")] self.homotopies() - .par_iter_enum() + .maybe_par_iter_enum() .skip(1) .for_each(|(s, homotopy)| { let s = s as u32; - homotopy.composites.range().into_par_iter().for_each(|t| { - (0..homotopy.source.number_of_gens_in_degree(t)) - .into_par_iter() - .for_each(|i| f(BidegreeGenerator::s_t(s, t, i))) - }) + homotopy + .composites + .range() + .maybe_into_par_iter() + .for_each(|t| { + (0..homotopy.source.number_of_gens_in_degree(t)) + .maybe_into_par_iter() + .for_each(|i| f(BidegreeGenerator::s_t(s, t, i))) + }) }) } @@ -574,15 +548,11 @@ pub trait SecondaryLift: Sync + Sized { v }; - #[cfg(feature = "concurrent")] let mut intermediates: Vec = (0..num_gens) - .into_par_iter() + .maybe_into_par_iter() .map(get_intermediate) .collect(); - #[cfg(not(feature = "concurrent"))] - let mut intermediates: Vec = (0..num_gens).map(get_intermediate).collect(); - let mut results = vec![FpVector::new(p, target_dim); num_gens]; assert!(target.apply_quasi_inverse(&mut results, target_b, &intermediates,)); @@ -640,30 +610,16 @@ pub trait SecondaryLift: Sync + Sized { h.homotopies.extend_by_zero(h.composites.max_degree()); } - #[cfg(not(feature = "concurrent"))] - for (s, homotopy) in self.homotopies().iter_enum().skip(1) { - let s = s as u32; - - for t in homotopy.homotopies.next_degree()..self.max().t(s) { - let b = Bidegree::s_t(s, t); - self.compute_homotopy_step(b); - } - } - - #[cfg(feature = "concurrent")] - { - let min_t = self.homotopies()[shift.s() as i32].homotopies.min_degree(); - let s_range = self.homotopies().range(); - let min = Bidegree::s_t(s_range.start as u32 + 1, min_t); - let max = self.max().restrict(s_range.end as u32); - sseq::coordinates::iter_s_t(&|b| self.compute_homotopy_step(b), min, max); - } + let min_t = self.homotopies()[shift.s() as i32].homotopies.min_degree(); + let s_range = self.homotopies().range(); + let min = Bidegree::s_t(s_range.start as u32 + 1, min_t); + let max = self.max().restrict(s_range.end as u32); + sseq::coordinates::iter_s_t(&|b| self.compute_homotopy_step(b), min, max); } fn extend_all(&self) { self.initialize_homotopies(); self.compute_composites(); - #[cfg(feature = "concurrent")] self.compute_intermediates(); self.compute_homotopies(); }