c# - threads - Extensiones Paralelas
parallel.for c# (5)
Tengo una aplicación con operaciones pesadas de IO, como copiar archivos, comprimir y mover los archivos por el sistema de archivos, y copiar en servidores de respaldo.
Construyo este programa como de un solo hilo. Funciona en 2 minutos.
Creé otra versión de este programa con extensiones paralelas y el uso de Tarea, que se ejecuta casi en 2 minutos también.
En otras palabras, no vi una ganancia en el rendimiento al usar Parallels debido a IO pesado.
¿Obtendré los mismos resultados si despliego la aplicación a un servidor Blade?
¿Los servidores blade procesan IO más rápido / en múltiples canales que mi estación de trabajo?
¿No se beneficia el uso de Parallels con aplicaciones vinculadas a IO?
Creo que la ventaja de las extensiones paralelas podría ser significativa en las operaciones de la CPU. Donnu cómo se supone que afecta a IO tho.
Si mueve archivos en un dispositivo físico, no verá mucho beneficio en el rendimiento al realizar múltiples solicitudes de IO paralelo en el mismo dispositivo. El dispositivo ya está funcionando muchos órdenes de magnitud más lento que la CPU, por lo que las solicitudes múltiples hechas en paralelo se alinearán para ser manejadas una por una en el dispositivo. Su código paralelo se serializa porque todo está accediendo al mismo dispositivo que realmente no puede manejar más de una solicitud a la vez.
Es posible que vea una pequeña mejora de rendimiento con código paralelo si su controlador de disco implementa "búsqueda de ascensor", "recopilación de dispersión" u otras operaciones fuera de servicio, pero la diferencia de rendimiento será relativamente pequeña.
Donde debe encontrar una diferencia de rendimiento más gratificante para la E / S de archivos es cuando mueve archivos entre muchos dispositivos físicos diferentes. Debería poder mover o copiar un archivo en el disco A a otra ubicación en el disco A y también copiar un archivo en el disco B en el disco C. Con muchos dispositivos físicos, no tiene todas las solicitudes paralelas apiladas esperando el único dispositivo para llenar todas las solicitudes.
Probablemente verás resultados similares con la E / S de red: si todo está pasando por una tarjeta / segmento de red Ethernet, no vas a notar tanto paralelismo como cuando tienes múltiples tarjetas de ethernet y múltiples segmentos de red para trabajar.
Tengo una aplicación que se implementa en WinForms que procesa ~ 7,800 URL en aproximadamente 5 minutos (descarga la URL, analiza el contenido, busca piezas específicas de datos y si encuentra lo que busca hace un procesamiento adicional de esos datos).
Esta aplicación específica solía tardar entre 26 y 30 minutos en ejecutarse, pero al cambiar el código al TPL (Biblioteca paralela de tareas en .NET v4.0) se ejecuta en solo 5. La computadora es una estación de trabajo Dell T7500 con doble núcleo cuádruple Procesadores Xeon (3 GHz), que se ejecutan con 24 GB de RAM y Windows 7 Ultimate edición de 64 bits.
Sin embargo, no es exactamente lo mismo que su situación, esto también es extremadamente intensivo de IO. La documentación en TPL indica que fue concebida originalmente para conjuntos de problemas vinculados al procesador, pero esto no excluye su uso en situaciones IO (como me demuestra mi aplicación). Si tiene al menos 4 núcleos y no ve que su tiempo de procesamiento disminuya significativamente, es posible que tenga otros problemas de implementación que impiden que el TPL sea realmente eficiente (bloqueos, elementos del disco duro, etc.). El libro Parallel Programming with Microsoft .NET realmente me ayudó a entender "cómo" su código necesita ser modificado para realmente aprovechar todo ese poder.
Vale la pena mirar en mi opinión.
Todo depende de si estás vinculado a la CPU o IO. Sugeriría hacer algunas pruebas de rendimiento para ver dónde están los cuellos de botella.
Si observa que se está moviendo y comprimiendo una gran cantidad de archivos (en diferentes discos, como un movimiento en el mismo disco es solo un cambio en la tabla FAT), le conviene ver cómo implementa un elemento de archivo de transmisión que se comprime a medida que se mueve. Esto puede ahorrar el IO extra de volver a leer los archivos después de moverlos. He hecho esto con movimientos y sumas de comprobación y en mi caso fue un gran bache de rendimiento.
Espero que esto ayude.
Si todo lo que hace es copiar o mover archivos por el sistema, entonces el paralelismo proporcionado por el TPL no le servirá de mucho. Moverse, por ejemplo, realmente no usa ninguna CPU, simplemente cambia la ubicación de los archivos en la estructura de registro del directorio del disco.
La compresión de archivos es una historia diferente. Aquí está cargando datos y usando la CPU para comprimirlo antes de guardarlo en el disco. Es posible que pueda usar una tubería o un bucle paralelo para cargar / comprimir / guardar los datos de una manera más eficiente. En lugar de tener un hilo trabajando en la compresión de cada archivo, podría tener varios hilos trabajando en diferentes archivos.
El siguiente código comprime una carga de archivos secuencialmente y luego en paralelo. Obtengo los siguientes momentos en un i7 920 y con un SSD Intel X25 que comprime 329 imágenes JPG que suman 800Mb de datos.
Secuencial: 39901ms
Paralelo: 12404ms
class Program
{
static void Main(string[] args)
{
string[] paths = Directory.GetFiles(@"C:/temp", "*.jpg");
DirectoryInfo di = new DirectoryInfo(@"C:/temp");
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (FileInfo fi in di.GetFiles("*.jpg"))
{
Compress(fi);
}
sw.Stop();
Console.WriteLine("Sequential: " + sw.ElapsedMilliseconds);
Console.WriteLine("Delete the results files and then rerun...");
Console.ReadKey();
sw.Reset();
sw.Start();
Parallel.ForEach(di.GetFiles("*.jpg"), (fi) => { Compress(fi); });
sw.Stop();
Console.WriteLine("Parallel: " + sw.ElapsedMilliseconds);
Console.ReadKey();
}
public static void Compress(FileInfo fi)
{
using (FileStream inFile = fi.OpenRead())
{
if ((File.GetAttributes(fi.FullName)
& FileAttributes.Hidden)
!= FileAttributes.Hidden & fi.Extension != ".gz")
{
using (FileStream outFile =
File.Create(fi.FullName + ".gz"))
{
using (GZipStream Compress =
new GZipStream(outFile,
CompressionMode.Compress))
{
inFile.CopyTo(Compress);
}
}
}
}
}
}
Para el código de compresión, vea Cómo: Comprimir archivos