17

A useful convenience introduced in .NET 4 is Stream.CopyTo(Stream[, Int32]) which reads the content from the current stream and writes it to another stream.

This obviates the need for slightly tedious code such as this:

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    while (true)
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
}

Since I don't have .NET 4 installed on this machine, I was wondering if someone who has .NET 4 installed could open up Reflector and show us how the Framework Class Library team implemented this method for .NET 4.

Compare and contrast their implementation with code snippet above. In particular, I'm interested to know what default buffer size was chosen.

Community
  • 1
  • 1
Daniel Fortunov
  • 43,309
  • 26
  • 81
  • 106

1 Answers1

16

In .NET 4.5.1, it uses a fixed buffer size of 81920 bytes. (Earlier versions of .NET used a fixed buffer size of 4096 bytes, and it will no doubt continue to change over time.) There is also an overload where you can pass your own buffer size.

The implementation is very similar to yours, modulo some shuffling around and some error checking. Reflector renders the heart of it as follows:

private void InternalCopyTo(Stream destination, int bufferSize)
{
  int num;
  byte[] buffer = new byte[bufferSize];
  while ((num = this.Read(buffer, 0, buffer.Length)) != 0)
  {
    destination.Write(buffer, 0, num);
  }
}

(You can now see the actual source at http://referencesource.microsoft.com/#mscorlib/system/io/stream.cs#98ac7cf3acb04bb1.)

The error checking is basically around whether input.CanRead and output.CanWrite are both true, or either is disposed. So in answer to Benny's question this should be perfectly happy copying from a NetworkStream (or to a writable NetworkStream).

itowlson
  • 73,686
  • 17
  • 161
  • 157
  • I guess the idea is to just use exactly 1 page for the buffer? – Yuliy Dec 19 '09 at 18:40
  • 4
    It's a "happy number" for buffer sizes. It is highly unlikely to fall exactly on a page boundary. – Hans Passant Dec 19 '09 at 19:20
  • 1
    It's also small enough not to be allocated on the large object heap. – skolima Oct 24 '13 at 22:38
  • 2
    @itowlson It is important to note that the default buffer size is actually 81,920 bytes, at least as of .NET 4.5.1. That is 20 times larger than the documentation's claim of 4096 bytes. You can confirm this via reflection on CopyTo(Stream). You can also see it in the [reference source](http://referencesource.microsoft.com/#mscorlib/system/io/stream.cs#2a0f078c2e0c0aa8#references). – Dan Mar 24 '14 at 19:27
  • This seems a bit inefficient, as in a lot of cases you can read and write in parallel (for example read from network and write to disk). An additional optional parameter to enable concurrent read/write would have been handy. – youen Jun 08 '17 at 08:42
  • I like how the buffer size is 81920 bytes. It's like somone doubled the old default to 8192 bytes, then figured 'I wonder if larger sizes give even better performance', then just added another 0, even though that means it's no longer a power of two. Being a power of two probably doesn't matter, but it still looks a little funny. – avl_sweden Sep 22 '21 at 11:25