From 7871f4e475136ffd0dc8845ce848c643a5982a01 Mon Sep 17 00:00:00 2001 From: Kamal Marhubi Date: Thu, 3 Nov 2016 23:10:21 -0400 Subject: [PATCH 1/3] atomic64: Move module to its own directory Preparation for refactoring to separate modules for each implementation. This commit will make later diffs easier to read. --- src/{atomic64.rs => atomic64/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{atomic64.rs => atomic64/mod.rs} (100%) diff --git a/src/atomic64.rs b/src/atomic64/mod.rs similarity index 100% rename from src/atomic64.rs rename to src/atomic64/mod.rs From 4d3d81878942934e28d15ac3705c353065faba41 Mon Sep 17 00:00:00 2001 From: Kamal Marhubi Date: Thu, 3 Nov 2016 23:51:17 -0400 Subject: [PATCH 2/3] atomic64: Move implementations to their own files Conditionally use the relevant file to separate out the different implementations. --- Cargo.toml | 1 + src/atomic64/atomic.rs | 63 +++++++++++++++++++ src/atomic64/mod.rs | 139 ++++++++++++----------------------------- src/atomic64/rwlock.rs | 46 ++++++++++++++ src/lib.rs | 2 + 5 files changed, 153 insertions(+), 98 deletions(-) create mode 100644 src/atomic64/atomic.rs create mode 100644 src/atomic64/rwlock.rs diff --git a/Cargo.toml b/Cargo.toml index 937246b8..ef73007f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ fnv = "1.0.3" lazy_static = "0.2.1" libc = "0.2" regex = "0.1" +cfg-if = "0.1" [dependencies.hyper] version = "0.9" diff --git a/src/atomic64/atomic.rs b/src/atomic64/atomic.rs new file mode 100644 index 00000000..e48dc79e --- /dev/null +++ b/src/atomic64/atomic.rs @@ -0,0 +1,63 @@ +use std::sync::atomic::{AtomicU64, Ordering}; +use std::mem::transmute; + +pub struct F64 { + inner: AtomicU64, +} + +impl F64 { + pub fn new(val: f64) -> F64 { + F64 { inner: AtomicU64::new(f64_to_u64(val)) } + } + + #[inline] + pub fn get(&self) -> f64 { + u64_to_f64(self.inner.load(Ordering::Relaxed)) + } + + #[inline] + pub fn set(&self, val: f64) { + self.inner.store(f64_to_u64(val), Ordering::Relaxed) + } + + #[inline] + pub fn inc_by(&self, delta: f64) { + loop { + let current = self.inner.load(Ordering::Acquire); + let new = u64_to_f64(current) + delta; + let swapped = self.inner + .compare_and_swap(current, f64_to_u64(new), Ordering::Release); + if swapped == current { + return; + } + } + } +} + +fn u64_to_f64(val: u64) -> f64 { + unsafe { transmute(val) } +} + +fn f64_to_u64(val: f64) -> u64 { + unsafe { transmute(val) } +} + +pub struct U64 { + inner: AtomicU64, +} + +impl U64 { + pub fn new(val: u64) -> U64 { + U64 { inner: AtomicU64::new(val) } + } + + #[inline] + pub fn get(&self) -> u64 { + self.inner.load(Ordering::Acquire) + } + + #[inline] + pub fn inc_by(&self, delta: u64) { + self.inner.fetch_add(delta, Ordering::Release); + } +} diff --git a/src/atomic64/mod.rs b/src/atomic64/mod.rs index e0c109f2..ddec7919 100644 --- a/src/atomic64/mod.rs +++ b/src/atomic64/mod.rs @@ -12,129 +12,72 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(not(feature = "nightly"))] -pub use self::rwlock::{RwlockF64 as AtomicF64, RwlockU64 as AtomicU64}; -#[cfg(feature = "nightly")] -pub use self::atomic::{AtomicF64, AtomicU64}; - -#[cfg(not(feature = "nightly"))] -mod rwlock { - use std::sync::RwLock; - - pub struct RwlockF64 { - inner: RwLock, +cfg_if! { + if #[cfg(feature = "nightly")] { + // Prefer AtomicU64 if available. + #[path = "atomic.rs"] + mod imp; + } else { + // Fall back to RwLock based version. + #[path = "rwlock.rs"] + mod imp; } +} - impl RwlockF64 { - pub fn new(val: f64) -> RwlockF64 { - RwlockF64 { inner: RwLock::new(val) } - } - #[inline] - pub fn set(&self, val: f64) { - *self.inner.write().unwrap() = val; - } +pub struct AtomicF64 { + inner: imp::F64, +} - #[inline] - pub fn get(&self) -> f64 { - *self.inner.read().unwrap() - } - #[inline] - pub fn inc_by(&self, delta: f64) { - *self.inner.write().unwrap() += delta; +impl AtomicF64 { + pub fn new(val: f64) -> AtomicF64 { + AtomicF64 { + inner: imp::F64::new(val), } } - pub struct RwlockU64 { - inner: RwLock, + #[inline] + pub fn get(&self) -> f64 { + self.inner.get() } - impl RwlockU64 { - pub fn new(val: u64) -> RwlockU64 { - RwlockU64 { inner: RwLock::new(val) } - } - - #[inline] - pub fn get(&self) -> u64 { - *self.inner.read().unwrap() - } - - #[inline] - pub fn inc_by(&self, delta: u64) { - *self.inner.write().unwrap() += delta; - } + #[inline] + pub fn set(&self, val: f64) { + self.inner.set(val) } -} - -#[cfg(feature = "nightly")] -mod atomic { - use std::sync::atomic::{AtomicU64 as StdAtomicU64, Ordering}; - use std::mem::transmute; - pub struct AtomicF64 { - inner: StdAtomicU64, + #[inline] + pub fn inc_by(&self, delta: f64) { + self.inner.inc_by(delta) } +} - impl AtomicF64 { - pub fn new(val: f64) -> AtomicF64 { - AtomicF64 { inner: StdAtomicU64::new(f64_to_u64(val)) } - } - - #[inline] - pub fn get(&self) -> f64 { - u64_to_f64(self.inner.load(Ordering::Relaxed)) - } - #[inline] - pub fn set(&self, val: f64) { - self.inner.store(f64_to_u64(val), Ordering::Relaxed) - } +pub struct AtomicU64 { + inner: imp::U64, +} - #[inline] - pub fn inc_by(&self, delta: f64) { - loop { - let current = self.inner.load(Ordering::Acquire); - let new = u64_to_f64(current) + delta; - let swapped = self.inner - .compare_and_swap(current, f64_to_u64(new), Ordering::Release); - if swapped == current { - return; - } - } +impl AtomicU64 { + pub fn new(val: u64) -> AtomicU64 { + AtomicU64 { + inner: imp::U64::new(val), } } - fn u64_to_f64(val: u64) -> f64 { - unsafe { transmute(val) } - } - - fn f64_to_u64(val: f64) -> u64 { - unsafe { transmute(val) } + #[inline] + pub fn get(&self) -> u64 { + self.inner.get() } - pub struct AtomicU64 { - inner: StdAtomicU64, - } - - impl AtomicU64 { - pub fn new(val: u64) -> AtomicU64 { - AtomicU64 { inner: StdAtomicU64::new(val) } - } - - #[inline] - pub fn get(&self) -> u64 { - self.inner.load(Ordering::Acquire) - } - - #[inline] - pub fn inc_by(&self, delta: u64) { - self.inner.fetch_add(delta, Ordering::Release); - } + #[inline] + pub fn inc_by(&self, delta: u64) { + self.inner.inc_by(delta) } } + #[cfg(test)] mod test { use std::f64::consts::PI; diff --git a/src/atomic64/rwlock.rs b/src/atomic64/rwlock.rs new file mode 100644 index 00000000..56ac3ba9 --- /dev/null +++ b/src/atomic64/rwlock.rs @@ -0,0 +1,46 @@ +use std::sync::RwLock; + +pub struct F64 { + inner: RwLock, +} + +impl F64 { + pub fn new(val: f64) -> F64 { + F64 { inner: RwLock::new(val) } + } + + #[inline] + pub fn set(&self, val: f64) { + *self.inner.write().unwrap() = val; + } + + #[inline] + pub fn get(&self) -> f64 { + *self.inner.read().unwrap() + } + + #[inline] + pub fn inc_by(&self, delta: f64) { + *self.inner.write().unwrap() += delta; + } +} + +pub struct U64 { + inner: RwLock, +} + +impl U64 { + pub fn new(val: u64) -> U64 { + U64 { inner: RwLock::new(val) } + } + + #[inline] + pub fn get(&self) -> u64 { + *self.inner.read().unwrap() + } + + #[inline] + pub fn inc_by(&self, delta: u64) { + *self.inner.write().unwrap() += delta; + } +} diff --git a/src/lib.rs b/src/lib.rs index f9a07995..f3de43ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ #![cfg_attr(feature="dev", plugin(clippy))] #![cfg_attr(feature="nightly", feature(integer_atomics))] +#[macro_use] +extern crate cfg_if; #[macro_use] extern crate quick_error; extern crate protobuf; From f6a4a0e6661098dd633ff5637814e920b69e2a64 Mon Sep 17 00:00:00 2001 From: Kamal Marhubi Date: Fri, 4 Nov 2016 00:51:23 -0400 Subject: [PATCH 3/3] atomic64: Use AtomicUsize on 64 bit targets This builds on the work in #64 to allow stable Rust users to get atomics if they run on 64-bit architectures. Benchmark improvements: name old ns/iter new ns/iter diff ns/iter diff % counter::bench_counter_no_labels 49 16 -33 -67.35% counter::bench_counter_with_label_values 195 139 -56 -28.72% counter::bench_counter_with_mapped_labels 715 371 -344 -48.11% counter::bench_counter_with_prepared_mapped_labels 436 219 -217 -49.77% gauge::bench_gauge_no_labels 48 15 -33 -68.75% gauge::bench_gauge_with_label_values 167 115 -52 -31.14% histogram::bench_histogram_no_labels 179 30 -149 -83.24% histogram::bench_histogram_timer 276 138 -138 -50.00% histogram::bench_histogram_with_label_values 363 137 -226 -62.26% Benchmarks run with default features (no "nightly" feature) on nightly-2016-09-22, which corresponds roughly to 1.12.0. Tested: Ran tests on the following combinations (all unknown-linux-gnu): - stable Rust x86_64 - stable Rust i686 - nightly Rust x86_64 - nightly Rust i686 - nightly Rust x86_64 with `--features=nightly` - nightly Rust i686 with `--features=nightly` --- src/atomic64/atomic.rs | 18 +++++++++--------- src/atomic64/mod.rs | 12 ++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/atomic64/atomic.rs b/src/atomic64/atomic.rs index e48dc79e..b061629d 100644 --- a/src/atomic64/atomic.rs +++ b/src/atomic64/atomic.rs @@ -1,13 +1,13 @@ -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::atomic::Ordering; use std::mem::transmute; pub struct F64 { - inner: AtomicU64, + inner: super::AtomicU64Type, } impl F64 { pub fn new(val: f64) -> F64 { - F64 { inner: AtomicU64::new(f64_to_u64(val)) } + F64 { inner: super::AtomicU64Type::new(f64_to_u64(val)) } } #[inline] @@ -34,30 +34,30 @@ impl F64 { } } -fn u64_to_f64(val: u64) -> f64 { +fn u64_to_f64(val: super::U64Type) -> f64 { unsafe { transmute(val) } } -fn f64_to_u64(val: f64) -> u64 { +fn f64_to_u64(val: f64) -> super::U64Type { unsafe { transmute(val) } } pub struct U64 { - inner: AtomicU64, + inner: super::AtomicU64Type, } impl U64 { pub fn new(val: u64) -> U64 { - U64 { inner: AtomicU64::new(val) } + U64 { inner: super::AtomicU64Type::new(val as super::U64Type) } } #[inline] pub fn get(&self) -> u64 { - self.inner.load(Ordering::Acquire) + self.inner.load(Ordering::Acquire) as u64 } #[inline] pub fn inc_by(&self, delta: u64) { - self.inner.fetch_add(delta, Ordering::Release); + self.inner.fetch_add(delta as super::U64Type, Ordering::Release); } } diff --git a/src/atomic64/mod.rs b/src/atomic64/mod.rs index ddec7919..9b98200a 100644 --- a/src/atomic64/mod.rs +++ b/src/atomic64/mod.rs @@ -16,6 +16,18 @@ cfg_if! { if #[cfg(feature = "nightly")] { // Prefer AtomicU64 if available. + type AtomicU64Type = ::std::sync::atomic::AtomicU64; + type U64Type = u64; + + #[path = "atomic.rs"] + mod imp; + } else if #[cfg(target_pointer_width = "64")] { + // Use AtomicUsize if pointer width is 64 bit. This *may* have issues if + // a target has 64 bit pointer width but no atomic pointer-sized type, + // but that is not the case for any of the major architectures. + type AtomicU64Type = ::std::sync::atomic::AtomicUsize; + type U64Type = usize; + #[path = "atomic.rs"] mod imp; } else {