Skip to content

Commit 872ab78

Browse files
committed
work around compiler overhead around lambdas in generics by extracting them into free functions
1 parent 771b8ec commit 872ab78

File tree

1 file changed

+39
-34
lines changed

1 file changed

+39
-34
lines changed

library/alloc/src/vec.rs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,6 +2212,34 @@ impl<T> SpecFrom<T, IntoIter<T>> for Vec<T> {
22122212
}
22132213
}
22142214

2215+
fn write_in_place<T>(src_end: *const T) -> impl FnMut(*mut T, T) -> Result<*mut T, !> {
2216+
move |mut dst, item| {
2217+
unsafe {
2218+
// the InPlaceIterable contract cannot be verified precisely here since
2219+
// try_fold has an exclusive reference to the source pointer
2220+
// all we can do is check if it's still in range
2221+
debug_assert!(dst as *const _ <= src_end, "InPlaceIterable contract violation");
2222+
ptr::write(dst, item);
2223+
dst = dst.add(1);
2224+
}
2225+
Ok(dst)
2226+
}
2227+
}
2228+
2229+
fn write_in_place_with_drop<T>(
2230+
src_end: *const T,
2231+
) -> impl FnMut(InPlaceDrop<T>, T) -> Result<InPlaceDrop<T>, !> {
2232+
move |mut sink, item| {
2233+
unsafe {
2234+
// same caveat as above
2235+
debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation");
2236+
ptr::write(sink.dst, item);
2237+
sink.dst = sink.dst.add(1);
2238+
}
2239+
Ok(sink)
2240+
}
2241+
}
2242+
22152243
// Further specialization potential once
22162244
// https://github.com/rust-lang/rust/issues/62645 has been solved:
22172245
// T can be split into IN and OUT which only need to have the same size and alignment
@@ -2230,46 +2258,23 @@ where
22302258
let inner = unsafe { iterator.as_inner().as_into_iter() };
22312259
(inner.buf.as_ptr(), inner.end, inner.cap)
22322260
};
2233-
let dst = src_buf;
22342261

2262+
// use try-fold
2263+
// - it vectorizes better for some iterator adapters
2264+
// - unlike most internal iteration methods methods it only takes a &mut self
2265+
// - lets us thread the write pointer through its innards and get it back in the end
22352266
let dst = if mem::needs_drop::<T>() {
2236-
// special-case drop handling since it prevents vectorization
2237-
let mut sink = InPlaceDrop { inner: src_buf, dst };
2238-
let _ = iterator.try_for_each::<_, Result<_, !>>(|item| {
2239-
unsafe {
2240-
debug_assert!(
2241-
sink.dst as *const _ <= src_end,
2242-
"InPlaceIterable contract violation"
2243-
);
2244-
ptr::write(sink.dst, item);
2245-
sink.dst = sink.dst.add(1);
2246-
}
2247-
Ok(())
2248-
});
2267+
// special-case drop handling since it forces us to lug that extra field around which
2268+
// can inhibit optimizations
2269+
let sink = InPlaceDrop { inner: src_buf, dst: src_buf };
2270+
let sink = iterator
2271+
.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(src_end))
2272+
.unwrap();
22492273
// iteration succeeded, don't drop head
22502274
let sink = mem::ManuallyDrop::new(sink);
22512275
sink.dst
22522276
} else {
2253-
// use try-fold
2254-
// - it vectorizes better
2255-
// - unlike most internal iteration methods methods it only takes a &mut self
2256-
// - lets us thread the write pointer through its innards and get it back in the end
2257-
iterator
2258-
.try_fold::<_, _, Result<_, !>>(dst, move |mut dst, item| {
2259-
unsafe {
2260-
// the InPlaceIterable contract cannot be verified precisely here since
2261-
// try_fold has an exclusive reference to the source pointer
2262-
// all we can do is check if it's still in range
2263-
debug_assert!(
2264-
dst as *const _ <= src_end,
2265-
"InPlaceIterable contract violation"
2266-
);
2267-
ptr::write(dst, item);
2268-
dst = dst.add(1);
2269-
}
2270-
Ok(dst)
2271-
})
2272-
.unwrap()
2277+
iterator.try_fold::<_, _, Result<_, !>>(src_buf, write_in_place(src_end)).unwrap()
22732278
};
22742279

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

0 commit comments

Comments
 (0)