File.Copy vs. Manual FileStream.Write For Copying File File.Copy vs. Manual FileStream.Write For Copying File windows windows

File.Copy vs. Manual FileStream.Write For Copying File


File.Copy was build around CopyFile Win32 function and this function takes lot of attention from MS crew (remember this Vista-related threads about slow copy performance).

Several clues to improve performance of your method:

  1. Like many said earlier remove Flush method from your cycle. You do not need it at all.
  2. Increasing buffer may help, but only on file-to-file operations, for network shares, or ftp servers this will slow down instead. 60 * 1024 is ideal for network shares, at least before vista. for ftp 32k will be enough in most cases.
  3. Help os by providing your caching strategy (in your case sequential reading and writing), use FileStream constructor override with FileOptions parameter (SequentalScan).
  4. You can speed up copying by using asynchronous pattern (especially useful for network-to-file cases), but do not use threads for this, instead use overlapped io (BeginRead, EndRead, BeginWrite, EndWrite in .net), and do not forget set Asynchronous option in FileStream constructor (see FileOptions)

Example of asynchronous copy pattern:

int Readed = 0;IAsyncResult ReadResult;IAsyncResult WriteResult;ReadResult = sourceStream.BeginRead(ActiveBuffer, 0, ActiveBuffer.Length, null, null);do{    Readed = sourceStream.EndRead(ReadResult);    WriteResult = destStream.BeginWrite(ActiveBuffer, 0, Readed, null, null);    WriteBuffer = ActiveBuffer;    if (Readed > 0)    {      ReadResult = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);      BackBuffer = Interlocked.Exchange(ref ActiveBuffer, BackBuffer);    }    destStream.EndWrite(WriteResult);  }  while (Readed > 0);


Dusting off reflector we can see that File.Copy actually calls the Win32 API:

if (!Win32Native.CopyFile(fullPathInternal, dst, !overwrite))

Which resolves to

[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]internal static extern bool CopyFile(string src, string dst, bool failIfExists);

And here is the documentation for CopyFile


Three changes will dramatically improve performance:

  1. Increase your buffer size, try 1MB (well -just experiment)
  2. After you open your fileStream, call fileStream.SetLength(inStream.Length) to allocate the entire block on disk up front (only works if inStream is seekable)
  3. Remove fileStream.Flush() - it is redundant and probably has the single biggest impact on performance as it will block until the flush is complete. The stream will be flushed anyway on dispose.

This seemed about 3-4 times faster in the experiments I tried:

   public static void Copy(System.IO.Stream inStream, string outputFilePath)    {        int bufferSize = 1024 * 1024;        using (FileStream fileStream = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))        {            fileStream.SetLength(inStream.Length);            int bytesRead = -1;            byte[] bytes = new byte[bufferSize];            while ((bytesRead = inStream.Read(bytes, 0, bufferSize)) > 0)            {                fileStream.Write(bytes, 0, bytesRead);            }       }    }