From d5459022347326c281537d0e1d8e604b9423f79a Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 4 Aug 2018 21:14:53 -0700 Subject: [PATCH 1/2] Try to guess a smarter initial capacity in Vec::from_iter --- src/liballoc/vec.rs | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index cc913dfbb4b01..00facc4758ea0 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1857,19 +1857,40 @@ impl SpecExtend for Vec // empty, but the loop in extend_desugared() is not going to see the // vector being full in the few subsequent loop iterations. // So we get better branch prediction. - let mut vector = match iterator.next() { - None => return Vec::new(), - Some(element) => { - let (lower, _) = iterator.size_hint(); - let mut vector = Vec::with_capacity(lower.saturating_add(1)); - unsafe { - ptr::write(vector.get_unchecked_mut(0), element); - vector.set_len(1); + let element = + if let Some(x) = iterator.next() { x } + else { return Vec::new() }; + let (lower, upper) = iterator.size_hint(); + let upper = upper.unwrap_or(lower); + let mut vector = + if lower >= upper / 2 { + // This branch covers three main cases: + // - There was no upper bound, so we just use the lower. + // - The hint turned out to be exact, so we use it. + // - Picking the upper won't waste more that the doubling + // strategy might anyway, so go directly there. + Vec::with_capacity(upper.saturating_add(1)) + } else { + // Try to start near the geometric mean of the range. That's + // never all that high -- even 0B..1GB will only allocate 32kB -- + // but it's much more useful than the lower bound, especially + // for iterator adapters like filter that have lower == 0. + let mut v = Vec::new(); + let mag_diff = lower.leading_zeros() - upper.leading_zeros(); + let guess = upper >> (mag_diff / 2); + match v.try_reserve(guess.saturating_add(1)) { + Ok(_) => v, + Err(_) => Vec::with_capacity(lower.saturating_add(1)), } - vector - } - }; + }; + unsafe { + ptr::write(vector.get_unchecked_mut(0), element); + vector.set_len(1); + } as SpecExtend>::spec_extend(&mut vector, iterator); + if vector.len() < vector.capacity() / 2 { + vector.shrink_to_fit(); + } vector } From aa080f40413693e9bb8dd55e645c47984952a517 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 7 Aug 2018 22:31:21 -0700 Subject: [PATCH 2/2] Be much less clever about things This one only helps lower>0, but by doing so means it's always strictly less than 100% overhead, same as the normal doubling algorithm. And thus doesn't need to do the try_reserve dance or a post-extend cleanup. --- src/liballoc/vec.rs | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 00facc4758ea0..22be12b9ef246 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1861,36 +1861,17 @@ impl SpecExtend for Vec if let Some(x) = iterator.next() { x } else { return Vec::new() }; let (lower, upper) = iterator.size_hint(); - let upper = upper.unwrap_or(lower); - let mut vector = - if lower >= upper / 2 { - // This branch covers three main cases: - // - There was no upper bound, so we just use the lower. - // - The hint turned out to be exact, so we use it. - // - Picking the upper won't waste more that the doubling - // strategy might anyway, so go directly there. - Vec::with_capacity(upper.saturating_add(1)) - } else { - // Try to start near the geometric mean of the range. That's - // never all that high -- even 0B..1GB will only allocate 32kB -- - // but it's much more useful than the lower bound, especially - // for iterator adapters like filter that have lower == 0. - let mut v = Vec::new(); - let mag_diff = lower.leading_zeros() - upper.leading_zeros(); - let guess = upper >> (mag_diff / 2); - match v.try_reserve(guess.saturating_add(1)) { - Ok(_) => v, - Err(_) => Vec::with_capacity(lower.saturating_add(1)), - } - }; + let guess = if let Some(upper) = upper { + lower.saturating_mul(2).min(upper) + } else { + lower + }; + let mut vector = Vec::with_capacity(guess.saturating_add(1)); unsafe { ptr::write(vector.get_unchecked_mut(0), element); vector.set_len(1); } as SpecExtend>::spec_extend(&mut vector, iterator); - if vector.len() < vector.capacity() / 2 { - vector.shrink_to_fit(); - } vector }