Parallel.ForEach Más lento que ForEach

Aquí está el código:

using (var context = new AventureWorksDataContext()) { IEnumerable _customerQuery = from c in context.Customers where c.FirstName.StartsWith("A") select c; var watch = new Stopwatch(); watch.Start(); var result = Parallel.ForEach(_customerQuery, c => Console.WriteLine(c.FirstName)); watch.Stop(); Debug.WriteLine(watch.ElapsedMilliseconds); watch = new Stopwatch(); watch.Start(); foreach (var customer in _customerQuery) { Console.WriteLine(customer.FirstName); } watch.Stop(); Debug.WriteLine(watch.ElapsedMilliseconds); } 

El problema es que Parallel.ForEach tarda unos 400 ms frente a un foreach normal, que tarda unos 40 ms. ¿Qué estoy haciendo exactamente mal y por qué esto no funciona como lo espero?

Supongamos que tiene una tarea que realizar. Digamos que eres un profesor de matemáticas y tienes veinte papeles para calificar. Le toma dos minutos calificar un papel, por lo que le tomará unos cuarenta minutos.

Supongamos ahora que decides contratar a algunos asistentes para que te ayuden a calificar los trabajos. Te lleva una hora localizar a cuatro asistentes. Cada uno toma cuatro documentos y todo está listo en ocho minutos. Has cambiado 40 minutos de trabajo por 68 minutos totales de trabajo, incluida la hora extra para encontrar a los asistentes, por lo que no es un ahorro. La sobrecarga de encontrar a los asistentes es mayor que el costo de hacer el trabajo usted mismo.

Ahora supongamos que tiene veinte mil papeles para calificar, por lo que le tomará unos 40000 minutos. Ahora, si pasas una hora buscando ayudantes, es una victoria. Cada uno de ustedes toma 4000 documentos y lo hacen en un total de 8060 minutos en lugar de 40000 minutos, un ahorro de casi un factor de 5. La sobrecarga de encontrar asistentes es básicamente irrelevante.

La paralelización no es gratis . El costo de dividir el trabajo entre los diferentes hilos debe ser pequeño en comparación con la cantidad de trabajo realizado por hilo.

Otras lecturas:

https://en.wikipedia.org/wiki/Amdahl%27s_law

https://en.wikipedia.org/wiki/Gustafson%27s_law

Lo primero que debes tener en cuenta es que no todo el paralelismo es beneficioso. Hay una cantidad de sobrecarga al paralelismo, y esta sobrecarga puede o no ser significativa dependiendo de la complejidad a la que se está paralelizando. Como el trabajo en su función paralela es muy pequeño, la sobrecarga de la gestión que tiene que hacer el paralelismo se vuelve significativa, lo que ralentiza el trabajo general.

La sobrecarga adicional de crear todos los hilos para su VS enumerable simplemente ejecutando el numerable es más que probable la causa de la desaceleración. Parallel.ForEach no es un movimiento general de aumento de rendimiento; necesita ser pesado ya sea que la operación que se completará para cada elemento sea o no bloqueable.

Por ejemplo, si hiciera una solicitud web o algo así en lugar de simplemente escribir en la consola, la versión paralela podría ser más rápida. Tal como está, simplemente escribir en la consola es una operación muy rápida, por lo que la sobrecarga de crear los hilos y comenzarlos será más lenta.

Como escritor anterior ha dicho que hay algunos gastos generales asociados con Parallel.ForEach , pero esa no es la razón por la que no puede ver su mejora de rendimiento. Console.WriteLine es una operación síncrona, por lo que solo funciona un hilo a la vez. Intenta cambiar el cuerpo a algo que no sea bloqueador y verás que el rendimiento aumenta (siempre que la cantidad de trabajo en el cuerpo sea lo suficientemente grande como para superar la sobrecarga).