streams parallel lambdas ejemplo java lambda parallel-processing java-8 java-stream

java - parallel - Secuencia paralela vs secuencia en serie



stream java 8 ejemplo (3)

Cuando utiliza reduce con un flujo paralelo, las operaciones no se realizan en un orden específico.

Por lo tanto, si desea que las transmisiones paralelas produzcan un resultado predecible, su operación de reducción debe tener la misma respuesta, sin importar en qué orden se realicen las cosas.

Por ejemplo, reducir el uso de la suma tiene sentido, porque la adición es asociativa. No importa cuál de estos haga, la respuesta es 6 en ambos casos.

(1 + 2) + 3 1 + (2 + 3)

atan2 no es asociativa.

Math.atan2(Math.atan2(1, 2), 3) == 0.15333604941031637

mientras

Math.atan2(1, Math.atan2(2, 3)) == 1.0392451500584097

¿Es posible que una secuencia paralela pueda dar un resultado diferente que una secuencia en serie en Java 8? De acuerdo con mi información, un flujo paralelo es lo mismo que un flujo en serie, excepto que se divide en varios flujos secundarios. Es una cuestión de velocidad. Todas las operaciones sobre los elementos se realizan y los resultados de los subflujos se combinan al final. Al final, en mi opinión, el resultado de las operaciones debería ser el mismo para las transmisiones en paralelo y en serie. Entonces, mi pregunta es, ¿es posible que este código me dé un resultado diferente? Y si es posible, ¿por qué sucede?

int[] i = {1, 2, 5, 10, 9, 7, 25, 24, 26, 34, 21, 23, 23, 25, 27, 852, 654, 25, 58}; Double serial = Arrays.stream(i).filter(si -> { return si > 5; }).mapToDouble(Double::new).map(NewClass::add).reduce(Math::atan2).getAsDouble(); Double parallel = Arrays.stream(i).filter(si -> { return si > 5; }).parallel().mapToDouble(Double::new).map(NewClass::add).reduce(Math::atan2).getAsDouble(); System.out.println("serial: " + serial); System.out.println("parallel: " + parallel);

public static double add(double i) { return i + 0.005; }

y los resultados son:

serial: 3.6971567726175894E-23 parallel: 0.779264049587662


El javadoc para reduce() dice:

Realiza una reducción en los elementos de este flujo, utilizando una función de acumulación asociativa , [...] La función de acumulador debe ser una función asociativa .

La palabra "associative" está vinculada a este documento java:

Un operador u función op es asociativo si se cumple lo siguiente:

(a op b) op c == a op (b op c)

La importancia de esto para la evaluación paralela se puede ver si ampliamos esto a cuatro términos:

a op b op c op d == (a op b) op (c op d)

Entonces podemos evaluar (a op b) en paralelo con (c op d), y luego invocar op sobre los resultados.

Los ejemplos de operaciones asociativas incluyen suma numérica, mín. Y máx. Y concatenación de cadenas.

Como @PaulBoddington se menciona en un comentario, atan2 no es asociativo y, por lo tanto, no es válido para una operación de reducción.

No relacionado

Tu secuencia de secuencias está un poco apagada. Debería filtrar después de la operación paralela, la lambda se puede acortar y no se debe encuadrar el doble:

double parallel = Arrays.stream(i) .parallel() // <-- before filter .filter(si -> si > 5) // <-- shorter .asDoubleStream() // <-- not boxing .reduce(Math::atan2) .getAsDouble();


Su método de reducción produce resultados diferentes, si los elementos se dan en un orden diferente.

Por lo tanto, si utiliza un flujo paralelo, el pedido original no está garantizado.

Si utiliza un método de reducción diferente (por ejemplo, (x, y) -> x + y) funciona bien.