From 6ac0ba3c3a1fc20e17923724e1e7635131eb19d6 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 7 Jul 2015 13:50:23 +0200 Subject: [PATCH 1/2] Improve Vec::resize so that it can be used in Read::read_to_end We needed a more efficient way to zerofill the vector in read_to_end. This to reduce the memory intialization overhead to a minimum. Use the implementation of `std::vec::from_elem` (used for the vec![] macro) for Vec::resize as well. For simple element types like u8, this compiles to memset, so it makes Vec::resize much more efficient. --- src/libcollections/vec.rs | 49 ++++++++++++++++++++++----------------- src/libstd/io/mod.rs | 12 ++++++++-- src/libstd/lib.rs | 1 + 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index c032471b6b64b..3848263c53097 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -67,7 +67,7 @@ use core::cmp::Ordering; use core::fmt; use core::hash::{self, Hash}; use core::intrinsics::{arith_offset, assume}; -use core::iter::{repeat, FromIterator}; +use core::iter::FromIterator; use core::marker::PhantomData; use core::mem; use core::ops::{Index, IndexMut, Deref}; @@ -1106,12 +1106,35 @@ impl Vec { let len = self.len(); if new_len > len { - self.extend(repeat(value).take(new_len - len)); + self.extend_with_element(new_len - len, value); } else { self.truncate(new_len); } } + /// Extend the vector by `n` additional clones of `value`. + fn extend_with_element(&mut self, n: usize, value: T) { + self.reserve(n); + + unsafe { + let len = self.len(); + let mut ptr = self.as_mut_ptr().offset(len as isize); + // Write all elements except the last one + for i in 1..n { + ptr::write(ptr, value.clone()); + ptr = ptr.offset(1); + // Increment the length in every step in case clone() panics + self.set_len(len + i); + } + + if n > 0 { + // We can write the last element directly without cloning needlessly + ptr::write(ptr, value); + self.set_len(len + n); + } + } + } + /// Appends all elements in a slice to the `Vec`. /// /// Iterates over the slice `other`, clones each element, and then appends @@ -1294,25 +1317,9 @@ unsafe fn dealloc(ptr: *mut T, len: usize) { #[doc(hidden)] #[stable(feature = "rust1", since = "1.0.0")] pub fn from_elem(elem: T, n: usize) -> Vec { - unsafe { - let mut v = Vec::with_capacity(n); - let mut ptr = v.as_mut_ptr(); - - // Write all elements except the last one - for i in 1..n { - ptr::write(ptr, Clone::clone(&elem)); - ptr = ptr.offset(1); - v.set_len(i); // Increment the length in every step in case Clone::clone() panics - } - - if n > 0 { - // We can write the last element directly without cloning needlessly - ptr::write(ptr, elem); - v.set_len(n); - } - - v - } + let mut v = Vec::with_capacity(n); + v.extend_with_element(n, elem); + v } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 9021f32fad003..50c44299dc709 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -16,7 +16,7 @@ use cmp; use rustc_unicode::str as core_str; use error as std_error; use fmt; -use iter::{self, Iterator, Extend}; +use iter::{Iterator}; use marker::Sized; use ops::{Drop, FnOnce}; use option::Option::{self, Some, None}; @@ -106,7 +106,7 @@ fn read_to_end(r: &mut R, buf: &mut Vec) -> Result if new_write_size < DEFAULT_BUF_SIZE { new_write_size *= 2; } - buf.extend(iter::repeat(0).take(new_write_size)); + buf.resize(len + new_write_size, 0); } match r.read(&mut buf[len..]) { @@ -984,6 +984,14 @@ mod tests { let mut v = Vec::new(); assert_eq!(c.read_to_end(&mut v).unwrap(), 1); assert_eq!(v, b"1"); + + let cap = 1024 * 1024; + let data = (0..cap).map(|i| (i / 3) as u8).collect::>(); + let mut v = Vec::new(); + let (a, b) = data.split_at(data.len() / 2); + assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len()); + assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len()); + assert_eq!(v, data); } #[test] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 73e45619774d7..caf3f497e10b4 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -146,6 +146,7 @@ #![feature(unique)] #![feature(unsafe_no_drop_flag, filling_drop)] #![feature(vec_push_all)] +#![feature(vec_resize)] #![feature(wrapping)] #![feature(zero_one)] #![cfg_attr(windows, feature(str_utf16))] From a5cc17adaadf9bc4da3b6b375879fda55ed823c9 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 8 Jul 2015 03:20:00 +0200 Subject: [PATCH 2/2] io: Simplify BufReader::with_capacity Use the vec![] macro directly to create a sized, zeroed vector. This should result in a big speedup when creating BufReader, because vec![0; cap] compiles to a memset call, while the previous extend code currently did not. --- src/libstd/io/buffered.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 1d0152e275117..28033b4c1a9db 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -19,7 +19,6 @@ use error; use fmt; use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom}; use ptr; -use iter; /// Wraps a `Read` and buffers input from it /// @@ -63,11 +62,9 @@ impl BufReader { /// Creates a new `BufReader` with the specified buffer capacity #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(cap: usize, inner: R) -> BufReader { - let mut buf = Vec::with_capacity(cap); - buf.extend(iter::repeat(0).take(cap)); BufReader { inner: inner, - buf: buf, + buf: vec![0; cap], pos: 0, cap: 0, }