threads parallel number ejemplo collection java parallel-processing java-8 java-stream

parallel - stream java 8 ejemplo



¿Debo usar siempre un flujo paralelo cuando sea posible? (4)

JB golpeó el clavo en la cabeza. Lo único que puedo agregar es que Java8 no realiza un procesamiento paralelo puro, lo hace paraquential Sí, escribí el artículo y he estado haciendo F / J durante treinta años, así que entiendo el problema.

Con Java 8 y las lambdas, es fácil iterar sobre colecciones como secuencias, y tan fácil de usar una secuencia paralela. Dos ejemplos de los documentos , el segundo utilizando parallelStream:

myShapesCollection.stream() .filter(e -> e.getColor() == Color.RED) .forEach(e -> System.out.println(e.getName())); myShapesCollection.parallelStream() // <-- This one uses parallel .filter(e -> e.getColor() == Color.RED) .forEach(e -> System.out.println(e.getName()));

Mientras no me importe el pedido, ¿siempre sería beneficioso utilizar el paralelo? Uno podría pensar que es más rápido dividir el trabajo en más núcleos.

¿Hay otras consideraciones? ¿Cuándo debería usarse la transmisión paralela y cuándo debería usarse la transmisión no paralela?

(Se plantea esta pregunta para iniciar una discusión sobre cómo y cuándo usar flujos paralelos, no porque creo que usarlos siempre es una buena idea).


La API de Stream se diseñó para facilitar la escritura de los cálculos de una manera que se abstraía de la forma en que se ejecutarían, lo que facilita el cambio entre la secuencia y el paralelo.

Sin embargo, solo porque es fácil, no significa que siempre sea una buena idea, y de hecho, es una mala idea simplemente soltar .parallel() todo el lugar simplemente porque puedes.

Primero, tenga en cuenta que el paralelismo no ofrece más beneficios que la posibilidad de una ejecución más rápida cuando haya más núcleos disponibles. Una ejecución paralela siempre implicará más trabajo que una secuencia, porque además de resolver el problema, también tiene que realizar el envío y la coordinación de subtareas. La esperanza es que pueda llegar a la respuesta más rápido al dividir el trabajo en varios procesadores; si esto realmente sucede depende de muchas cosas, incluido el tamaño de su conjunto de datos, cuánto cálculo está haciendo en cada elemento, la naturaleza del cálculo (específicamente, ¿el procesamiento de un elemento interactúa con el procesamiento de otros?) , la cantidad de procesadores disponibles y la cantidad de otras tareas que compiten por esos procesadores.

Además, tenga en cuenta que el paralelismo a menudo también expone el no determinismo en el cálculo que a menudo está oculto por implementaciones secuenciales; a veces esto no importa, o puede mitigarse restringiendo las operaciones involucradas (es decir, los operadores de reducción deben ser apátridas y asociativos).

En realidad, algunas veces el paralelismo acelerará su cálculo, otras no, y otras incluso lo hará más lento. Es mejor desarrollar primero la ejecución secuencial y luego aplicar el paralelismo donde (A) sabe que en realidad se beneficia el aumento del rendimiento y (B) que realmente proporcionará un mayor rendimiento. (A) es un problema de negocios, no técnico. Si usted es un experto en rendimiento, generalmente podrá mirar el código y determinar (B), pero la ruta inteligente es medir. (Y, ni siquiera te molestes hasta que estés convencido de (A); si el código es lo suficientemente rápido, es mejor aplicar los ciclos de tu cerebro en otros lugares).

El modelo de rendimiento más simple para el paralelismo es el modelo "NQ", donde N es el número de elementos y Q es el cálculo por elemento. En general, necesita que el NQ del producto supere algún umbral antes de comenzar a obtener un beneficio de rendimiento. Para un problema de baja Q como "sumar números de 1 a N", generalmente verá un punto de equilibrio entre N = 1000 y N = 10000. Con problemas de mayor Q, verá puntos de equilibrio en umbrales más bajos.

Pero la realidad es bastante complicada. Entonces, hasta que logre la experiencia, primero identifique cuándo el procesamiento secuencial realmente le está costando algo, y luego mida si el paralelismo le ayudará.


Un flujo paralelo tiene una sobrecarga mucho mayor en comparación con una secuencia. La coordinación de los hilos lleva una cantidad significativa de tiempo. Usaría secuencias secuenciales por defecto y solo consideraría las paralelas si

  • Tengo una cantidad masiva de artículos para procesar (o el procesamiento de cada artículo lleva tiempo y es paralelizable)

  • Tengo un problema de rendimiento en primer lugar

  • Aún no ejecuto el proceso en un entorno de subprocesos múltiples (por ejemplo: en un contenedor web, si ya tengo muchas solicitudes para procesar en paralelo, agregar una capa adicional de paralelismo dentro de cada solicitud podría tener más efectos negativos que positivos) )

En su ejemplo, el rendimiento será controlado por el acceso sincronizado a System.out.println() , y hacer que este proceso sea paralelo no tendrá ningún efecto, ni siquiera uno negativo.

Además, recuerde que los flujos paralelos no resuelven mágicamente todos los problemas de sincronización. Si los predicados y las funciones utilizadas en el proceso utilizan un recurso compartido, deberá asegurarse de que todo sea seguro para subprocesos. En particular, los efectos secundarios son cosas de las que realmente tiene que preocuparse si va en paralelo.

En cualquier caso, mida, no adivine! Solo una medida te dirá si el paralelismo vale la pena o no.


Vi una de las presentations de Brian Goetz (Arquitecto del lenguaje Java y líder de especificaciones para Lambda Expressions) . Explica en detalle los siguientes 4 puntos a considerar antes de pasar a la paralelización:

Costos de división / descomposición
- ¡A veces la división es más costosa que solo hacer el trabajo!
Costes de despacho / gestión de tareas
- Puede hacer mucho trabajo en el tiempo que lleva el trabajo manual a otro hilo.
Costos de combinación de resultados
- A veces la combinación implica copiar muchos datos. Por ejemplo, agregar números es barato, mientras que combinar conjuntos es costoso.
Localidad
- El elefante en el cuarto. Este es un punto importante que todos pueden pasar por alto. Debería considerar las fallas de caché, si una CPU espera datos debido a fallas de caché, entonces no obtendría nada por la paralelización. Es por eso que las fuentes basadas en matrices paralelizan lo mejor, ya que los próximos índices (cerca del índice actual) se almacenan en caché y hay menos posibilidades de que la CPU experimente una falta de memoria caché.

También menciona una fórmula relativamente simple para determinar una posibilidad de aceleración paralela.

Modelo NQ :

N x Q > 10000

dónde,
N = número de elementos de datos
Q = cantidad de trabajo por artículo