Skip to content

Commit 4e27ed3

Browse files
saethlinKodrAus
authored andcommitted
Add benchmark and fast path for BufReader::read_exact
1 parent 8a65184 commit 4e27ed3

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,20 @@ impl<R: Read> Read for BufReader<R> {
271271
Ok(nread)
272272
}
273273

274+
// Small read_exacts from a BufReader are extremely common when used with a deserializer.
275+
// The default implementation calls read in a loop, which results in surprisingly poor code
276+
// generation for the common path where the buffer has enough bytes to fill the passed-in
277+
// buffer.
278+
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
279+
if self.buffer().len() >= buf.len() {
280+
buf.copy_from_slice(&self.buffer()[..buf.len()]);
281+
self.consume(buf.len());
282+
return Ok(());
283+
}
284+
285+
crate::io::default_read_exact(self, buf)
286+
}
287+
274288
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
275289
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
276290
if self.pos == self.cap && total_len >= self.buf.len() {

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

+12
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,18 @@ fn bench_buffered_reader(b: &mut test::Bencher) {
443443
b.iter(|| BufReader::new(io::empty()));
444444
}
445445

446+
#[bench]
447+
fn bench_buffered_reader_small_reads(b: &mut test::Bencher) {
448+
let data = (0..u8::MAX).cycle().take(1024 * 4).collect::<Vec<_>>();
449+
b.iter(|| {
450+
let mut reader = BufReader::new(&data[..]);
451+
let mut buf = [0u8; 4];
452+
for _ in 0..1024 {
453+
reader.read_exact(&mut buf).unwrap();
454+
}
455+
});
456+
}
457+
446458
#[bench]
447459
fn bench_buffered_writer(b: &mut test::Bencher) {
448460
b.iter(|| BufWriter::new(io::sink()));

library/std/src/io/mod.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,25 @@ where
416416
write(buf)
417417
}
418418

419+
pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [u8]) -> Result<()> {
420+
while !buf.is_empty() {
421+
match this.read(buf) {
422+
Ok(0) => break,
423+
Ok(n) => {
424+
let tmp = buf;
425+
buf = &mut tmp[n..];
426+
}
427+
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
428+
Err(e) => return Err(e),
429+
}
430+
}
431+
if !buf.is_empty() {
432+
Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
433+
} else {
434+
Ok(())
435+
}
436+
}
437+
419438
/// The `Read` trait allows for reading bytes from a source.
420439
///
421440
/// Implementors of the `Read` trait are called 'readers'.
@@ -766,23 +785,8 @@ pub trait Read {
766785
/// }
767786
/// ```
768787
#[stable(feature = "read_exact", since = "1.6.0")]
769-
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
770-
while !buf.is_empty() {
771-
match self.read(buf) {
772-
Ok(0) => break,
773-
Ok(n) => {
774-
let tmp = buf;
775-
buf = &mut tmp[n..];
776-
}
777-
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
778-
Err(e) => return Err(e),
779-
}
780-
}
781-
if !buf.is_empty() {
782-
Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
783-
} else {
784-
Ok(())
785-
}
788+
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
789+
default_read_exact(self, buf)
786790
}
787791

788792
/// Creates a "by reference" adaptor for this instance of `Read`.

0 commit comments

Comments
 (0)