From ea9bd6cae39d97598d7187950a30d1987b661f5a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 23 Aug 2022 10:50:08 +1000 Subject: [PATCH 1/5] Optimize `ThinVec::drop` for the empty case. --- src/lib.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1cb03f2..cacb766 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -935,12 +935,6 @@ impl ThinVec { } } - unsafe fn deallocate(&mut self) { - if self.has_allocation() { - dealloc(self.ptr() as *mut u8, layout::(self.capacity())) - } - } - /// Resize the buffer and update its capacity, without changing the length. /// Unsafe because it can cause length to be greater than capacity. unsafe fn reallocate(&mut self, new_cap: usize) { @@ -1078,10 +1072,25 @@ impl ThinVec { } impl Drop for ThinVec { + #[inline] fn drop(&mut self) { - unsafe { - ptr::drop_in_place(&mut self[..]); - self.deallocate(); + #[cold] + #[inline(never)] + fn drop_non_singleton(this: &mut ThinVec) { + unsafe { + ptr::drop_in_place(&mut this[..]); + + #[cfg(feature = "gecko-ffi")] + if this.ptr.as_ref().uses_stack_allocated_buffer() { + return; + } + + dealloc(this.ptr() as *mut u8, layout::(this.capacity())) + } + } + + if !self.is_singleton() { + drop_non_singleton(self); } } } From c5046b665628dd05c15de5d48da4c8b10f2982d6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 23 Aug 2022 11:33:15 +1000 Subject: [PATCH 2/5] Optimize `ThinVec::clone` for the empty case. --- src/lib.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cacb766..cbad649 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1327,20 +1327,33 @@ impl Clone for ThinVec where T: Clone, { + #[inline] fn clone(&self) -> ThinVec { - let len = self.len(); - let mut new_vec = ThinVec::::with_capacity(len); - let mut data_raw = new_vec.data_raw(); - for x in self.iter() { + #[cold] + #[inline(never)] + fn clone_non_singleton(this: &ThinVec) -> ThinVec { + let len = this.len(); + let mut new_vec = ThinVec::::with_capacity(len); + let mut data_raw = new_vec.data_raw(); + for x in this.iter() { + unsafe { + ptr::write(data_raw, x.clone()); + data_raw = data_raw.add(1); + } + } unsafe { - ptr::write(data_raw, x.clone()); - data_raw = data_raw.add(1); + // `this` is not the singleton, but `new_vec` will be if + // `this` is empty. + new_vec.set_len(len); // could be the singleton } + new_vec } - unsafe { - new_vec.set_len(len); // could be the singleton + + if self.is_singleton() { + ThinVec::new() + } else { + clone_non_singleton(self) } - new_vec } } From f7c5fb4d1b7029649120ad4f3c44aa6052035c5b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 23 Aug 2022 11:52:11 +1000 Subject: [PATCH 3/5] Optimize `IntoIter::drop` for the empty case. --- src/lib.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cbad649..225ddea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1416,11 +1416,20 @@ impl DoubleEndedIterator for IntoIter { } impl Drop for IntoIter { + #[inline] fn drop(&mut self) { - unsafe { - let mut vec = mem::replace(&mut self.vec, ThinVec::new()); - ptr::drop_in_place(&mut vec[self.start..]); - vec.set_len(0) // could be the singleton + #[cold] + #[inline(never)] + fn drop_non_singleton(this: &mut IntoIter) { + unsafe { + let mut vec = mem::replace(&mut this.vec, ThinVec::new()); + ptr::drop_in_place(&mut vec[this.start..]); + vec.set_len_non_singleton(0) + } + } + + if !self.vec.is_singleton() { + drop_non_singleton(self); } } } From d8b7442a8ef7044783591797632f5781a4e989ca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 23 Aug 2022 13:33:26 +1000 Subject: [PATCH 4/5] Optimize `IntoIter::extend` for the likely-empty case. --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 225ddea..919b154 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1134,7 +1134,10 @@ impl Extend for ThinVec { I: IntoIterator, { let iter = iter.into_iter(); - self.reserve(iter.size_hint().0); + let hint = iter.size_hint().0; + if hint > 0 { + self.reserve(hint); + } for x in iter { self.push(x); } From fdb6b2ea0c3cb4e1aa4362aecea2da8c9acc56b5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 24 Aug 2022 07:11:17 +1000 Subject: [PATCH 5/5] Add more tests for `clone`. --- src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 919b154..6078515 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1829,6 +1829,27 @@ mod tests { assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } + + { + let v = ThinVec::::new(); + let v = v.clone(); + + assert_eq!(v.len(), 0); + assert_eq!(v.capacity(), 0); + assert_eq!(&v[..], &[]); + } + } + + #[test] + fn test_clone() { + let mut v = ThinVec::::new(); + assert!(v.is_singleton()); + v.push(0); + v.pop(); + assert!(!v.is_singleton()); + + let v2 = v.clone(); + assert!(v2.is_singleton()); } }