File.Copy vs. FileStream.Write Manual para copiar el archivo

Mi problema está relacionado con el rendimiento de copia de archivos. Tenemos un sistema de administración de medios que requiere mover muchos archivos en el sistema de archivos a diferentes ubicaciones, incluidos los recursos compartidos de Windows en la misma red, sitios FTP, AmazonS3, etc. Cuando todos estábamos en una red de Windows, podíamos salirnos usando System.IO.File.Copy (origen, destino) para copiar un archivo. Dado que muchas veces todo lo que tenemos es un flujo de entrada (como un MemoryStream), tratamos de abstraer la operación de copia para tomar un flujo de entrada y un flujo de salida, pero estamos viendo una disminución masiva de rendimiento. A continuación se muestra un código para copiar un archivo para usar como punto de discusión.

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

¿Alguien sabe por qué esto se realiza mucho más lento que File.Copy? ¿Hay algo que pueda hacer para mejorar el rendimiento? ¿Voy a tener que poner una lógica especial para ver si estoy copiando de una ubicación de Windows a otra, en cuyo caso solo usaría File.Copy y en los otros casos usaré las transmisiones?

Por favor, hágame saber lo que piensa y si necesita información adicional. He probado diferentes tamaños de buffer y parece que un tamaño de buffer de 64k es óptimo para nuestros archivos “pequeños” y 256k + es un mejor tamaño de buffer para nuestros archivos “grandes”, pero en cualquier caso funciona mucho peor que File.Copy ( ) ¡Gracias por adelantado!

File.Copy fue construido alrededor de la función CopyFile Win32 y esta función llama mucho la atención de la tripulación de MS (recuerda estos hilos relacionados con Vista sobre el rendimiento de copia lenta).

Varias pistas para mejorar el rendimiento de su método:

  1. Como muchos dijeron antes, quite el método de lavado de su ciclo. No lo necesitas en absoluto.
  2. Aumentar el búfer puede ayudar, pero solo en las operaciones de archivo a archivo, para recursos compartidos de red o servidores ftp esto se ralentizará. 60 * 1024 es ideal para recursos compartidos de red, al menos antes de vista. para ftp 32k será suficiente en la mayoría de los casos.
  3. Ayúdelo proporcionando su estrategia de almacenamiento en caché (en su caso, lectura y escritura secuencial), use la anulación del constructor FileStream con el parámetro FileOptions (SequentalScan).
  4. Puede acelerar la copia mediante el uso de un patrón asincrónico (especialmente útil para casos de red a archivo), pero no use hilos para esto, en su lugar use io superpuesto (BeginRead, EndRead, BeginWrite, EndWrite en .net) y no lo olvide establecer la opción asincrónica en el constructor FileStream (ver FileOptions )

Ejemplo de patrón de copia asíncrona:

 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);