Skip to content

Commit 42f7720

Browse files
committed
Specialize Bytes<R>::next when R is a BufReader.
This reduces the runtime for a simple program using `Bytes::next` to iterate through a file from 220ms to 70ms on my Linux box.
1 parent cc705b8 commit 42f7720

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

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

+19-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ mod buffer;
22

33
use crate::fmt;
44
use crate::io::{
5-
self, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
5+
self, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, SpecReadByte,
6+
DEFAULT_BUF_SIZE,
67
};
78
use buffer::Buffer;
89

@@ -259,6 +260,21 @@ impl<R: ?Sized + Seek> BufReader<R> {
259260
}
260261
}
261262

263+
impl<R> SpecReadByte for BufReader<R>
264+
where
265+
Self: Read,
266+
{
267+
#[inline]
268+
fn spec_read_byte(&mut self) -> Option<io::Result<u8>> {
269+
let mut byte = 0;
270+
if self.buf.consume_with(1, |claimed| byte = claimed[0]) {
271+
return Some(Ok(byte));
272+
}
273+
274+
self.slow_read_byte()
275+
}
276+
}
277+
262278
#[stable(feature = "rust1", since = "1.0.0")]
263279
impl<R: ?Sized + Read> Read for BufReader<R> {
264280
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -269,10 +285,8 @@ impl<R: ?Sized + Read> Read for BufReader<R> {
269285
self.discard_buffer();
270286
return self.inner.read(buf);
271287
}
272-
let nread = {
273-
let mut rem = self.fill_buf()?;
274-
rem.read(buf)?
275-
};
288+
let mut rem = self.fill_buf()?;
289+
let nread = rem.read(buf)?;
276290
self.consume(nread);
277291
Ok(nread)
278292
}

library/std/src/io/mod.rs

+34-7
Original file line numberDiff line numberDiff line change
@@ -2777,23 +2777,50 @@ pub struct Bytes<R> {
27772777
impl<R: Read> Iterator for Bytes<R> {
27782778
type Item = Result<u8>;
27792779

2780-
#[inline]
2780+
// Not `#[inline]`. This function gets inlined even without it, but having
2781+
// the inline annotation can result in worse code generation. See #116785.
27812782
fn next(&mut self) -> Option<Result<u8>> {
2783+
SpecReadByte::spec_read_byte(&mut self.inner)
2784+
}
2785+
2786+
#[inline]
2787+
fn size_hint(&self) -> (usize, Option<usize>) {
2788+
SizeHint::size_hint(&self.inner)
2789+
}
2790+
}
2791+
2792+
// Trait for specialization of `Bytes::next`.
2793+
trait SpecReadByte {
2794+
// Specializable method for reading a single byte.
2795+
fn spec_read_byte(&mut self) -> Option<Result<u8>>;
2796+
2797+
// Non-specializable method for reading a single byte. This is used by the
2798+
// default `spec_read_byte` and can also be called on a slow path of a
2799+
// specialized `spec_read_byte` method.
2800+
fn slow_read_byte(&mut self) -> Option<Result<u8>>;
2801+
}
2802+
2803+
impl<R> SpecReadByte for R
2804+
where
2805+
Self: Read,
2806+
{
2807+
#[inline]
2808+
default fn spec_read_byte(&mut self) -> Option<Result<u8>> {
2809+
self.slow_read_byte()
2810+
}
2811+
2812+
#[inline(never)]
2813+
fn slow_read_byte(&mut self) -> Option<Result<u8>> {
27822814
let mut byte = 0;
27832815
loop {
2784-
return match self.inner.read(slice::from_mut(&mut byte)) {
2816+
return match self.read(slice::from_mut(&mut byte)) {
27852817
Ok(0) => None,
27862818
Ok(..) => Some(Ok(byte)),
27872819
Err(ref e) if e.is_interrupted() => continue,
27882820
Err(e) => Some(Err(e)),
27892821
};
27902822
}
27912823
}
2792-
2793-
#[inline]
2794-
fn size_hint(&self) -> (usize, Option<usize>) {
2795-
SizeHint::size_hint(&self.inner)
2796-
}
27972824
}
27982825

27992826
trait SizeHint {

0 commit comments

Comments
 (0)