Cómo trabajar con Parallel LINQ en C #

Language Integrated Query, también conocido como LINQ, es una canalización de ejecución de consultas que agrega capacidades de consulta a los lenguajes dirigidos al entorno administrado de .Net. Parallel LINQ, o PLINQ, es un motor de ejecución de consultas que se ejecuta sobre el entorno administrado de .Net y aprovecha los múltiples procesadores o núcleos de su sistema informático para ejecutar las consultas en paralelo. En otras palabras, le permite optimizar sus consultas dividiéndolas en partes para ejecutar estas partes en paralelo y, por lo tanto, aumentar el rendimiento de la consulta.

PLINQ es una extensión de LINQ y se introdujo como parte de .Net Framework 4. Es un motor de ejecución de consultas de Microsoft y forma parte de la biblioteca de extensiones paralelas. La biblioteca de extensiones paralelas está compuesta a su vez por TPL (Task Parallel Library) y PLINQ. Microsoft ha proporcionado soporte para la programación paralela en .Net Framework para aprovechar los beneficios de los sistemas de múltiples núcleos. Para aprovechar las capacidades de programación paralela, se introdujo una nueva clase llamada Parallel en .Net Framework 4.

PLINQ es una buena opción en operaciones de cálculo. Pero, ¿de qué se trata y cuáles son los problemas que puede solucionar? ¿Es apropiado usarlo en lugar de LINQ siempre que necesitemos consultar datos? Discutiríamos todos estos en un momento, pero primero comprendamos cómo funciona PLINQ detrás de escena. PLINQ funciona particionando la fuente de datos o la entrada en fragmentos que, a su vez, son ejecutados por diferentes subprocesos.

Un poco de código ahora

Considere la siguiente consulta LINQ.

var data = from e in employees

           where e.FirstName.StartsWith("J")

           select e;

Puede convertir la consulta anterior fácilmente en una consulta PLINQ utilizando el método de extensión AsParallel. Tenga en cuenta que AsParallel es un método de extensión de la clase System.Linq.ParallelEnumerable.

var data = from e in employees.AsParallel()

           where e.FirstName.StartsWith("J")

           select e;

Si desea conservar el orden del resultado de la consulta, puede aprovechar el método AsOrdered.

var data = from e in employees.AsParallel().AsOrdered()

           where e.FirstName.StartsWith("J")

           select e;

También puede preservar el orden de los datos que se devuelven como resultado de la ejecución de la consulta PLINQ pasando QueryOptions.PreserveOrdering como parámetro al método AsParallel.

var data = from e in employees.AsParallel(QueryOptions.PreserveOrdering)

           where e.FirstName.StartsWith("J")

           select e;

Tenga en cuenta que no se recomienda utilizar el método AsParallel () en colecciones pequeñas; preferiría ejecutarse más lento en comparación con una consulta normal. ¿Qué pasa si quieres forzar el paralelismo? Sin embargo, esto no se recomienda, pero puede aprovechar el método de extensión WithExecutionMode para lograrlo. Aquí hay un ejemplo que ilustra esto.

var data = from e in employees.AsParallel().WithExecutionMode

                (ParallelExecutionMode.ForceParallelism)

           where e.FirstName.StartsWith("J")

           select e;

Tenga en cuenta que ParallelExecutionMode es una enumeración que está disponible como parte del espacio de nombres System.Linq y puede tener uno de estos valores: Default y ForceParallelism. Si especifica Default como parámetro para el método de extensión WithExecutionMode, PLINQ ejecutará la consulta en paralelo si es evidente una mejora en el rendimiento al ejecutar la consulta en paralelo. De lo contrario, PLINQ ejecutará la consulta como una consulta LINQ. Por el contrario, si especifica ForeParallelism como parámetro para el método de extensión WithExecutionMode, PLINQ ejecutará la consulta en paralelo incluso si eso puede incurrir en una penalización de rendimiento.

¿Cómo limito el grado de paralelismo?

También debe tener en cuenta otro concepto relacionado: grado de paralelismo. Este es un número entero sin signo que denota el número máximo de procesadores que su consulta PLINQ debe aprovechar mientras está en ejecución. En otras palabras, el grado de paralelismo es un número entero que denota el número máximo de tareas que se ejecutarían simultáneamente para procesar una consulta.

Por cierto, el valor predeterminado del grado de paralelismo es 64, lo que implica que PLINQ puede aprovechar un máximo de 64 procesadores en su sistema. A continuación, le mostramos cómo puede limitar el grado de paralelismo en PLINQ a dos procesadores en su sistema.

var data = from e in employees.AsParallel().WithDegreeOfParallelism(2)

           where e.FirstName.StartsWith("J")

           select e;

Observe cómo se ha pasado el número de procesadores como argumento al método WithDegreeofParallelism. Debe especificar un valor más alto para el grado de paralelismo para las ganancias de rendimiento si su consulta realiza más trabajo sin límite de cálculo, es decir, sin límite de CPU.

Recomiendo ampliamente leer el documento "Patrones de programación paralela" de Stephen Toub. Proporciona una discusión en profundidad sobre los patrones de programación paralela en .Net.