Skip to content

Commit 3e3ff4b

Browse files
committed
macro-inline len() and is_empty() to fix performance regressions
This also changes the IR for nth(), but the new IR actually looks nicer that the old (and it is one instruction shorter).
1 parent 1b3c6ba commit 3e3ff4b

File tree

1 file changed

+29
-20
lines changed

1 file changed

+29
-20
lines changed

src/libcore/slice/mod.rs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2337,14 +2337,30 @@ impl<'a, T> IntoIterator for &'a mut [T] {
23372337
}
23382338
}
23392339

2340+
// Inlining is_empty and len makes a huge performance difference
2341+
macro_rules! is_empty {
2342+
// The way we encode the length of a ZST iterator, this works both for ZST
2343+
// and non-ZST.
2344+
($self: expr) => {$self.ptr == $self.end}
2345+
}
2346+
macro_rules! len {
2347+
($T: ty, $self: expr) => {{
2348+
if mem::size_of::<$T>() == 0 {
2349+
($self.end as usize).wrapping_sub($self.ptr as usize)
2350+
} else {
2351+
$self.end.offset_from($self.ptr) as usize
2352+
}
2353+
}}
2354+
}
2355+
23402356
// The shared definition of the `Iter` and `IterMut` iterators
23412357
macro_rules! iterator {
23422358
(struct $name:ident -> $ptr:ty, $elem:ty, $raw_mut:tt, $( $mut_:tt )*) => {
23432359
impl<'a, T> $name<'a, T> {
23442360
// Helper function for creating a slice from the iterator.
23452361
#[inline(always)]
23462362
fn make_slice(&self) -> &'a [T] {
2347-
unsafe { from_raw_parts(self.ptr, self.len()) }
2363+
unsafe { from_raw_parts(self.ptr, len!(T, self)) }
23482364
}
23492365

23502366
// Helper function for moving the start of the iterator forwards by `offset` elements,
@@ -2382,20 +2398,12 @@ macro_rules! iterator {
23822398
impl<'a, T> ExactSizeIterator for $name<'a, T> {
23832399
#[inline(always)]
23842400
fn len(&self) -> usize {
2385-
let diff = (self.end as usize).wrapping_sub(self.ptr as usize);
2386-
if mem::size_of::<T>() == 0 {
2387-
// end is really ptr+len, so we are already done
2388-
diff
2389-
} else {
2390-
diff / mem::size_of::<T>()
2391-
}
2401+
unsafe { len!(T, self) }
23922402
}
23932403

23942404
#[inline(always)]
23952405
fn is_empty(&self) -> bool {
2396-
// The way we encode the length of a ZST iterator, this works both for ZST
2397-
// and non-ZST.
2398-
self.ptr == self.end
2406+
is_empty!(self)
23992407
}
24002408
}
24012409

@@ -2411,7 +2419,7 @@ macro_rules! iterator {
24112419
if mem::size_of::<T>() != 0 {
24122420
assume(!self.end.is_null());
24132421
}
2414-
if self.is_empty() {
2422+
if is_empty!(self) {
24152423
None
24162424
} else {
24172425
Some(& $( $mut_ )* *self.post_inc_start(1))
@@ -2421,7 +2429,7 @@ macro_rules! iterator {
24212429

24222430
#[inline]
24232431
fn size_hint(&self) -> (usize, Option<usize>) {
2424-
let exact = self.len();
2432+
let exact = unsafe { len!(T, self) };
24252433
(exact, Some(exact))
24262434
}
24272435

@@ -2432,7 +2440,7 @@ macro_rules! iterator {
24322440

24332441
#[inline]
24342442
fn nth(&mut self, n: usize) -> Option<$elem> {
2435-
if n >= self.len() {
2443+
if n >= unsafe { len!(T, self) } {
24362444
// This iterator is now empty.
24372445
if mem::size_of::<T>() == 0 {
24382446
// We have to do it this way as `ptr` may never be 0, but `end`
@@ -2463,13 +2471,13 @@ macro_rules! iterator {
24632471
// manual unrolling is needed when there are conditional exits from the loop
24642472
let mut accum = init;
24652473
unsafe {
2466-
while self.len() >= 4 {
2474+
while len!(T, self) >= 4 {
24672475
accum = f(accum, & $( $mut_ )* *self.post_inc_start(1))?;
24682476
accum = f(accum, & $( $mut_ )* *self.post_inc_start(1))?;
24692477
accum = f(accum, & $( $mut_ )* *self.post_inc_start(1))?;
24702478
accum = f(accum, & $( $mut_ )* *self.post_inc_start(1))?;
24712479
}
2472-
while !self.is_empty() {
2480+
while !is_empty!(self) {
24732481
accum = f(accum, & $( $mut_ )* *self.post_inc_start(1))?;
24742482
}
24752483
}
@@ -2539,7 +2547,7 @@ macro_rules! iterator {
25392547
if mem::size_of::<T>() != 0 {
25402548
assume(!self.end.is_null());
25412549
}
2542-
if self.is_empty() {
2550+
if is_empty!(self) {
25432551
None
25442552
} else {
25452553
Some(& $( $mut_ )* *self.pre_dec_end(1))
@@ -2554,13 +2562,14 @@ macro_rules! iterator {
25542562
// manual unrolling is needed when there are conditional exits from the loop
25552563
let mut accum = init;
25562564
unsafe {
2557-
while self.len() >= 4 {
2565+
while len!(T, self) >= 4 {
25582566
accum = f(accum, & $( $mut_ )* *self.pre_dec_end(1))?;
25592567
accum = f(accum, & $( $mut_ )* *self.pre_dec_end(1))?;
25602568
accum = f(accum, & $( $mut_ )* *self.pre_dec_end(1))?;
25612569
accum = f(accum, & $( $mut_ )* *self.pre_dec_end(1))?;
25622570
}
2563-
while !self.is_empty() {
2571+
// inlining is_empty everywhere makes a huge performance difference
2572+
while !is_empty!(self) {
25642573
accum = f(accum, & $( $mut_ )* *self.pre_dec_end(1))?;
25652574
}
25662575
}
@@ -2760,7 +2769,7 @@ impl<'a, T> IterMut<'a, T> {
27602769
/// ```
27612770
#[stable(feature = "iter_to_slice", since = "1.4.0")]
27622771
pub fn into_slice(self) -> &'a mut [T] {
2763-
unsafe { from_raw_parts_mut(self.ptr, self.len()) }
2772+
unsafe { from_raw_parts_mut(self.ptr, len!(T, self)) }
27642773
}
27652774
}
27662775

0 commit comments

Comments
 (0)