]> git.lizzy.rs Git - rust.git/commitdiff
std: Optimize Vec::from_iter
authorAlex Crichton <alex@alexcrichton.com>
Wed, 11 Feb 2015 22:18:32 +0000 (14:18 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Thu, 12 Feb 2015 18:25:34 +0000 (10:25 -0800)
This PR is an optimization of the `FromIterator` implementation of `Vec`

Benchmark: https://gist.github.com/alexcrichton/03d666159a28a80e7c70

Before:
    test macro_repeat1     ... bench:        57 ns/iter (+/- 1)
    test macro_repeat2     ... bench:        56 ns/iter (+/- 1)
    test map_clone1        ... bench:       828 ns/iter (+/- 13)
    test map_clone2        ... bench:       828 ns/iter (+/- 8)
    test repeat1           ... bench:      1104 ns/iter (+/- 10)
    test repeat2           ... bench:      1106 ns/iter (+/- 11)

After:
    test macro_repeat1     ... bench:        75 ns/iter (+/- 21)
    test macro_repeat2     ... bench:        59 ns/iter (+/- 31)
    test map_clone1        ... bench:        34 ns/iter (+/- 22)
    test map_clone2        ... bench:        52 ns/iter (+/- 21)
    test repeat1           ... bench:        34 ns/iter (+/- 11)
    test repeat2           ... bench:        33 ns/iter (+/- 12)

The idea behind this optimization is to avoid all bounds checks for space
already allocated into the vector. This may involve running the iterator twice,
but the first run of the iterator should be optimizable to a memcpy or memset if
possible.

The same treatment can in theory be applied to `Vec::extend` but the benchmarks
for that currently get *worse* if the change is applied. This appears to be some
LLVM optimizations going awry but it's seems prudent to land at least the
`collect` portion beforehand.

src/libcollections/vec.rs

index 1cd2a89ad604e970bda2f690a1d4b0a7a40abb12..7c7d783d129fb972a80d54da60c7028901e1e1e5 100644 (file)
@@ -1380,7 +1380,17 @@ impl<T> FromIterator<T> for Vec<T> {
     fn from_iter<I:Iterator<Item=T>>(iterator: I) -> Vec<T> {
         let (lower, _) = iterator.size_hint();
         let mut vector = Vec::with_capacity(lower);
-        for element in iterator {
+
+        let mut i = iterator.fuse();
+        for element in i.by_ref().take(vector.capacity()) {
+            let len = vector.len();
+            unsafe {
+                ptr::write(vector.get_unchecked_mut(len), element);
+                vector.set_len(len + 1);
+            }
+        }
+
+        for element in i {
             vector.push(element)
         }
         vector