Code change:
} } <del> /// The general read-write-loop implementation of /// `io::copy` that is used when specializations are not available or not applicable. pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> </del> <ins> /// The userspace read-write-loop implementation of `io::copy` that is used when /// OS-specific specializations for copy offloading are not available or not applicable. pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64> </ins> where R: Read, W: Write, { <del> let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); </del> <ins> BufferedCopySpec::copy_to(reader, writer) } /// Specialization of the read-write loop that either uses a stack buffer /// or reuses the internal buffer of a BufWriter trait BufferedCopySpec: Write { fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64>; } impl<W: Write + ?Sized> BufferedCopySpec for W { default fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> { stack_buffer_copy(reader, writer) } } impl<I: Write> BufferedCopySpec for BufWriter<I> { fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> { if writer.capacity() < DEFAULT_BUF_SIZE { return stack_buffer_copy(reader, writer); } // FIXME: #42788 // //   - This creates a (mut) reference to a slice of //     _uninitialized_ integers, which is **undefined behavior** // //   - Only the standard library gets to soundly "ignore" this, //     based on its privileged knowledge of unstable rustc //     internals; unsafe { let spare_cap = writer.buffer_mut().spare_capacity_mut(); reader.initializer().initialize(MaybeUninit::slice_assume_init_mut(spare_cap)); } let mut len = 0; loop { let buf = writer.buffer_mut(); let spare_cap = buf.spare_capacity_mut(); if spare_cap.len() >= DEFAULT_BUF_SIZE { match reader.read(unsafe { MaybeUninit::slice_assume_init_mut(spare_cap) }) { Ok(0) => return Ok(len), // EOF reached Ok(bytes_read) => { assert!(bytes_read <= spare_cap.len()); // Safety: The initializer contract guarantees that either it or `read` // will have initialized these bytes. And we just checked that the number // of bytes is within the buffer capacity. unsafe { buf.set_len(buf.len() + bytes_read) }; len += bytes_read as u64; // Read again if the buffer still has enough capacity, as BufWriter itself would do // This will occur if the reader returns short reads continue; } Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e), } } writer.flush_buf()?; } } } fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( reader: &mut R, writer: &mut W, ) -> Result<u64> { let mut buf = MaybeUninit::<[u8; DEFAULT_BUF_SIZE]>::uninit(); </ins> // FIXME: #42788 // //   - This creates a (mut) reference to a slice of

Related discussion:
text: I was using std::io::copy from an SSD to memory using , which reported an eye-raising 30 MB/s. Wrapping it in a BufReader (either as the outer or middle layer) made no real difference. So I googled around a bit and found the recommended size was 64KB according to some random Microsoft article, so I decided to copy-paste the std::io::copy implementation and increase the buffer size. This got up to 600 MB/s at about 256KB which sounded a lot more it continued going upwards from there past the point of believability (unless the files were being pulled from RAM as a consequence of my repeated accesses). With a 10MB Vec, I got 228.36 GB/s . Unrealistic values aside, has anybody checked the buffer value to make sure there isn't some huge low-hanging fruit for std::io::copy speeds?
You mention windows. This may be platform-specific behavior. On unix systems I would fadvise on the input file for copying. Googling tells me that the windows equivalent is . Have you tried that?
After a coarse overview, my intuition is that there is some funny business going on with the length determined by and the subsequent calls on the , that could explain the unrealistic results.
It’s possible. The actual times did increase dramatically from the std::io::copy default though - my suspicion is that there is probably some caching to RAM going on or something as cold runs were generally slower than hot runs. This was for some work code that I’m no longer actively developing. I’ll leave this email marked as unread for awhile in case I have some spare time to write a benchmark. I don’t think the other flag that was mentioned is accessible with the default open file api, but in any case it at least appears there’s some low hanging fruit wrt to std::io::copy perf
I tried benchmarking on Windoze before and after changing to (instead of the original ) and the results were slightly worse (up to ~3%) with the modified setup, both on HDD and SSD. However, I'm not sure if my benchmarks were good enough - I was having a hard time tweaking them so that I could see any difference between results for different file sizes.