Change:
I tried this code: I expected to see this happen: Instead, this happened: The problem doesn't occur with optimization enabled. It most recently worked on: Also fails on: modify labels: +regression-from-stable-to-stable -regression-untriaged <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Thanks for reporting this! This is an issue with a debug build so it's unlikely to be prioritized high but I'm interested in helping out because this is very goofy. The slow code here is the Iterator impl on slices. I think in the last stable release we switched the implementation of BufReader to eventually use ReadBuf, and the slow code is ReadBuf initializing the remaining uninitialized capacity. It does this by iterating over a slice, and that iteration is the slow bit. What I don't get here is why we need to initialize at all. That doesn't seem right.
WG-prioritization assigning highest priority since this looks a pretty visible runtime perf. regression (). If the assessment should be revised, please feel free to comment. label -I-prioritize +P-critical i-slow
I've tried bisecting with and feeding this command: . Regression seems to be in and confirm <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.0</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with: </details
:joy: I was so sure this wasn't my fault. More fool me for not bisecting it myself.
(This probably shows an opportunity to add a benchmark to the newly-established "runtime benchmarks" (rather than the compilation-time benchmarks that has classically focused on...)
After sleeping on this, I realized that there's no real reason to expect the worlds-worst-memset code which is the source of the slowdown to be totally optimized out with . And of course it isn't. This is also a substantial regression with optimizations. Here's a version of the original program that's touched up a bit: On 1.63 I see runtime ~0.43 s, and on 1.64 ~4.8 s. So we have a slowdown of around 10x, though I suspect that number could be ratcheted up higher.
claim
Awesome, thanks for the really quick fix!

Discussion:
} } <ins> // This is only used by a test which asserts that the initialization-tracking is correct. #[cfg(test)] impl<R> BufReader<R> { pub fn initialized(&self) -> usize { self.buf.initialized() } } </ins> impl<R: Seek> BufReader<R> { /// Seeks relative to the current position. If the new position lies within the buffer, /// the buffer will not be flushed, allowing for more efficient seeks.

// Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are // initialized with bytes from a read. filled: usize, <ins> // This is the max number of bytes returned across all `fill_buf` calls. We track this so that we // can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its // defensive initialization as possible. Note that while this often the same as `filled`, it // doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and // omitting this is a huge perf regression for `Read` impls that do not. initialized: usize, </ins> } impl Buffer { #[inline] pub fn with_capacity(capacity: usize) -> Self { let buf = Box::new_uninit_slice(capacity); <del> Self { buf, pos: 0, filled: 0 } </del> <ins> Self { buf, pos: 0, filled: 0, initialized: 0 } </ins> } #[inline]

self.pos } <ins> // This is only used by a test which asserts that the initialization-tracking is correct. #[cfg(test)] pub fn initialized(&self) -> usize { self.initialized } </ins> #[inline] pub fn discard_buffer(&mut self) { self.pos = 0;

let mut buf = BorrowedBuf::from(&mut *self.buf); // SAFETY: `self.filled` bytes will always have been initialized. unsafe { <del> buf.set_init(self.filled); </del> <ins> buf.set_init(self.initialized); </ins> } reader.read_buf(buf.unfilled())?; <del> self.filled = buf.len(); </del> self.pos = 0; <ins> self.filled = buf.len(); self.initialized = buf.init_len(); </ins> } Ok(self.buffer()) }

writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap(); assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!n".to_string())]); } <ins> #[test] fn bufreader_full_initialize() { struct OneByteReader; impl Read for OneByteReader { fn read(&mut self, buf: &mut [u8]) -> crate::io::Result<usize> { if buf.len() > 0 { buf[0] = 0; Ok(1) } else { Ok(0) } } } let mut reader = BufReader::new(OneByteReader); // Nothing is initialized yet. assert_eq!(reader.initialized(), 0); let buf = reader.fill_buf().unwrap(); // We read one byte... assert_eq!(buf.len(), 1); // But we initialized the whole buffer! assert_eq!(reader.initialized(), reader.capacity()); } </ins>