Skip to content

Commit 089c9c8

Browse files
committed
work around compiler overhead around lambdas in generics by extracting them into free functions
1 parent 6cf243a commit 089c9c8

File tree

1 file changed

+39
-34
lines changed

1 file changed

+39
-34
lines changed

src/liballoc/vec.rs

+39-34
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,34 @@ impl<T> SpecFrom<T, IntoIter<T>> for Vec<T> {
21072107
}
21082108
}
21092109

2110+
fn write_in_place<T>(src_end: *const T) -> impl FnMut(*mut T, T) -> Result<*mut T, !> {
2111+
move |mut dst, item| {
2112+
unsafe {
2113+
// the InPlaceIterable contract cannot be verified precisely here since
2114+
// try_fold has an exclusive reference to the source pointer
2115+
// all we can do is check if it's still in range
2116+
debug_assert!(dst as *const _ <= src_end, "InPlaceIterable contract violation");
2117+
ptr::write(dst, item);
2118+
dst = dst.add(1);
2119+
}
2120+
Ok(dst)
2121+
}
2122+
}
2123+
2124+
fn write_in_place_with_drop<T>(
2125+
src_end: *const T,
2126+
) -> impl FnMut(InPlaceDrop<T>, T) -> Result<InPlaceDrop<T>, !> {
2127+
move |mut sink, item| {
2128+
unsafe {
2129+
// same caveat as above
2130+
debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation");
2131+
ptr::write(sink.dst, item);
2132+
sink.dst = sink.dst.add(1);
2133+
}
2134+
Ok(sink)
2135+
}
2136+
}
2137+
21102138
// Further specialization potential once
21112139
// https://github.com/rust-lang/rust/issues/62645 has been solved:
21122140
// T can be split into IN and OUT which only need to have the same size and alignment
@@ -2125,46 +2153,23 @@ where
21252153
let inner = unsafe { iterator.as_inner().as_into_iter() };
21262154
(inner.buf.as_ptr(), inner.end, inner.cap)
21272155
};
2128-
let dst = src_buf;
21292156

2157+
// use try-fold
2158+
// - it vectorizes better for some iterator adapters
2159+
// - unlike most internal iteration methods methods it only takes a &mut self
2160+
// - lets us thread the write pointer through its innards and get it back in the end
21302161
let dst = if mem::needs_drop::<T>() {
2131-
// special-case drop handling since it prevents vectorization
2132-
let mut sink = InPlaceDrop { inner: src_buf, dst };
2133-
let _ = iterator.try_for_each::<_, Result<_, !>>(|item| {
2134-
unsafe {
2135-
debug_assert!(
2136-
sink.dst as *const _ <= src_end,
2137-
"InPlaceIterable contract violation"
2138-
);
2139-
ptr::write(sink.dst, item);
2140-
sink.dst = sink.dst.add(1);
2141-
}
2142-
Ok(())
2143-
});
2162+
// special-case drop handling since it forces us to lug that extra field around which
2163+
// can inhibit optimizations
2164+
let sink = InPlaceDrop { inner: src_buf, dst: src_buf };
2165+
let sink = iterator
2166+
.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(src_end))
2167+
.unwrap();
21442168
// iteration succeeded, don't drop head
21452169
let sink = mem::ManuallyDrop::new(sink);
21462170
sink.dst
21472171
} else {
2148-
// use try-fold
2149-
// - it vectorizes better
2150-
// - unlike most internal iteration methods methods it only takes a &mut self
2151-
// - lets us thread the write pointer through its innards and get it back in the end
2152-
iterator
2153-
.try_fold::<_, _, Result<_, !>>(dst, move |mut dst, item| {
2154-
unsafe {
2155-
// the InPlaceIterable contract cannot be verified precisely here since
2156-
// try_fold has an exclusive reference to the source pointer
2157-
// all we can do is check if it's still in range
2158-
debug_assert!(
2159-
dst as *const _ <= src_end,
2160-
"InPlaceIterable contract violation"
2161-
);
2162-
ptr::write(dst, item);
2163-
dst = dst.add(1);
2164-
}
2165-
Ok(dst)
2166-
})
2167-
.unwrap()
2172+
iterator.try_fold::<_, _, Result<_, !>>(src_buf, write_in_place(src_end)).unwrap()
21682173
};
21692174

21702175
let src = unsafe { iterator.as_inner().as_into_iter() };

0 commit comments

Comments
 (0)