Skip to content

Commit b1dbf15

Browse files
committed
shared_from_iter/Arc: Use specialization to elide allocation.
1 parent 59ecff9 commit b1dbf15

File tree

1 file changed

+167
-40
lines changed

1 file changed

+167
-40
lines changed

src/liballoc/sync.rs

+167-40
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
1212
use core::borrow;
1313
use core::fmt;
1414
use core::cmp::{self, Ordering};
15+
use core::iter;
1516
use core::intrinsics::abort;
1617
use core::mem::{self, align_of, align_of_val, size_of_val};
1718
use core::ops::{Deref, Receiver, CoerceUnsized, DispatchFromDyn};
@@ -21,7 +22,7 @@ use core::marker::{Unpin, Unsize, PhantomData};
2122
use core::hash::{Hash, Hasher};
2223
use core::{isize, usize};
2324
use core::convert::From;
24-
use core::slice::from_raw_parts_mut;
25+
use core::slice::{self, from_raw_parts_mut};
2526

2627
use crate::alloc::{Global, Alloc, Layout, box_free, handle_alloc_error};
2728
use crate::boxed::Box;
@@ -587,21 +588,28 @@ impl<T: ?Sized> Arc<T> {
587588
}
588589

589590
impl<T: ?Sized> Arc<T> {
590-
// Allocates an `ArcInner<T>` with sufficient space for an unsized value
591-
unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner<T> {
592-
// Calculate layout using the given value.
591+
// Allocates an `ArcInner<T>` with sufficient space for
592+
// an unsized value where the value has the layout provided.
593+
//
594+
// The function `mem_to_arcinner` is called with the data pointer
595+
// and must return back a (potentially fat)-pointer for the `ArcInner<T>`.
596+
unsafe fn allocate_for_unsized(
597+
value_layout: Layout,
598+
mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>
599+
) -> *mut ArcInner<T> {
600+
// Calculate layout using the given value layout.
593601
// Previously, layout was calculated on the expression
594602
// `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
595603
// reference (see #54908).
596604
let layout = Layout::new::<ArcInner<()>>()
597-
.extend(Layout::for_value(&*ptr)).unwrap().0
605+
.extend(value_layout).unwrap().0
598606
.pad_to_align().unwrap();
599607

600608
let mem = Global.alloc(layout)
601609
.unwrap_or_else(|_| handle_alloc_error(layout));
602610

603611
// Initialize the ArcInner
604-
let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner<T>;
612+
let inner = mem_to_arcinner(mem.as_ptr());
605613
debug_assert_eq!(Layout::for_value(&*inner), layout);
606614

607615
ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1));
@@ -610,6 +618,15 @@ impl<T: ?Sized> Arc<T> {
610618
inner
611619
}
612620

621+
// Allocates an `ArcInner<T>` with sufficient space for an unsized value
622+
unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner<T> {
623+
// Allocate for the `ArcInner<T>` using the given value.
624+
Self::allocate_for_unsized(
625+
Layout::for_value(&*ptr),
626+
|mem| set_data_ptr(ptr as *mut T, mem) as *mut ArcInner<T>,
627+
)
628+
}
629+
613630
fn from_box(v: Box<T>) -> Arc<T> {
614631
unsafe {
615632
let box_unique = Box::into_unique(v);
@@ -632,6 +649,32 @@ impl<T: ?Sized> Arc<T> {
632649
}
633650
}
634651

652+
impl<T> Arc<[T]> {
653+
// Allocates an `ArcInner<[T]>` with the given length.
654+
unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> {
655+
// FIXME(#60667): Deduplicate.
656+
fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
657+
#[repr(C)]
658+
union Repr<T> {
659+
rust_mut: *mut [T],
660+
raw: FatPtr<T>,
661+
}
662+
663+
#[repr(C)]
664+
struct FatPtr<T> {
665+
data: *const T,
666+
len: usize,
667+
}
668+
unsafe { Repr { raw: FatPtr { data, len } }.rust_mut }
669+
}
670+
671+
Self::allocate_for_unsized(
672+
Layout::array::<T>(len).unwrap(),
673+
|mem| slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]>,
674+
)
675+
}
676+
}
677+
635678
// Sets the data pointer of a `?Sized` raw pointer.
636679
//
637680
// For a slice/trait object, this sets the `data` field and leaves the rest
@@ -646,8 +689,7 @@ impl<T> Arc<[T]> {
646689
//
647690
// Unsafe because the caller must either take ownership or bind `T: Copy`
648691
unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> {
649-
let v_ptr = v as *const [T];
650-
let ptr = Self::allocate_for_ptr(v_ptr);
692+
let ptr = Self::allocate_for_slice(v.len());
651693

652694
ptr::copy_nonoverlapping(
653695
v.as_ptr(),
@@ -656,16 +698,11 @@ impl<T> Arc<[T]> {
656698

657699
Self::from_ptr(ptr)
658700
}
659-
}
660701

661-
// Specialization trait used for From<&[T]>
662-
trait ArcFromSlice<T> {
663-
fn from_slice(slice: &[T]) -> Self;
664-
}
665-
666-
impl<T: Clone> ArcFromSlice<T> for Arc<[T]> {
667-
#[inline]
668-
default fn from_slice(v: &[T]) -> Self {
702+
/// Constructs an `Arc<[T]>` from an iterator known to be of a certain size.
703+
///
704+
/// Behavior is undefined should the size be wrong.
705+
unsafe fn from_iter_exact(iter: impl iter::Iterator<Item = T>, len: usize) -> Arc<[T]> {
669706
// Panic guard while cloning T elements.
670707
// In the event of a panic, elements that have been written
671708
// into the new ArcInner will be dropped, then the memory freed.
@@ -687,32 +724,43 @@ impl<T: Clone> ArcFromSlice<T> for Arc<[T]> {
687724
}
688725
}
689726

690-
unsafe {
691-
let v_ptr = v as *const [T];
692-
let ptr = Self::allocate_for_ptr(v_ptr);
727+
let ptr = Self::allocate_for_slice(len);
728+
729+
let mem = ptr as *mut _ as *mut u8;
730+
let layout = Layout::for_value(&*ptr);
693731

694-
let mem = ptr as *mut _ as *mut u8;
695-
let layout = Layout::for_value(&*ptr);
732+
// Pointer to first element
733+
let elems = &mut (*ptr).data as *mut [T] as *mut T;
696734

697-
// Pointer to first element
698-
let elems = &mut (*ptr).data as *mut [T] as *mut T;
735+
let mut guard = Guard {
736+
mem: NonNull::new_unchecked(mem),
737+
elems,
738+
layout,
739+
n_elems: 0,
740+
};
699741

700-
let mut guard = Guard{
701-
mem: NonNull::new_unchecked(mem),
702-
elems: elems,
703-
layout: layout,
704-
n_elems: 0,
705-
};
742+
for (i, item) in iter.enumerate() {
743+
ptr::write(elems.add(i), item);
744+
guard.n_elems += 1;
745+
}
706746

707-
for (i, item) in v.iter().enumerate() {
708-
ptr::write(elems.add(i), item.clone());
709-
guard.n_elems += 1;
710-
}
747+
// All clear. Forget the guard so it doesn't free the new ArcInner.
748+
mem::forget(guard);
711749

712-
// All clear. Forget the guard so it doesn't free the new ArcInner.
713-
mem::forget(guard);
750+
Self::from_ptr(ptr)
751+
}
752+
}
714753

715-
Self::from_ptr(ptr)
754+
// Specialization trait used for From<&[T]>
755+
trait ArcFromSlice<T> {
756+
fn from_slice(slice: &[T]) -> Self;
757+
}
758+
759+
impl<T: Clone> ArcFromSlice<T> for Arc<[T]> {
760+
#[inline]
761+
default fn from_slice(v: &[T]) -> Self {
762+
unsafe {
763+
Self::from_iter_exact(v.iter().cloned(), v.len())
716764
}
717765
}
718766
}
@@ -1792,9 +1840,88 @@ impl<T> From<Vec<T>> for Arc<[T]> {
17921840
}
17931841

17941842
#[stable(feature = "shared_from_iter", since = "1.37.0")]
1795-
impl<T> core::iter::FromIterator<T> for Arc<[T]> {
1796-
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
1797-
iter.into_iter().collect::<Vec<T>>().into()
1843+
impl<T> iter::FromIterator<T> for Arc<[T]> {
1844+
/// Takes each element in the `Iterator` and collects it into an `Arc<[T]>`.
1845+
///
1846+
/// # Performance characteristics
1847+
///
1848+
/// ## The general case
1849+
///
1850+
/// In the general case, collecting into `Arc<[T]>` is done by first
1851+
/// collecting into a `Vec<T>`. That is, when writing the following:
1852+
///
1853+
/// ```rust
1854+
/// # use std::sync::Arc;
1855+
/// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0).collect();
1856+
/// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]);
1857+
/// ```
1858+
///
1859+
/// this behaves as if we wrote:
1860+
///
1861+
/// ```rust
1862+
/// # use std::sync::Arc;
1863+
/// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0)
1864+
/// .collect::<Vec<_>>() // The first set of allocations happens here.
1865+
/// .into(); // A second allocation for `Arc<[T]>` happens here.
1866+
/// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]);
1867+
/// ```
1868+
///
1869+
/// This will allocate as many times as needed for constructing the `Vec<T>`
1870+
/// and then it will allocate once for turning the `Vec<T>` into the `Arc<[T]>`.
1871+
///
1872+
/// ## Iterators of known length
1873+
///
1874+
/// When your `Iterator` implements `TrustedLen` and is of an exact size,
1875+
/// a single allocation will be made for the `Arc<[T]>`. For example:
1876+
///
1877+
/// ```rust
1878+
/// # use std::sync::Arc;
1879+
/// let evens: Arc<[u8]> = (0..10).collect(); // Just a single allocation happens here.
1880+
/// # assert_eq!(&*evens, &*(0..10).collect::<Vec<_>>());
1881+
/// ```
1882+
fn from_iter<I: iter::IntoIterator<Item = T>>(iter: I) -> Self {
1883+
ArcFromIter::from_iter(iter.into_iter())
1884+
}
1885+
}
1886+
1887+
/// Specialization trait used for collecting into `Arc<[T]>`.
1888+
trait ArcFromIter<T, I> {
1889+
fn from_iter(iter: I) -> Self;
1890+
}
1891+
1892+
impl<T, I: Iterator<Item = T>> ArcFromIter<T, I> for Arc<[T]> {
1893+
default fn from_iter(iter: I) -> Self {
1894+
iter.collect::<Vec<T>>().into()
1895+
}
1896+
}
1897+
1898+
impl<T, I: iter::TrustedLen<Item = T>> ArcFromIter<T, I> for Arc<[T]> {
1899+
default fn from_iter(iter: I) -> Self {
1900+
// This is the case for a `TrustedLen` iterator.
1901+
let (low, high) = iter.size_hint();
1902+
if let Some(high) = high {
1903+
debug_assert_eq!(
1904+
low, high,
1905+
"TrustedLen iterator's size hint is not exact: {:?}",
1906+
(low, high)
1907+
);
1908+
1909+
unsafe {
1910+
// SAFETY: We need to ensure that the iterator has an exact length and we have.
1911+
Arc::from_iter_exact(iter, low)
1912+
}
1913+
} else {
1914+
// Fall back to normal implementation.
1915+
iter.collect::<Vec<T>>().into()
1916+
}
1917+
}
1918+
}
1919+
1920+
impl<'a, T: 'a + Clone> ArcFromIter<&'a T, slice::Iter<'a, T>> for Arc<[T]> {
1921+
fn from_iter(iter: slice::Iter<'a, T>) -> Self {
1922+
// Delegate to `impl<T: Clone> From<&[T]> for Arc<[T]>`
1923+
// which will use `ptr::copy_nonoverlapping`.
1924+
iter.as_slice().into()
17981925
}
17991926
}
18001927

0 commit comments

Comments
 (0)