Skip to content

Commit e90bbca

Browse files
committed
Pre-allocate in Read::read_to_end and read_to_string based on size_snapshot
In the case of reading a normal file, this creates a buffer of the desired size up front so it avoids over-allocation and re-allocation. It adds an fstat syscall, but in the case of reading a normal file, this eliminates the final 0-byte read syscall at the end. This also changes the default read size pattern from 32,64,128,256,... to 32,32,64,128,... so that default allocation sizes and file reads are aligned at power-of-two boundaries.
1 parent 2460adc commit e90bbca

File tree

1 file changed

+35
-17
lines changed

1 file changed

+35
-17
lines changed

src/libstd/io/mod.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -366,32 +366,50 @@ fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
366366
fn read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> {
367367
let start_len = buf.len();
368368
let mut g = Guard { len: buf.len(), buf: buf };
369-
let ret;
369+
let size_snapshot = r.size_snapshot();
370+
371+
// Determine the size to start reading with.
372+
let initial_resize_len = if let Some(size) = size_snapshot {
373+
// We know the (present) size. Don't use reserve_exact because when the
374+
// initial size of buf is zero, reserve should still give us exactly the
375+
// size we request, and when it's non-zero, we're concatenating things.
376+
g.buf.reserve(size);
377+
start_len + size
378+
} else {
379+
// We don't know the size. Start with a relatively small guess.
380+
g.buf.reserve(32);
381+
g.buf.capacity()
382+
};
383+
unsafe {
384+
g.buf.set_len(initial_resize_len);
385+
r.initializer().initialize(&mut g.buf[g.len..]);
386+
}
387+
370388
loop {
371-
if g.len == g.buf.len() {
372-
unsafe {
373-
g.buf.reserve(32);
374-
let capacity = g.buf.capacity();
375-
g.buf.set_len(capacity);
376-
r.initializer().initialize(&mut g.buf[g.len..]);
377-
}
389+
match r.read(&mut g.buf[g.len..]) {
390+
Ok(0) => break,
391+
Ok(n) => g.len += n,
392+
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
393+
Err(e) => return Err(e),
378394
}
379395

380-
match r.read(&mut g.buf[g.len..]) {
381-
Ok(0) => {
382-
ret = Ok(g.len - start_len);
396+
if g.len == g.buf.len() {
397+
if size_snapshot.is_some() {
398+
// We finished what the snapshot told us, so we're done.
399+
debug_assert_eq!(size_snapshot.unwrap(), g.len - start_len);
383400
break;
384401
}
385-
Ok(n) => g.len += n,
386-
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
387-
Err(e) => {
388-
ret = Err(e);
389-
break;
402+
// We've used up our available buffer space; allocate more.
403+
g.buf.reserve(32);
404+
let capacity = g.buf.capacity();
405+
unsafe {
406+
g.buf.set_len(capacity);
407+
r.initializer().initialize(&mut g.buf[g.len..]);
390408
}
391409
}
392410
}
393411

394-
ret
412+
Ok(g.len - start_len)
395413
}
396414

397415
/// The `Read` trait allows for reading bytes from a source.

0 commit comments

Comments
 (0)