Con Java 8 y lambdas es fácil iterar sobre colecciones como flujos, e igual de fácil usar un flujo paralelo. Dos ejemplos de los documentos , el segundo usando 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 orden, ¿siempre sería beneficioso usar el paralelo? Uno pensaría que es más rápido dividir el trabajo en más núcleos.
¿Hay otras consideraciones? ¿Cuándo debe usarse la corriente paralela y cuándo debe usarse la no paralela?
(Se le pide a esta pregunta que provoque una discusión sobre cómo y cuándo usar flujos paralelos, no porque piense que usarlos siempre es una buena idea).
fuente
Runnable
que llamostart()
para usarlosThreads
, ¿está bien cambiar eso para usar flujos de Java 8 en.forEach()
paralelo? Entonces podría quitar el código del hilo de la clase. ¿Pero hay alguna desventaja?La API Stream se diseñó para facilitar la escritura de cálculos de una manera que se abstraía de cómo se ejecutarían, facilitando el cambio entre secuencial y paralelo.
Sin embargo, solo porque sea fácil, no significa que siempre sea una buena idea, y de hecho, es una mala idea simplemente dejar
.parallel()
todo el lugar simplemente porque puedes.Primero, tenga en cuenta que el paralelismo no ofrece otros beneficios que la posibilidad de una ejecución más rápida cuando hay más núcleos disponibles. Una ejecución paralela siempre implicará más trabajo que una secuencial, porque además de resolver el problema, también debe realizar el despacho y la coordinación de subtareas. La esperanza es que pueda obtener la respuesta más rápido al dividir el trabajo en múltiples 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 limitando las operaciones involucradas (es decir, los operadores de reducción deben ser apátridas y asociativos).
En realidad, a veces el paralelismo acelerará su cálculo, a veces no, y a veces incluso lo ralentizará. Lo mejor es desarrollar primero usando la ejecución secuencial y luego aplicar paralelismo donde
(A) usted sabe que en realidad hay un beneficio para un mayor rendimiento
(B) que realmente ofrecerá un mayor rendimiento.
(A) es un problema comercial, no técnico. Si es un experto en rendimiento, generalmente podrá mirar el código y determinar (B), pero la ruta inteligente es medir. (Y ni siquiera se moleste hasta que esté convencido de (A); si el código es lo suficientemente rápido, mejor aplicar sus ciclos cerebrales en otro lugar)
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 producto NQ supere algún umbral antes de comenzar a obtener un beneficio de rendimiento. Para un problema de baja Q como "sumar números del 1 al N", generalmente verá un punto de equilibrio entre N = 1000 y N = 10000. Con problemas de mayor Q, verá los puntos de equilibrio en los umbrales más bajos.
Pero la realidad es bastante complicada. Por lo tanto, hasta que obtenga experiencia, primero identifique cuándo el procesamiento secuencial realmente le está costando algo y luego mida si el paralelismo ayudará.
fuente
findAny
lugar defindFirst
...myListOfURLs.stream().map((url) -> downloadPage(url))...
).ForkJoinPool.commonPool()
y no desea que las tareas de bloqueo vayan allí.Vi una de las presentaciones de Brian Goetz (Arquitecto de lenguaje Java y líder de especificaciones para Lambda Expressions) . Explica en detalle los siguientes 4 puntos a considerar antes de ir a la paralelización:
Costos de división / descomposición
: ¡a veces dividir es más costoso que simplemente hacer el trabajo!
Despacho de tareas / costos de administración
: 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 fusionar conjuntos es costoso.
Localidad
- El elefante en la habitación. Este es un punto importante que todos pueden pasar por alto. Debería considerar los errores de caché, si una CPU espera datos debido a errores de caché, entonces no ganaría nada por la paralelización. Es por eso que las fuentes basadas en matriz paralelizan mejor a medida que se almacenan en caché los siguientes índices (cerca del índice actual) y hay menos posibilidades de que la CPU experimente una pérdida de caché.
También menciona una fórmula relativamente simple para determinar una posibilidad de aceleración paralela.
Modelo NQ :
donde,
N = número de elementos de datos
Q = cantidad de trabajo por elemento
fuente
JB golpeó el clavo en la cabeza. Lo único que puedo agregar es que Java 8 no hace 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.
fuente
ArrayList
/HashMap
.Otras respuestas ya han cubierto la creación de perfiles para evitar la optimización prematura y los costos generales en el procesamiento paralelo. Esta respuesta explica la elección ideal de las estructuras de datos para la transmisión en paralelo.
Fuente: Artículo # 48 Tenga precaución al hacer flujos paralelos, Java 3e efectivo por Joshua Bloch
fuente
Nunca paralelice una corriente infinita con un límite. Esto es lo que pasa:
Resultado
Lo mismo si usas
.limit(...)
Explicación aquí: Java 8, el uso de .parallel en una secuencia provoca un error OOM
Del mismo modo, no use paralelo si la secuencia está ordenada y tiene muchos más elementos de los que desea procesar, p. Ej.
Esto puede durar mucho más tiempo porque los subprocesos paralelos pueden funcionar en muchos rangos de números en lugar del crucial 0-100, lo que lleva mucho tiempo.
fuente