work around compiler overhead around lambdas in generics by extracting them into free functions

This commit is contained in:
The8472 2020-01-26 20:51:57 +01:00
parent 771b8ecc83
commit 872ab780c0

View file

@ -2212,6 +2212,34 @@ impl<T> SpecFrom<T, IntoIter<T>> for Vec<T> {
} }
} }
fn write_in_place<T>(src_end: *const T) -> impl FnMut(*mut T, T) -> Result<*mut T, !> {
move |mut dst, item| {
unsafe {
// the InPlaceIterable contract cannot be verified precisely here since
// try_fold has an exclusive reference to the source pointer
// all we can do is check if it's still in range
debug_assert!(dst as *const _ <= src_end, "InPlaceIterable contract violation");
ptr::write(dst, item);
dst = dst.add(1);
}
Ok(dst)
}
}
fn write_in_place_with_drop<T>(
src_end: *const T,
) -> impl FnMut(InPlaceDrop<T>, T) -> Result<InPlaceDrop<T>, !> {
move |mut sink, item| {
unsafe {
// same caveat as above
debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation");
ptr::write(sink.dst, item);
sink.dst = sink.dst.add(1);
}
Ok(sink)
}
}
// Further specialization potential once // Further specialization potential once
// https://github.com/rust-lang/rust/issues/62645 has been solved: // https://github.com/rust-lang/rust/issues/62645 has been solved:
// T can be split into IN and OUT which only need to have the same size and alignment // T can be split into IN and OUT which only need to have the same size and alignment
@ -2230,46 +2258,23 @@ where
let inner = unsafe { iterator.as_inner().as_into_iter() }; let inner = unsafe { iterator.as_inner().as_into_iter() };
(inner.buf.as_ptr(), inner.end, inner.cap) (inner.buf.as_ptr(), inner.end, inner.cap)
}; };
let dst = src_buf;
// use try-fold
// - it vectorizes better for some iterator adapters
// - unlike most internal iteration methods methods it only takes a &mut self
// - lets us thread the write pointer through its innards and get it back in the end
let dst = if mem::needs_drop::<T>() { let dst = if mem::needs_drop::<T>() {
// special-case drop handling since it prevents vectorization // special-case drop handling since it forces us to lug that extra field around which
let mut sink = InPlaceDrop { inner: src_buf, dst }; // can inhibit optimizations
let _ = iterator.try_for_each::<_, Result<_, !>>(|item| { let sink = InPlaceDrop { inner: src_buf, dst: src_buf };
unsafe { let sink = iterator
debug_assert!( .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(src_end))
sink.dst as *const _ <= src_end, .unwrap();
"InPlaceIterable contract violation"
);
ptr::write(sink.dst, item);
sink.dst = sink.dst.add(1);
}
Ok(())
});
// iteration succeeded, don't drop head // iteration succeeded, don't drop head
let sink = mem::ManuallyDrop::new(sink); let sink = mem::ManuallyDrop::new(sink);
sink.dst sink.dst
} else { } else {
// use try-fold iterator.try_fold::<_, _, Result<_, !>>(src_buf, write_in_place(src_end)).unwrap()
// - it vectorizes better
// - unlike most internal iteration methods methods it only takes a &mut self
// - lets us thread the write pointer through its innards and get it back in the end
iterator
.try_fold::<_, _, Result<_, !>>(dst, move |mut dst, item| {
unsafe {
// the InPlaceIterable contract cannot be verified precisely here since
// try_fold has an exclusive reference to the source pointer
// all we can do is check if it's still in range
debug_assert!(
dst as *const _ <= src_end,
"InPlaceIterable contract violation"
);
ptr::write(dst, item);
dst = dst.add(1);
}
Ok(dst)
})
.unwrap()
}; };
let src = unsafe { iterator.as_inner().as_into_iter() }; let src = unsafe { iterator.as_inner().as_into_iter() };