Skip to content

Commit f853cf7

Browse files
committed
Optimize common path of Once::doit
Optimize `Once::doit`: perform optimistic check that initializtion is already completed. `load` is much cheaper than `fetch_add` at least on x86_64. Verified with this test: ``` static mut o: one::Once = one::ONCE_INIT; unsafe { loop { let start = time::precise_time_ns(); let iters = 50000000u64; for _ in range(0, iters) { o.doit(|| { println!("once!"); }); } let end = time::precise_time_ns(); let ps_per_iter = 1000 * (end - start) / iters; println!("{} ps per iter", ps_per_iter); // confuse the optimizer o.doit(|| { println!("once!"); }); } } ``` Test executed on Mac, Intel Core i7 2GHz. Result is: * 20ns per iteration without patch * 4ns per iteration with this patch applied Once.doit could be even faster (800ps per iteration), if `doit` function was split into a pair of `doit`/`doit_slow`, and `doit` marked as `#[inline]` like this: ``` #[inline(always)] pub fn doit(&self, f: ||) { if self.cnt.load(atomics::SeqCst) < 0 { return } self.doit_slow(f); } fn doit_slow(&self, f: ||) { ... } ```
1 parent db5ca23 commit f853cf7

File tree

1 file changed

+5
-0
lines changed

1 file changed

+5
-0
lines changed

src/libsync/one.rs

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ impl Once {
6464
/// When this function returns, it is guaranteed that some initialization
6565
/// has run and completed (it may not be the closure specified).
6666
pub fn doit(&self, f: ||) {
67+
// Optimize common path: load is much cheaper than fetch_add.
68+
if self.cnt.load(atomics::SeqCst) < 0 {
69+
return
70+
}
71+
6772
// Implementation-wise, this would seem like a fairly trivial primitive.
6873
// The stickler part is where our mutexes currently require an
6974
// allocation, and usage of a `Once` should't leak this allocation.

0 commit comments

Comments
 (0)