diff --git a/Cargo.lock b/Cargo.lock index e12fcc5..4116a58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,11 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.6" @@ -45,12 +50,21 @@ name = "gmp-mpfr-sys" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hashbrown" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itertools" -version = "0.7.8" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -63,7 +77,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.43" +version = "0.2.44" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -81,7 +95,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -103,16 +117,17 @@ name = "num_cpus" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pseudoprimes" version = "0.1.0" dependencies = [ - "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", "modinverse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -127,7 +142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -153,6 +168,11 @@ dependencies = [ "gmp-mpfr-sys 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "threadpool" version = "1.7.1" @@ -188,15 +208,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gmp-mpfr-sys 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "44c134f130ccafac31fa35d08e4cb67e5d14369bf0a819de75f71dc6e4bc6a32" -"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" +"checksum hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "64b7d419d0622ae02fe5da6b9a5e1964b610a65bb37923b976aeebb6dbb8f86e" +"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311" "checksum modinverse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce003725589b388a671226e8307adb20f4fc6fe358b0061a462a1a6d95bd38f8" "checksum nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" @@ -206,6 +228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" "checksum rug 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "becfc07b9c2379b3eaa43c42adadfe5eb573b3c42fe00e36ab003f02573ce4ac" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" diff --git a/Cargo.toml b/Cargo.toml index 898b2e6..93efbea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "pseudoprimes" version = "0.1.0" authors = ["Andrew Hopkins "] +edition = "2018" [dependencies] modinverse = "0.1.0" @@ -10,6 +11,7 @@ threadpool = "1.7" lazy_static = "1.1" rand = "0.5.5" itertools = "0.7.8" +hashbrown = "0.1" [dependencies.nix] version = "0.9" diff --git a/LICENSE-THIRD-PARTY.txt b/LICENSE-THIRD-PARTY.txt index 2821e3f..9530de5 100644 --- a/LICENSE-THIRD-PARTY.txt +++ b/LICENSE-THIRD-PARTY.txt @@ -198,6 +198,7 @@ Library. ** lazy_static ** rand ** itertools +** hashbrown Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/README.md b/README.md index 1db3e97..88054c7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ However, it is hard to parallelize hashmap insertion. If only a single thread is The solution to all of these problems is to use a three-phase approach with a Bloom filter. Insertion in a Bloom filter is guaranteed constant time. It is trivial to have multiple threads operating at once, and merging Bloom filters is linear time. The Bloom filter only stores the SSP itself, not the corresponding mask, which makes this a three phase approach: we insert the SSPs for the first half of R into the filter, and then compute the SSPs for the second half of R, checking for membership in the Bloom filter and storing the SSP and the mask in a (small) hashmap. In the third phase, we *recompute* the SSPs for the first half of R, checking the hashmap for membership. In more detail, the three phases are: ## PHASE 1 -In this stage, we need to record 2^32 64-bit subset products (SSPs) for the first half of R. We build a Bloom filter for this, since parallel insertion is easy, and it is easy to combine the results of two Bloom filters. Since we are inserting 2^32 items, a large Bloom filter is needed (approximately 2^39 bits to get a reasonable false-positive rate). Runtime of the algorithm is bound by memory, so we can increase perforamce by creating two Bloom filters, each of size 2^39 bits, for each of the NUMA nodes on a m5d.24xlarge EC2 instance, and inserting the SSPs into one of the two filters (depending on the NUMA node of the thread running computing the SSP). The resulting Bloom filters are then OR'd together to form two identical Bloom filters, both containing all 2^32 SSPs. +In this stage, we need to record 2^32 64-bit subset products (SSPs) for the first half of R. We build a Bloom filter for this, since parallel insertion is easy, and it is easy to combine the results of two Bloom filters. Since we are inserting 2^32 items, a large Bloom filter is needed (approximately 2^39 bits to get a reasonable false-positive rate). Runtime of the algorithm is bound by memory, so we can increase performance by creating two Bloom filters, each of size 2^39 bits, for each of the NUMA nodes on a m5d.24xlarge EC2 instance, and inserting the SSPs into one of the two filters (depending on the NUMA node of the thread running computing the SSP). The resulting Bloom filters are then OR'd together to form two identical Bloom filters, both containing all 2^32 SSPs. ## PHASE 2 The next phase of the algorithm computes all 2^32 SSPs of the (inverse mod M of the) other half of the set. We check the closest Bloom filter to see if the SSP may be preset. If so, we record this in a map from SSP values to the SSP mask (from this phase) which created the SSP. @@ -41,7 +41,7 @@ git clone https://github.com/awslabs/fast-pseudoprimes.git cd fast-pseudoprimes cargo +nightly run --features numa,unstable --release ``` -The code takes about 24 seconds to run from start to finish. +The code takes about 21.9 seconds to run from start to finish. ## Status of this code This code is released as-is, and we have no plans to maintain it. We are happy to accept pull requests. diff --git a/src/bitset/mod.rs b/src/bitset.rs similarity index 66% rename from src/bitset/mod.rs rename to src/bitset.rs index 2f80ed5..13d9c8a 100644 --- a/src/bitset/mod.rs +++ b/src/bitset.rs @@ -1,4 +1,4 @@ -// mod.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// bitset.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 mod stable; @@ -14,5 +14,5 @@ pub use self::stable::*; #[cfg(not(all(feature = "unstable", feature = "numa")))] impl BitSet { - pub fn on_node(self, node_id: u32) -> Self { self } + pub fn on_node(self, _node_id: u32) -> Self { self } } \ No newline at end of file diff --git a/src/bloomfilter/mod.rs b/src/bloomfilter.rs similarity index 94% rename from src/bloomfilter/mod.rs rename to src/bloomfilter.rs index 3959f10..66f1a9d 100644 --- a/src/bloomfilter/mod.rs +++ b/src/bloomfilter.rs @@ -1,20 +1,21 @@ -// mod.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// bitset.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use gray_prod_iter::*; -use progress; -use numa_threadpool::ThreadPool; +use crate::gray_prod_iter::*; +use crate::progress; +use crate::numa_threadpool::ThreadPool; use std::sync::{Mutex, Arc, mpsc::channel}; use std::sync::atomic::{Ordering, AtomicUsize}; -use std::collections::HashMap; +use hashbrown::HashMap; use std::time::Instant; mod conc_bloom; -use bloomfilter::conc_bloom::*; +use crate::bloomfilter::conc_bloom::*; -use magic_numbers::*; -use modulus::*; +use crate::magic_numbers::*; +use crate::modulus::*; +use crate::time::get_elapsed_time; const FILTER_SIZE : usize = 1usize << 39; const FILTER_HASHES : usize = 2; @@ -31,8 +32,6 @@ pub fn bloom_t1_kernel( ) { let mut handle = progress.handle(); - let filter = &filter as &BloomFilter; - for (_k, v) in ProductIter::new(&product_set, start, end) { filter.put(&v); handle.report(1); @@ -45,7 +44,7 @@ pub fn bloom_t1_kernel( /// Details: we create a bloom filter for each NUMA node, /// then divides the work up into lots of chunks. /// The subset products for each chunk are inserted into one of the two bloom filters, -/// and at the end of the comptutation, we "cross_or" the two filters together so that +/// and at the end of the computation, we "cross_or" the two filters together so that /// each bloom filter contains *all* of the subset products. /// We output a map from NUMA node ID to a bloom filter, /// where each bloom filter contains all subset products in t1. @@ -142,7 +141,7 @@ pub fn build_t2( let per_task = total_work / N_TASKS; - let pool : ThreadPool>> = ThreadPool::new(|node_id| + let pool : ThreadPool>> = ThreadPool::new(|node_id| filters.get(&node_id).unwrap_or_else(|| { println!("Warning: Couldn't find a T1 for node {}, falling back to arbitrary node", node_id); filters.iter().next().unwrap().1 @@ -230,6 +229,7 @@ pub fn final_sieve( t1: &[u64], t2: &[u64] ) -> Vec { + let total = Instant::now(); let t2map = Arc::new(t2map); let pool = ThreadPool::new(|_| ()); let t1_product_set = Arc::new(ProductSet::new(t1_forward, MODULUS)); @@ -264,6 +264,8 @@ pub fn final_sieve( let t3_misses = t3_misses.load(Ordering::SeqCst); + println!("[final_sieve] Completed in {}", get_elapsed_time(total)); + println!("Found {} pseudoprimes, with {} T3 misses, {} T2 false positives", results.len(), t3_misses, t2map.len() - t3_misses - results.len()); diff --git a/src/bloomfilter/conc_bloom.rs b/src/bloomfilter/conc_bloom.rs index fe712bf..c36bc5f 100644 --- a/src/bloomfilter/conc_bloom.rs +++ b/src/bloomfilter/conc_bloom.rs @@ -5,7 +5,7 @@ use std::hash::{Hasher, Hash, BuildHasher}; use std::collections::hash_map::RandomState; use std::marker::PhantomData; -use bitset::BitSet; +use crate::bitset::BitSet; pub struct Builder { hash_states: Vec, diff --git a/src/gray_prod_iter.rs b/src/gray_prod_iter.rs index ea7b1c7..63b86d3 100644 --- a/src/gray_prod_iter.rs +++ b/src/gray_prod_iter.rs @@ -1,7 +1,8 @@ // gray_prod_iters.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use modulus::*; + +use crate::modulus::*; pub struct ProductSet { elems: Vec, diff --git a/src/lib.rs b/src/lib.rs index f53c532..8dc1832 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,19 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 #![allow(dead_code)] +#![cfg_attr(feature = "unstable", feature(duration_as_u128))] #![cfg_attr(feature = "unstable", feature(asm))] #![cfg_attr(feature = "unstable", feature(core_intrinsics))] #![cfg_attr(feature = "unstable", feature(avx512_target_feature))] -#[macro_use] -extern crate lazy_static; -extern crate modinverse; -extern crate rug; -extern crate threadpool; -#[cfg(feature="unstable")] -extern crate libc; -extern crate itertools; - pub mod mulmod; pub mod bloomfilter; pub mod progress; @@ -22,4 +14,5 @@ pub mod gray_prod_iter; pub mod magic_numbers; pub mod bitset; pub mod modulus; -pub mod numa_threadpool; \ No newline at end of file +pub mod numa_threadpool; +pub mod time; \ No newline at end of file diff --git a/src/magic_numbers.rs b/src/magic_numbers.rs index 9d752b3..646dc2c 100644 --- a/src/magic_numbers.rs +++ b/src/magic_numbers.rs @@ -1,10 +1,11 @@ // magic_numbers.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 - +use lazy_static::*; use rug::Integer; use rug::integer::IsPrime; use itertools::iproduct; -use modulus::*; +use crate::modulus::*; + pub const M: u64 = 11908862398227544750; pub const MAX_R: u64 = 1152921504606846976; diff --git a/src/main.rs b/src/main.rs index 8f20a98..fd263e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #![cfg_attr(feature = "unstable", feature(asm))] +#![cfg_attr(feature = "unstable", feature(duration_as_u128))] extern crate pseudoprimes; extern crate rug; @@ -10,14 +11,15 @@ extern crate itertools; use pseudoprimes::*; -use magic_numbers::*; -use bloomfilter::*; +use crate::magic_numbers::*; +use crate::time::get_elapsed_time; +use crate::bloomfilter::*; use std::time::Instant; fn main() { - let total = Instant::now(); + let start = Instant::now(); // fp p<=0.001, 32GiB, k=2 let filter = bloom_t1(&T1_INVERSE); @@ -32,5 +34,5 @@ fn main() { println!("Found passing prime {}, vector {:?}", result.pseudoprime, result.factors); } - println!("Total time: {} seconds, primes found: {}", total.elapsed().as_secs(), results.len()); + println!("Completed in: {}, primes found: {}", get_elapsed_time(start), results.len()); } diff --git a/src/modulus.rs b/src/modulus.rs index ee01ef6..5045af6 100644 --- a/src/modulus.rs +++ b/src/modulus.rs @@ -1,7 +1,7 @@ // modulus.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use magic_numbers::M; +use crate::magic_numbers::M; use modinverse::modinverse; pub const MODULUS : OptiM = OptiM{}; diff --git a/src/mulmod/mod.rs b/src/mulmod.rs similarity index 81% rename from src/mulmod/mod.rs rename to src/mulmod.rs index c56508d..436ea91 100644 --- a/src/mulmod/mod.rs +++ b/src/mulmod.rs @@ -1,4 +1,4 @@ -// mod.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// bitset.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #[cfg(all(feature = "unstable", target_arch = "x86_64"))] diff --git a/src/numa_threadpool.rs b/src/numa_threadpool.rs index b6eabc3..29267ae 100644 --- a/src/numa_threadpool.rs +++ b/src/numa_threadpool.rs @@ -49,12 +49,12 @@ pub use self::numa::*; mod numa { extern crate nix; - use libc::{c_char, c_uint, c_int, c_ulong}; use std::ffi::CString; use std::sync::{Arc, Mutex, Condvar}; use std::thread::{JoinHandle, self}; use std::collections::VecDeque; use std::marker::Send; + use std::os::raw::{c_ulong, c_uint, c_int, c_char}; #[repr(C)] struct bitmask { @@ -148,7 +148,7 @@ mod numa { fn worker(node: &NodeInfo, cpu_id: c_uint, queue: &WorkQueue) { use self::nix::unistd::gettid; - use libc::pid_t; + use crate::numa_threadpool::numa::nix::libc::pid_t; let tid = pid_t::from(gettid()); unsafe { @@ -182,7 +182,7 @@ mod numa { if unsafe { numa_bitmask_isbitset(numa_all_nodes_ptr, i) } { let mut info = NodeInfo { node_id: (i as c_uint), cpuset: Vec::new(), context: context_ctor(i) }; - let mut bitmask = unsafe { numa_allocate_cpumask() }; + let bitmask = unsafe { numa_allocate_cpumask() }; if unsafe { numa_node_to_cpus(i, bitmask) } != 0 { panic!("numa_node_to_cpus"); } diff --git a/src/progress.rs b/src/progress.rs index 9742edb..e6b6dfa 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -3,6 +3,7 @@ use std::time::Instant; use std::sync::atomic::{Ordering, AtomicUsize}; +use crate::time::get_elapsed_time; pub struct ProgressReporter { desc: String, @@ -27,8 +28,8 @@ impl<'a> Drop for ProgressHandle<'a> { impl<'a> Drop for ProgressReporter { fn drop(&mut self) { - println!("[{}] Completed {} in {}s", self.desc, self.counter.load(Ordering::SeqCst), - self.start_time.elapsed().as_secs()); + println!("[{}] Completed {} in {}", self.desc, self.counter.load(Ordering::SeqCst), + get_elapsed_time(self.start_time)); } } diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..9753332 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,14 @@ +// time.rs Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Instant; + +#[cfg(feature = "unstable")] +pub fn get_elapsed_time(start: Instant) -> String { + return format!("{} ms", start.elapsed().as_millis()); +} + +#[cfg(not(feature = "unstable"))] +pub fn get_elapsed_time(start: Instant) -> String { + return format!("{} sec", start.elapsed().as_secs()); +} \ No newline at end of file