mozilla/rust — pull request #14174 by stepancheg (2014-05-13 10:25:57+00:00)
Title: Optimize common path of Once::doit

Submitting PR again, because I cannot reopen #13349, and github does not attach new patch to that PR.

=======

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
* 800ps per iteration when doit function is split into doit/doit_slow, and doit is #[inline]

Inlineable `doit` looks like this:

```
#[inline(always)]
pub fn doit(&self, f: ||) {
// Optimize common path: load is much cheaper than fetch_add.
if self.cnt.load(atomics::SeqCst) < 0 {
return
}

self.doit_slow(f);
}

fn doit_slow(&self, f: ||) { ... }
```