diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 9ba6de4..20602c2 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -13,16 +13,17 @@ rand = "0.4" bit-vec = "0.4.4" ff = { path = "../ff" } futures = "0.1" -futures-cpupool = "0.1" +futures-cpupool = { version = "0.1", optional = true } group = { path = "../group" } -num_cpus = "1" -crossbeam = "0.3" +num_cpus = { version = "1", optional = true } +crossbeam = { version = "0.3", optional = true } pairing = { path = "../pairing", optional = true } byteorder = "1" [features] groth16 = ["pairing"] -default = ["groth16"] +multicore = ["futures-cpupool", "crossbeam", "num_cpus"] +default = ["groth16", "multicore"] [[test]] name = "mimc" diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs index f6d7163..ffbb25b 100644 --- a/bellman/src/lib.rs +++ b/bellman/src/lib.rs @@ -3,13 +3,18 @@ extern crate group; #[cfg(feature = "pairing")] extern crate pairing; extern crate rand; -extern crate num_cpus; + extern crate futures; -extern crate futures_cpupool; extern crate bit_vec; -extern crate crossbeam; extern crate byteorder; +#[cfg(feature = "multicore")] +extern crate crossbeam; +#[cfg(feature = "multicore")] +extern crate futures_cpupool; +#[cfg(feature = "multicore")] +extern crate num_cpus; + pub mod multicore; mod multiexp; pub mod domain; diff --git a/bellman/src/multicore.rs b/bellman/src/multicore.rs index c0062fc..8d0b00c 100644 --- a/bellman/src/multicore.rs +++ b/bellman/src/multicore.rs @@ -4,103 +4,165 @@ //! crossbeam but may be extended in the future to //! allow for various parallelism strategies. -use num_cpus; -use futures::{Future, IntoFuture, Poll}; -use futures_cpupool::{CpuPool, CpuFuture}; -use crossbeam::{self, Scope}; - -#[derive(Clone)] -pub struct Worker { - cpus: usize, - pool: CpuPool -} +#[cfg(feature = "multicore")] +mod implementation { + use num_cpus; + use futures::{Future, IntoFuture, Poll}; + use futures_cpupool::{CpuPool, CpuFuture}; + use crossbeam::{self, Scope}; + + #[derive(Clone)] + pub struct Worker { + cpus: usize, + pool: CpuPool + } + + impl Worker { + // We don't expose this outside the library so that + // all `Worker` instances have the same number of + // CPUs configured. + pub(crate) fn new_with_cpus(cpus: usize) -> Worker { + Worker { + cpus: cpus, + pool: CpuPool::new(cpus) + } + } + + pub fn new() -> Worker { + Self::new_with_cpus(num_cpus::get()) + } + + pub fn log_num_cpus(&self) -> u32 { + log2_floor(self.cpus) + } -impl Worker { - // We don't expose this outside the library so that - // all `Worker` instances have the same number of - // CPUs configured. - pub(crate) fn new_with_cpus(cpus: usize) -> Worker { - Worker { - cpus: cpus, - pool: CpuPool::new(cpus) + pub fn compute( + &self, f: F + ) -> WorkerFuture + where F: FnOnce() -> R + Send + 'static, + R: IntoFuture + 'static, + R::Future: Send + 'static, + R::Item: Send + 'static, + R::Error: Send + 'static + { + WorkerFuture { + future: self.pool.spawn_fn(f) + } + } + + pub fn scope<'a, F, R>( + &self, + elements: usize, + f: F + ) -> R + where F: FnOnce(&Scope<'a>, usize) -> R + { + let chunk_size = if elements < self.cpus { + 1 + } else { + elements / self.cpus + }; + + crossbeam::scope(|scope| { + f(scope, chunk_size) + }) } } - pub fn new() -> Worker { - Self::new_with_cpus(num_cpus::get()) + pub struct WorkerFuture { + future: CpuFuture } - pub fn log_num_cpus(&self) -> u32 { - log2_floor(self.cpus) + impl Future for WorkerFuture { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll + { + self.future.poll() + } } - pub fn compute( - &self, f: F - ) -> WorkerFuture - where F: FnOnce() -> R + Send + 'static, - R: IntoFuture + 'static, - R::Future: Send + 'static, - R::Item: Send + 'static, - R::Error: Send + 'static - { - WorkerFuture { - future: self.pool.spawn_fn(f) + fn log2_floor(num: usize) -> u32 { + assert!(num > 0); + + let mut pow = 0; + + while (1 << (pow+1)) <= num { + pow += 1; } + + pow } - pub fn scope<'a, F, R>( - &self, - elements: usize, - f: F - ) -> R - where F: FnOnce(&Scope<'a>, usize) -> R - { - let chunk_size = if elements < self.cpus { - 1 - } else { - elements / self.cpus - }; - - crossbeam::scope(|scope| { - f(scope, chunk_size) - }) + #[test] + fn test_log2_floor() { + assert_eq!(log2_floor(1), 0); + assert_eq!(log2_floor(2), 1); + assert_eq!(log2_floor(3), 1); + assert_eq!(log2_floor(4), 2); + assert_eq!(log2_floor(5), 2); + assert_eq!(log2_floor(6), 2); + assert_eq!(log2_floor(7), 2); + assert_eq!(log2_floor(8), 3); } } -pub struct WorkerFuture { - future: CpuFuture -} +#[cfg(not(feature = "multicore"))] +mod implementation { + use futures::{future, Future, IntoFuture, Poll}; + + #[derive(Clone)] + pub struct Worker; + + impl Worker { + pub fn new() -> Worker { + Worker + } -impl Future for WorkerFuture { - type Item = T; - type Error = E; + pub fn log_num_cpus(&self) -> u32 { + 0 + } + + pub fn compute(&self, f: F) -> R::Future + where + F: FnOnce() -> R + Send + 'static, + R: IntoFuture + 'static, + R::Future: Send + 'static, + R::Item: Send + 'static, + R::Error: Send + 'static, + { + f().into_future() + } - fn poll(&mut self) -> Poll - { - self.future.poll() + pub fn scope(&self, elements: usize, f: F) -> R + where + F: FnOnce(&DummyScope, usize) -> R, + { + f(&DummyScope, elements) + } } -} -fn log2_floor(num: usize) -> u32 { - assert!(num > 0); + pub struct WorkerFuture { + future: future::FutureResult, + } - let mut pow = 0; + impl Future for WorkerFuture { + type Item = T; + type Error = E; - while (1 << (pow+1)) <= num { - pow += 1; + fn poll(&mut self) -> Poll { + self.future.poll() + } } - pow -} + pub struct DummyScope; -#[test] -fn test_log2_floor() { - assert_eq!(log2_floor(1), 0); - assert_eq!(log2_floor(2), 1); - assert_eq!(log2_floor(3), 1); - assert_eq!(log2_floor(4), 2); - assert_eq!(log2_floor(5), 2); - assert_eq!(log2_floor(6), 2); - assert_eq!(log2_floor(7), 2); - assert_eq!(log2_floor(8), 3); + impl DummyScope { + pub fn spawn(&self, f: F) { + f(); + } + } } + +pub use self::implementation::*;