Skip to content

Commit b43e8e2

Browse files
committed
BufWriter: avoid using expensive Vec methods
We use a Vec as our internal, constant-sized buffer, but the overhead of using methods like `extend_from_slice` can be enormous, likely because they don't get inlined, because `Vec` has to repeat bounds checks that we've already done, and because it makes considerations for things like reallocating, even though they should never happen.
1 parent 1f32d40 commit b43e8e2

File tree

1 file changed

+75
-12
lines changed

1 file changed

+75
-12
lines changed

library/std/src/io/buffered/bufwriter.rs

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::io::{
44
self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
55
};
66
use crate::mem;
7+
use crate::ptr;
78

89
/// Wraps a writer and buffers its output.
910
///
@@ -68,6 +69,10 @@ use crate::mem;
6869
#[stable(feature = "rust1", since = "1.0.0")]
6970
pub struct BufWriter<W: Write> {
7071
inner: Option<W>,
72+
// The buffer. Avoid using this like a normal `Vec` in common code paths.
73+
// That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
74+
// methods that require bounds checking or the like. This makes an enormous
75+
// difference to performance (we may want to stop using a `Vec` entirely).
7176
buf: Vec<u8>,
7277
// #30888: If the inner writer panics in a call to write, we don't want to
7378
// write the buffered data a second time in BufWriter's destructor. This
@@ -150,7 +155,11 @@ impl<W: Write> BufWriter<W> {
150155
impl Drop for BufGuard<'_> {
151156
fn drop(&mut self) {
152157
if self.written > 0 {
153-
self.buffer.drain(..self.written);
158+
if self.done() {
159+
self.buffer.clear();
160+
} else {
161+
self.buffer.drain(..self.written);
162+
}
154163
}
155164
}
156165
}
@@ -183,7 +192,12 @@ impl<W: Write> BufWriter<W> {
183192
pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize {
184193
let available = self.buf.capacity() - self.buf.len();
185194
let amt_to_buffer = available.min(buf.len());
186-
self.buf.extend_from_slice(&buf[..amt_to_buffer]);
195+
196+
// SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction.
197+
unsafe {
198+
self.write_to_buffer_unchecked(&buf[..amt_to_buffer]);
199+
}
200+
187201
amt_to_buffer
188202
}
189203

@@ -348,7 +362,13 @@ impl<W: Write> BufWriter<W> {
348362
self.panicked = false;
349363
r
350364
} else {
351-
self.buf.extend_from_slice(buf);
365+
// SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and
366+
// we entered this else block because `buf.len() < self.buf.capacity()`.
367+
// Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`.
368+
unsafe {
369+
self.write_to_buffer_unchecked(buf);
370+
}
371+
352372
Ok(buf.len())
353373
}
354374
}
@@ -373,10 +393,29 @@ impl<W: Write> BufWriter<W> {
373393
self.panicked = false;
374394
r
375395
} else {
376-
self.buf.extend_from_slice(buf);
396+
// SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and
397+
// we entered this else block because `buf.len() < self.buf.capacity()`.
398+
// Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`.
399+
unsafe {
400+
self.write_to_buffer_unchecked(buf);
401+
}
402+
377403
Ok(())
378404
}
379405
}
406+
407+
// SAFETY: Requires `self.buf.len() + buf.len() <= self.buf.capacity()`,
408+
// i.e., that input buffer length is less than or equal to spare capacity.
409+
#[inline(always)]
410+
unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) {
411+
debug_assert!(self.buf.len() + buf.len() <= self.buf.capacity());
412+
let old_len = self.buf.len();
413+
let buf_len = buf.len();
414+
let src = buf.as_ptr();
415+
let dst = self.buf.as_mut_ptr().add(old_len);
416+
ptr::copy_nonoverlapping(src, dst, buf_len);
417+
self.buf.set_len(old_len + buf_len);
418+
}
380419
}
381420

382421
#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
@@ -456,7 +495,11 @@ impl<W: Write> Write for BufWriter<W> {
456495
// prevents pathological cases for other clients which *always* make writes of this size.
457496
// See #72919 and #79930 for more info and a breadcrumb trail.
458497
if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() {
459-
self.buf.extend_from_slice(buf);
498+
// SAFETY: safe by above conditional.
499+
unsafe {
500+
self.write_to_buffer_unchecked(buf);
501+
}
502+
460503
Ok(buf.len())
461504
} else {
462505
self.flush_and_write(buf)
@@ -471,7 +514,11 @@ impl<W: Write> Write for BufWriter<W> {
471514
// prevents pathological cases for other clients which *always* make writes of this size.
472515
// See #72919 and #79930 for more info and a breadcrumb trail.
473516
if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() {
474-
self.buf.extend_from_slice(buf);
517+
// SAFETY: safe by above conditional.
518+
unsafe {
519+
self.write_to_buffer_unchecked(buf);
520+
}
521+
475522
Ok(())
476523
} else {
477524
self.flush_and_write_all(buf)
@@ -492,7 +539,13 @@ impl<W: Write> Write for BufWriter<W> {
492539
self.panicked = false;
493540
r
494541
} else {
495-
bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
542+
// SAFETY: We checked whether or not the spare capacity was large enough above. If
543+
// it was, then we're safe already. If it wasn't, we flushed, making sufficient
544+
// room for any input <= the buffer size, which includes this input.
545+
unsafe {
546+
bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b));
547+
};
548+
496549
Ok(total_len)
497550
}
498551
} else {
@@ -511,19 +564,29 @@ impl<W: Write> Write for BufWriter<W> {
511564
self.panicked = false;
512565
return r;
513566
} else {
514-
self.buf.extend_from_slice(buf);
567+
// SAFETY: We checked whether or not the spare capacity was large enough above.
568+
// If it was, then we're safe already. If it wasn't, we flushed, making
569+
// sufficient room for any input <= the buffer size, which includes this input.
570+
unsafe {
571+
self.write_to_buffer_unchecked(buf);
572+
}
573+
515574
buf.len()
516575
}
517576
} else {
518577
return Ok(0);
519578
};
520579
debug_assert!(total_written != 0);
521580
for buf in iter {
522-
if self.buf.len() + buf.len() > self.buf.capacity() {
523-
break;
524-
} else {
525-
self.buf.extend_from_slice(buf);
581+
if self.buf.len() + buf.len() <= self.buf.capacity() {
582+
// SAFETY: safe by above conditional.
583+
unsafe {
584+
self.write_to_buffer_unchecked(buf);
585+
}
586+
526587
total_written += buf.len();
588+
} else {
589+
break;
527590
}
528591
}
529592
Ok(total_written)

0 commit comments

Comments
 (0)