2012-07-06 26 views
9

estoy haciendo una sencilla herramienta de copia de seguridad de sincronización de carpetas para mí y se encontró con un buen control de carretera usando 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..net File.Copy muy lento al copiar muchos archivos pequeños (no más de red)

Encontré un interesting article about a better File.Copy replacement, pero el código 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 formas comunes o fáciles de reemplazar File.Copy con algo más rápida?

Respuesta

4

Una de las cosas que ralentiza las operaciones de IO más en los discos de rotación se mueve, la cabeza del disco.

Es razonable suponer y probablemente sea bastante preciso que sus muchos archivos pequeños (que todos 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 uno 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 (lo que significa 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 va a copiar los archivos en el mismo disco, intente asignar un búfer grande como para leer en muchos archivos a la vez, a continuación, escribir esos archivos una vez que el buffer está lleno).

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

+0

¡Gracias por la buena información! Estaba particularmente interesado en intentar almacenar en búfer las lecturas/escrituras como describió XCopy. Hice algunas pruebas con un búfer de 50 mb y descubrí que mi tiempo de copia se redujo a 14 min 40 s. Entonces no es una mejora sorprendente, sino mejor. Todavía años luz detrás de los tiempos de XCopy. Veré si enhebrar las lecturas/escrituras ayuda a continuación ... – Guavaman

+0

En realidad, volví a 16 minutos después de darme cuenta de que mi sistema de copia en búfer basado en FileStream no copiaba las propiedades del archivo (atributos, tiempo de creación, etc.) Después de volver a agregarlos, el tiempo de copia vuelve a estar en File.Copy, solo he perdido 50 MB de memoria en el almacenamiento en búfer. :( – Guavaman

0

No tengo buena experiencia en este nivel. ¿Por qué no intenta ejecutar un archivo por lotes que contiene su comando xcopy? Compruebe este post: Executing Batch File in C#

8

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.

He escrito un programa similar y en mi experiencia, mi código corría 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 Enumerar en lugar de Obtener funciones (por ejemplo, EnumerateFiles en 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.

+3

+1. Especialmente para usar la enumeración diferida. –

+0

Tengo mi interfaz en un hilo de fondo. Gracias por los consejos para actualizar la pantalla con menos frecuencia. Desafortunadamente, esto no es realmente un problema para mis tiempos de copia. La actualización de la interfaz de usuario está completamente deshabilitada, el tiempo de copia es el mismo que el anterior. Veré si el uso de EnumerateFiles ayuda. – Guavaman

+0

Acabo de obtener los resultados del uso de IEnumerables. No ayudó en absoluto a los tiempos de copia del archivo, probablemente porque voy a carpeta por carpeta copiando archivos por lo que no lleva mucho tiempo hacer GetFiles la mayor parte del tiempo. Sin embargo, ayudó un poco al hacer que el proceso inicial de conteo de archivos fuera más fluido. – Guavaman

0

Creo que al menos podría paralizarlo para que pueda hacer 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. El uso de muchos hilos no ayudará porque esto hará que el movimiento de accionamiento alrededor de un montón más en lugar de ser capaz de 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) { } 
1

Hay dos algoritmos para la copia de archivos más rápido:

Si la fuente y el destino son discos diferentes Entonces:

  • Una secuencia de lectura de archivos continuamente y el almacenamiento en un búfer.
  • Otro hilo que escribe archivos continuamente desde ese búfer.

Si la fuente y el destino es el mismo disco a continuación:

  • leer un trozo fijo de bytes, dicen 8K a la vez, no importa la cantidad de archivos que es.
  • Escribe ese fragmento fijo en el destino, ya sea en un archivo o en varios archivos.

De esta manera obtendrá un rendimiento significativo.

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.

Cuestiones relacionadas