Skip to content

Commit c07a6ae

Browse files
committed
Auto merge of #37972 - bluss:iter-find-is-on-a-roll, r=sfackler
Improve the slice iterator's searching methods Improve all, any, find, position, rposition by explicitly unrolling the loop for the slice iterators. - Introduce a few extension methods and functions for raw pointers make the new code easy to express - Introduce helper methods `search_while, rsearch_while` that generalize all the searching methods LLVM doesn't unroll the loop in `.find()` by default (clang is the same), so performance benefits a lot from explicit unrolling here. An iterator method without conditional exits (like `.fold()`) does not need this on the other hand. One of the raw pointer extension methods is `fn post_inc(&mut self) -> Self` which is the rustic equivalent of “`ptr++`”, and it is a nice way to express the raw pointer loop (see commit 3). Specific development notes about `search_while`: I tried both computing an end pointer "rounded" to 4, as well as the `ptrdistance >= 4` loop condition, ptrdistance was better. I tried handling the last 0-3 elements unrolled or with a while loop, the loop was better.
2 parents bd8e9b0 + a54ddfb commit c07a6ae

File tree

1 file changed

+185
-14
lines changed

1 file changed

+185
-14
lines changed

src/libcore/slice.rs

+185-14
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,35 @@ macro_rules! slice_offset {
172172
($ptr:expr, $by:expr) => {{
173173
let ptr = $ptr;
174174
if size_from_ptr(ptr) == 0 {
175-
::intrinsics::arith_offset(ptr as *mut i8, $by) as *mut _
175+
(ptr as *mut i8).wrapping_offset($by) as _
176176
} else {
177177
ptr.offset($by)
178178
}
179179
}};
180180
}
181181

182-
macro_rules! slice_ref {
182+
// make a &T from a *const T
183+
macro_rules! make_ref {
184+
($ptr:expr) => {{
185+
let ptr = $ptr;
186+
if size_from_ptr(ptr) == 0 {
187+
// Use a non-null pointer value
188+
&*(1 as *mut _)
189+
} else {
190+
&*ptr
191+
}
192+
}};
193+
}
194+
195+
// make a &mut T from a *mut T
196+
macro_rules! make_ref_mut {
183197
($ptr:expr) => {{
184198
let ptr = $ptr;
185199
if size_from_ptr(ptr) == 0 {
186200
// Use a non-null pointer value
187201
&mut *(1 as *mut _)
188202
} else {
189-
mem::transmute(ptr)
203+
&mut *ptr
190204
}
191205
}};
192206
}
@@ -963,7 +977,7 @@ fn size_from_ptr<T>(_: *const T) -> usize {
963977

964978
// The shared definition of the `Iter` and `IterMut` iterators
965979
macro_rules! iterator {
966-
(struct $name:ident -> $ptr:ty, $elem:ty) => {
980+
(struct $name:ident -> $ptr:ty, $elem:ty, $mkref:ident) => {
967981
#[stable(feature = "rust1", since = "1.0.0")]
968982
impl<'a, T> Iterator for $name<'a, T> {
969983
type Item = $elem;
@@ -979,18 +993,14 @@ macro_rules! iterator {
979993
if self.ptr == self.end {
980994
None
981995
} else {
982-
let old = self.ptr;
983-
self.ptr = slice_offset!(self.ptr, 1);
984-
Some(slice_ref!(old))
996+
Some($mkref!(self.ptr.post_inc()))
985997
}
986998
}
987999
}
9881000

9891001
#[inline]
9901002
fn size_hint(&self) -> (usize, Option<usize>) {
991-
let diff = (self.end as usize).wrapping_sub(self.ptr as usize);
992-
let size = mem::size_of::<T>();
993-
let exact = diff / (if size == 0 {1} else {size});
1003+
let exact = ptrdistance(self.ptr, self.end);
9941004
(exact, Some(exact))
9951005
}
9961006

@@ -1009,6 +1019,64 @@ macro_rules! iterator {
10091019
fn last(mut self) -> Option<$elem> {
10101020
self.next_back()
10111021
}
1022+
1023+
fn all<F>(&mut self, mut predicate: F) -> bool
1024+
where F: FnMut(Self::Item) -> bool,
1025+
{
1026+
self.search_while(true, move |elt| {
1027+
if predicate(elt) {
1028+
SearchWhile::Continue
1029+
} else {
1030+
SearchWhile::Done(false)
1031+
}
1032+
})
1033+
}
1034+
1035+
fn any<F>(&mut self, mut predicate: F) -> bool
1036+
where F: FnMut(Self::Item) -> bool,
1037+
{
1038+
!self.all(move |elt| !predicate(elt))
1039+
}
1040+
1041+
fn find<F>(&mut self, mut predicate: F) -> Option<Self::Item>
1042+
where F: FnMut(&Self::Item) -> bool,
1043+
{
1044+
self.search_while(None, move |elt| {
1045+
if predicate(&elt) {
1046+
SearchWhile::Done(Some(elt))
1047+
} else {
1048+
SearchWhile::Continue
1049+
}
1050+
})
1051+
}
1052+
1053+
fn position<F>(&mut self, mut predicate: F) -> Option<usize>
1054+
where F: FnMut(Self::Item) -> bool,
1055+
{
1056+
let mut index = 0;
1057+
self.search_while(None, move |elt| {
1058+
if predicate(elt) {
1059+
SearchWhile::Done(Some(index))
1060+
} else {
1061+
index += 1;
1062+
SearchWhile::Continue
1063+
}
1064+
})
1065+
}
1066+
1067+
fn rposition<F>(&mut self, mut predicate: F) -> Option<usize>
1068+
where F: FnMut(Self::Item) -> bool,
1069+
{
1070+
let mut index = self.len();
1071+
self.rsearch_while(None, move |elt| {
1072+
index -= 1;
1073+
if predicate(elt) {
1074+
SearchWhile::Done(Some(index))
1075+
} else {
1076+
SearchWhile::Continue
1077+
}
1078+
})
1079+
}
10121080
}
10131081

10141082
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1024,10 +1092,51 @@ macro_rules! iterator {
10241092
if self.end == self.ptr {
10251093
None
10261094
} else {
1027-
self.end = slice_offset!(self.end, -1);
1028-
Some(slice_ref!(self.end))
1095+
Some($mkref!(self.end.pre_dec()))
1096+
}
1097+
}
1098+
}
1099+
}
1100+
1101+
// search_while is a generalization of the internal iteration methods.
1102+
impl<'a, T> $name<'a, T> {
1103+
// search through the iterator's element using the closure `g`.
1104+
// if no element was found, return `default`.
1105+
fn search_while<Acc, G>(&mut self, default: Acc, mut g: G) -> Acc
1106+
where Self: Sized,
1107+
G: FnMut($elem) -> SearchWhile<Acc>
1108+
{
1109+
// manual unrolling is needed when there are conditional exits from the loop
1110+
unsafe {
1111+
while ptrdistance(self.ptr, self.end) >= 4 {
1112+
search_while!(g($mkref!(self.ptr.post_inc())));
1113+
search_while!(g($mkref!(self.ptr.post_inc())));
1114+
search_while!(g($mkref!(self.ptr.post_inc())));
1115+
search_while!(g($mkref!(self.ptr.post_inc())));
1116+
}
1117+
while self.ptr != self.end {
1118+
search_while!(g($mkref!(self.ptr.post_inc())));
1119+
}
1120+
}
1121+
default
1122+
}
1123+
1124+
fn rsearch_while<Acc, G>(&mut self, default: Acc, mut g: G) -> Acc
1125+
where Self: Sized,
1126+
G: FnMut($elem) -> SearchWhile<Acc>
1127+
{
1128+
unsafe {
1129+
while ptrdistance(self.ptr, self.end) >= 4 {
1130+
search_while!(g($mkref!(self.end.pre_dec())));
1131+
search_while!(g($mkref!(self.end.pre_dec())));
1132+
search_while!(g($mkref!(self.end.pre_dec())));
1133+
search_while!(g($mkref!(self.end.pre_dec())));
1134+
}
1135+
while self.ptr != self.end {
1136+
search_while!(g($mkref!(self.end.pre_dec())));
10291137
}
10301138
}
1139+
default
10311140
}
10321141
}
10331142
}
@@ -1061,6 +1170,24 @@ macro_rules! make_mut_slice {
10611170
}}
10621171
}
10631172

1173+
// An enum used for controlling the execution of `.search_while()`.
1174+
enum SearchWhile<T> {
1175+
// Continue searching
1176+
Continue,
1177+
// Fold is complete and will return this value
1178+
Done(T),
1179+
}
1180+
1181+
// helper macro for search while's control flow
1182+
macro_rules! search_while {
1183+
($e:expr) => {
1184+
match $e {
1185+
SearchWhile::Continue => { }
1186+
SearchWhile::Done(done) => return done,
1187+
}
1188+
}
1189+
}
1190+
10641191
/// Immutable slice iterator
10651192
///
10661193
/// This struct is created by the [`iter`] method on [slices].
@@ -1147,7 +1274,7 @@ impl<'a, T> Iter<'a, T> {
11471274
}
11481275
}
11491276

1150-
iterator!{struct Iter -> *const T, &'a T}
1277+
iterator!{struct Iter -> *const T, &'a T, make_ref}
11511278

11521279
#[stable(feature = "rust1", since = "1.0.0")]
11531280
impl<'a, T> ExactSizeIterator for Iter<'a, T> {
@@ -1275,7 +1402,7 @@ impl<'a, T> IterMut<'a, T> {
12751402
}
12761403
}
12771404

1278-
iterator!{struct IterMut -> *mut T, &'a mut T}
1405+
iterator!{struct IterMut -> *mut T, &'a mut T, make_ref_mut}
12791406

12801407
#[stable(feature = "rust1", since = "1.0.0")]
12811408
impl<'a, T> ExactSizeIterator for IterMut<'a, T> {
@@ -1290,6 +1417,50 @@ impl<'a, T> FusedIterator for IterMut<'a, T> {}
12901417
#[unstable(feature = "trusted_len", issue = "37572")]
12911418
unsafe impl<'a, T> TrustedLen for IterMut<'a, T> {}
12921419

1420+
1421+
// Return the number of elements of `T` from `start` to `end`.
1422+
// Return the arithmetic difference if `T` is zero size.
1423+
#[inline(always)]
1424+
fn ptrdistance<T>(start: *const T, end: *const T) -> usize {
1425+
let diff = (end as usize).wrapping_sub(start as usize);
1426+
let size = mem::size_of::<T>();
1427+
diff / (if size == 0 { 1 } else { size })
1428+
}
1429+
1430+
// Extension methods for raw pointers, used by the iterators
1431+
trait PointerExt : Copy {
1432+
unsafe fn slice_offset(self, i: isize) -> Self;
1433+
1434+
/// Increment self by 1, but return the old value
1435+
#[inline(always)]
1436+
unsafe fn post_inc(&mut self) -> Self {
1437+
let current = *self;
1438+
*self = self.slice_offset(1);
1439+
current
1440+
}
1441+
1442+
/// Decrement self by 1, and return the new value
1443+
#[inline(always)]
1444+
unsafe fn pre_dec(&mut self) -> Self {
1445+
*self = self.slice_offset(-1);
1446+
*self
1447+
}
1448+
}
1449+
1450+
impl<T> PointerExt for *const T {
1451+
#[inline(always)]
1452+
unsafe fn slice_offset(self, i: isize) -> Self {
1453+
slice_offset!(self, i)
1454+
}
1455+
}
1456+
1457+
impl<T> PointerExt for *mut T {
1458+
#[inline(always)]
1459+
unsafe fn slice_offset(self, i: isize) -> Self {
1460+
slice_offset!(self, i)
1461+
}
1462+
}
1463+
12931464
/// An internal abstraction over the splitting iterators, so that
12941465
/// splitn, splitn_mut etc can be implemented once.
12951466
#[doc(hidden)]

0 commit comments

Comments
 (0)