streamfile handling files example copyto c# .net windows performance copy

handling - filestream copyto c#



.net File.Copy muy lento al copiar muchos archivos pequeños(no sobre la red) (5)

Estoy creando una sencilla herramienta de copia de seguridad de sincronización de carpetas para mí y me encontré con una barrera con File.Copy. Al hacer pruebas para copiar una carpeta de ~ 44,000 archivos pequeños (carpetas de correo de Windows) a otra unidad en mi sistema, descubrí que usar File.Copy era más de 3 veces más lento que usar una línea de comando y ejecutar xcopy para copiar los mismos archivos / carpetas. La versión de My C # toma más de 16 minutos para copiar los archivos, mientras que la copia toma solo 5 minutos. Intenté buscar ayuda sobre este tema, pero todo lo que encuentro es que las personas se quejan de la lenta copia de archivos grandes en una red. Esto no es un gran problema de archivos ni un problema de copia de red.

Encontré un artículo interesante sobre un mejor reemplazo de File.Copy , pero el código tal como se publicó tiene algunos errores que causan problemas con la pila y no estoy lo suficientemente informado como para solucionar los problemas en su código.

¿Hay alguna manera común o fácil de reemplazar File.Copy con algo más rápido?


Creo que al menos podrías paralizarlo para que hagas dos archivos al mismo tiempo. Mientras un hilo está escribiendo, otro ya puede estar leyendo el siguiente archivo. Si tiene una lista de archivos, puede hacerlo de esta manera. Usar muchos subprocesos no ayudará porque esto hará que la unidad se mueva mucho más en lugar de poder escribir secuencialmente.

var files = new List<string>(); // todo: fill the files list using directoryenumeration or so... var po = new ParallelOptions() {MaxDegreeOfParallelism = 2}; Parallel.ForEach(files, po, CopyAFile); // Routine to copy a single file private void CopyAFile(string file) { }


Hay dos algoritmos para una copia de archivo más rápida:

Si el origen y el destino son discos diferentes, entonces:

  • Un hilo que lee archivos continuamente y los almacena en un búfer.
  • Otro hilo que escribe archivos continuamente desde ese búfer.

Si el origen y el destino son el mismo disco, entonces:

  • Lea un fragmento fijo de bytes, digamos 8K a la vez, sin importar cuántos archivos sean.
  • Escriba ese fragmento fijo en el destino, ya sea en un archivo o en varios archivos.

De esta forma obtendrás un rendimiento significativo.

Una alternativa es invocar xcopy desde su código .net. ¿Por qué molestarse en hacerlo usando File.Copy? Puede capturar la salida de xcopy usando Process.StandardOutput y mostrar en la pantalla para mostrar al usuario lo que está sucediendo.


No tengo buena experiencia en este nivel. ¿Por qué no intenta ejecutar un archivo por lotes que contiene su comando xcopy? Verifique esta publicación: Ejecutar archivo por lotes en C #


Una cosa a considerar es si su copia tiene una interfaz de usuario que se actualiza durante la copia. Si es así, asegúrese de que su copia se ejecute en un hilo separado, o su IU se congelará durante la copia, y la copia se ralentizará haciendo llamadas de bloqueo para actualizar la UI.

xcopy un programa similar y, según mi experiencia, mi código se ejecutó más rápido que una copia de explorador de Windows (no estoy seguro acerca de xcopy desde el símbolo del sistema).

Además, si tiene una IU, no actualice en cada archivo; en su lugar, actualice cada X megabytes o cada archivo Y (lo que ocurra primero), esto reduce la cantidad de actualizaciones a algo que la IU realmente puede manejar. Usé cada .5MB o 10 archivos; Es posible que no sean óptimos, pero aumentó notablemente la velocidad de copia y la capacidad de respuesta de la IU.

Otra forma de acelerar las cosas es utilizar las funciones Enumerate en lugar de Get (por ejemplo, EnumerateFiles lugar de GetFiles ). Estas funciones comienzan a devolver resultados tan pronto como sea posible en lugar de esperar a devolver todo cuando la lista se termine de construir. Devuelven un Enumerable, por lo que puede llamar a foreach sobre el resultado: foreach (archivo de cadena en System.IO.Directory.EnumerateDirectories(path)) . Para mi programa, esto también hizo una notable diferencia en la velocidad, y sería aún más útil en casos como el suyo, donde se trata de directorios que contienen muchos archivos.


Una de las cosas que ralentiza más las operaciones de IO en discos giratorios es mover la cabeza del disco.

Es razonable suponer, y probablemente sea bastante preciso, que sus muchos pequeños archivos (que están relacionados entre sí) están más juntos en el disco que cerca del destino de la copia (suponiendo que está copiando de una parte de un disco). a otra parte del mismo disco). Si copia por un bit luego escribe un poco, abre una ventana de oportunidad para que otros procesos muevan la cabeza del disco en el disco de origen o de destino.

Una cosa que XCopy hace mucho mejor que Copiar (es decir, en ambos casos, los comandos) es que XCopy lee en un grupo de archivos antes de comenzar a escribir esos archivos en el destino.

Si está copiando archivos en el mismo disco, intente asignar un búfer grande para leer en muchos archivos a la vez, luego escriba esos archivos una vez que el búfer esté lleno).

Si está leyendo desde un disco y escribiendo en otro disco, intente iniciar un hilo para leer desde el disco de origen y un hilo separado para escribir en el otro disco.