Skip to content

Commit 2c56ea3

Browse files
committed
Auto merge of #78768 - mzabaluev:optimize-buf-writer, r=cramertj
Use is_write_vectored to optimize the write_vectored implementation for BufWriter In case when the underlying writer does not have an efficient implementation `write_vectored`, the present implementation of `write_vectored` for `BufWriter` may still forward vectored writes directly to the writer depending on the total length of the data. This misses the advantage of buffering, as the actually written slice may be small. Provide an alternative code path for the non-vectored case, where the slices passed to `BufWriter` are coalesced in the buffer before being flushed to the underlying writer with plain `write` calls. The buffer is only bypassed if an individual slice's length is at least as large as the buffer. Remove a FIXME comment referring to #72919 as the issue has been closed with an explanation provided.
2 parents 1700ca0 + 674dd62 commit 2c56ea3

File tree

2 files changed

+53
-14
lines changed

2 files changed

+53
-14
lines changed

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

+46-13
Original file line numberDiff line numberDiff line change
@@ -328,24 +328,57 @@ impl<W: Write> Write for BufWriter<W> {
328328
}
329329

330330
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
331-
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
332-
if self.buf.len() + total_len > self.buf.capacity() {
333-
self.flush_buf()?;
334-
}
335-
// FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
336-
if total_len >= self.buf.capacity() {
337-
self.panicked = true;
338-
let r = self.get_mut().write_vectored(bufs);
339-
self.panicked = false;
340-
r
331+
if self.get_ref().is_write_vectored() {
332+
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
333+
if self.buf.len() + total_len > self.buf.capacity() {
334+
self.flush_buf()?;
335+
}
336+
if total_len >= self.buf.capacity() {
337+
self.panicked = true;
338+
let r = self.get_mut().write_vectored(bufs);
339+
self.panicked = false;
340+
r
341+
} else {
342+
bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
343+
Ok(total_len)
344+
}
341345
} else {
342-
bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
343-
Ok(total_len)
346+
let mut iter = bufs.iter();
347+
let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
348+
// This is the first non-empty slice to write, so if it does
349+
// not fit in the buffer, we still get to flush and proceed.
350+
if self.buf.len() + buf.len() > self.buf.capacity() {
351+
self.flush_buf()?;
352+
}
353+
if buf.len() >= self.buf.capacity() {
354+
// The slice is at least as large as the buffering capacity,
355+
// so it's better to write it directly, bypassing the buffer.
356+
self.panicked = true;
357+
let r = self.get_mut().write(buf);
358+
self.panicked = false;
359+
return r;
360+
} else {
361+
self.buf.extend_from_slice(buf);
362+
buf.len()
363+
}
364+
} else {
365+
return Ok(0);
366+
};
367+
debug_assert!(total_written != 0);
368+
for buf in iter {
369+
if self.buf.len() + buf.len() > self.buf.capacity() {
370+
break;
371+
} else {
372+
self.buf.extend_from_slice(buf);
373+
total_written += buf.len();
374+
}
375+
}
376+
Ok(total_written)
344377
}
345378
}
346379

347380
fn is_write_vectored(&self) -> bool {
348-
self.get_ref().is_write_vectored()
381+
true
349382
}
350383

351384
fn flush(&mut self) -> io::Result<()> {

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ impl<'a, W: Write> LineWriterShim<'a, W> {
2020
Self { buffer }
2121
}
2222

23+
/// Get a reference to the inner writer (that is, the writer
24+
/// wrapped by the BufWriter).
25+
fn inner(&self) -> &W {
26+
self.buffer.get_ref()
27+
}
28+
2329
/// Get a mutable reference to the inner writer (that is, the writer
2430
/// wrapped by the BufWriter). Be careful with this writer, as writes to
2531
/// it will bypass the buffer.
@@ -227,7 +233,7 @@ impl<'a, W: Write> Write for LineWriterShim<'a, W> {
227233
}
228234

229235
fn is_write_vectored(&self) -> bool {
230-
self.buffer.is_write_vectored()
236+
self.inner().is_write_vectored()
231237
}
232238

233239
/// Write some data into this BufReader with line buffering. This means

0 commit comments

Comments
 (0)