recursiveaction programacion paralelos paralelo paralela examples ejemplos docs algoritmos java parallel-processing fork-join

programacion - Resultados de Escalabilidad inesperados en Java Fork-Join(Java 8)



java paralelo (1)

Recientemente, estaba ejecutando algunos experimentos de escalabilidad utilizando Java Fork-Join. Aquí, utilicé ForkJoinPool, el constructor ForkJoinPool(int parallelism) no predeterminado ForkJoinPool(int parallelism) , pasando el paralelismo deseado (# workers) como argumento constructor.

Específicamente, usando la siguiente pieza de código:

public static void main(String[] args) throws InterruptedException { ForkJoinPool pool = new ForkJoinPool(Integer.parseInt(args[0])); pool.invoke(new ParallelLoopTask()); } static class ParallelLoopTask extends RecursiveAction { final int n = 1000; @Override protected void compute() { RecursiveAction[] T = new RecursiveAction[n]; for(int p = 0; p < n; p++){ T[p] = new DummyTask(); T[p].fork(); } for(int p = 0; p < n; p++){ T[p].join(); } /* //The problem does not occur when tasks are joined in the reverse order, i.e. for(int p = n-1; p >= 0; p--){ T[p].join(); } */ } } static public class DummyTask extends RecursiveAction { //performs some dummy work final int N = 10000000; //avoid memory bus contention by restricting access to cache (which is distributed) double val = 1; @Override protected void compute() { for(int j = 0; j < N; j++){ if(val < 11){ val *= 1.1; }else{ val = 1; } } } }

Obtuve estos resultados en un procesador con 4 núcleos físicos y 8 lógicos (usando java 8: jre1.8.0_45):

T1: 11730

T2: 2381 (aceleración: 4,93)

T4: 2463 (aceleración: 4,76)

T8: 2418 (aceleración: 4,85)

Mientras que cuando uso java 7 (jre1.7.0), obtengo

T1: 11938

T2: 11843 (aceleración: 1,01)

T4: 5133 (aceleración: 2,33)

T8: 2607 (aceleración: 4,58)

(donde TP es el tiempo de ejecución en ms, usando el nivel de paralelismo P)

Si bien ambos resultados me sorprenden, lo último que puedo entender (la combinación causará que 1 trabajador (ejecutando el ciclo) bloquee, ya que no reconoce que podría, mientras espera, procesar otras tareas ficticias pendientes de su cola local). El primero, sin embargo, me desconcertó.

Por cierto: al contar el número de tareas ficticias iniciadas, pero aún no completadas, encontré que hasta 24 de esas tareas existían en un grupo con paralelismo 2 en algún momento ...?

EDITAR:

Hice una evaluación comparativa de la aplicación anterior usando JMH (jdk1.8.0_45) (opciones -bm avgt -f 1) (= 1 bifurcación, 20 + 20 iteraciones) Los resultados a continuación

T1: 11,664

11,664 ±(99.9%) 0,044 s/op [Average] (min, avg, max) = (11,597, 11,664, 11,810), stdev = 0,050 CI (99.9%): [11,620, 11,708] (assumes normal distribution)

T2: 4,134 (aceleración: 2,82)

4,134 ±(99.9%) 0,787 s/op [Average] (min, avg, max) = (3,045, 4,134, 5,376), stdev = 0,906 CI (99.9%): [3,348, 4,921] (assumes normal distribution)

T4: 2,972 (aceleración: 3,92)

2,972 ±(99.9%) 0,212 s/op [Average] (min, avg, max) = (2,375, 2,972, 3,200), stdev = 0,245 CI (99.9%): [2,759, 3,184] (assumes normal distribution)

T8: 2,845 (aceleración: 4,10)

2,845 ±(99.9%) 0,306 s/op [Average] (min, avg, max) = (2,277, 2,845, 3,310), stdev = 0,352 CI (99.9%): [2,540, 3,151] (assumes normal distribution)

A primera vista, uno pensaría que estos resultados de escalabilidad están más cerca de lo que cabría esperar, es decir, T1 <T2 <T4 ~ T8. Sin embargo, lo que todavía me molesta es lo siguiente:

  1. La diferencia para T2 entre java 7 y 8. Supongo que una explicación sería que el trabajador que ejecuta el bucle paralelo no está inactivo en Java 8, sino que busca otro trabajo para realizar.
  2. La aceleración súper lineal (3x) con 2 trabajadores. Además, tenga en cuenta que T2 parece aumentar con cada iteración (véase más abajo, tenga en cuenta que este es también el caso, aunque en menor medida con P = 4,8). Los tiempos en las primeras iteraciones del calentamiento son similares a los mencionados anteriormente. Tal vez el período de calentamiento debería ser más largo, pero aún así, ¿no es extraño que el tiempo de ejecución aumente, es decir, prefiero esperar que disminuya?
  3. Finalmente, todavía encuentro la observación de que hay muchas más tareas iniciadas y no completadas que las que son curiosas.

>

Run progress: 0,00% complete, ETA 00:00:40 Fork: 1 of 1 Warmup Iteration 1: 2,365 s/op Warmup Iteration 2: 2,341 s/op Warmup Iteration 3: 2,393 s/op Warmup Iteration 4: 2,323 s/op Warmup Iteration 5: 2,925 s/op Warmup Iteration 6: 3,040 s/op Warmup Iteration 7: 2,304 s/op Warmup Iteration 8: 2,347 s/op Warmup Iteration 9: 2,939 s/op Warmup Iteration 10: 3,083 s/op Warmup Iteration 11: 3,004 s/op Warmup Iteration 12: 2,327 s/op Warmup Iteration 13: 3,083 s/op Warmup Iteration 14: 3,229 s/op Warmup Iteration 15: 3,076 s/op Warmup Iteration 16: 2,325 s/op Warmup Iteration 17: 2,993 s/op Warmup Iteration 18: 3,112 s/op Warmup Iteration 19: 3,074 s/op Warmup Iteration 20: 2,354 s/op Iteration 1: 3,045 s/op Iteration 2: 3,094 s/op Iteration 3: 3,113 s/op Iteration 4: 3,057 s/op Iteration 5: 3,050 s/op Iteration 6: 3,106 s/op Iteration 7: 3,080 s/op Iteration 8: 3,370 s/op Iteration 9: 4,482 s/op Iteration 10: 4,325 s/op Iteration 11: 5,002 s/op Iteration 12: 4,980 s/op Iteration 13: 5,121 s/op Iteration 14: 4,310 s/op Iteration 15: 5,146 s/op Iteration 16: 5,376 s/op Iteration 17: 4,810 s/op Iteration 18: 4,320 s/op Iteration 19: 5,249 s/op Iteration 20: 4,654 s/op


No hay nada en su ejemplo de cómo hizo este punto de referencia. Parece que acabas de hacer un milli-time al principio y al final de la carrera. Esto no es exacto Sugiero que eche un vistazo a esta respuesta SO y vuelva a publicar sus tiempos. Por cierto, el benchmark jmh va a ser el estándar en Java9, así que eso es lo que deberías estar usando.

EDITAR:

Usted admite que los resultados de escalabilidad son los que esperaba. Pero dices que todavía no estás satisfecho con los resultados. Ahora es el momento de mirar dentro del código.

Hay serios problemas con este marco. He estado escribiendo una crítica al respecto desde 2010. Como señalo aquí , unirse no funciona. El autor ha intentado varios medios para solucionar el problema, pero el problema persiste.

Aumente el tiempo de ejecución a aproximadamente un minuto (n = 100000000) o realice cálculos pesados ​​en el cálculo (). Ahora perfila la aplicación en VisualVM u otro generador de perfiles. Esto le mostrará los hilos de espera, los hilos excesivos, etc.

Si eso no ayuda a responder sus preguntas, debe mirar el flujo del código usando un depurador. El análisis de perfiles y códigos es la única forma en que obtendrá respuestas satisfactorias a sus preguntas.