Creo que las garantías actuales de Java memory model hacen que sea bastante difícil hacer mucho, si es que lo hace, la paralelización automática en el compilador o nivel de VM. El lenguaje Java no tiene semántica para garantizar que cualquier estructura de datos sea incluso efectivamente inmutable, o que cualquier declaración particular sea pura y libre de efectos colaterales, por lo que el compilador tendría que resolverlos automáticamente para poder paralelizarlos. Algunas oportunidades elementales serían posibles de inferir en el compilador, pero el caso general quedaría en el tiempo de ejecución, ya que la carga dinámica y el enlace podrían introducir nuevas mutaciones que no existían en tiempo de compilación.
Considere el siguiente código:
for (int i = 0; i < array.length; i++) {
array[i] = expensiveComputation(array[i]);
}
sería trivial para paralelizar, si expensiveComputation
es una pure function, cuya salida depende sólo de su argumento, y si pudiéramos garantizar que array
no sería cambiado durante el bucle (en realidad lo estamos cambiando, estableciendo array[i]=...
, pero en este caso particular, siempre se llama primero a expensiveComputation(array[i])
, por lo que está bien aquí - asumiendo que array
es local y no se hace referencia desde ningún otro lugar).
Por otra parte, si cambiamos el bucle de la siguiente manera:
for (int i = 0; i < array.length; i++) {
array[i] = expensiveComputation(array, i);
// expensiveComputation has the whole array at its disposal!
// It could read or write values anywhere in it!
}
continuación paralelización no es trivial, más aún si expensiveComputation
es puro y no altera su argumento, porque los hilos paralelos se estar cambiando el contenido de array
mientras que otros lo están leyendo! El paralelizador tendría que averiguar al que se refieren las partes de la matriz expensiveComputation
en diversas condiciones, y sincronizar en consecuencia.
Tal vez no sería imposible de plano para detectar todas las mutaciones y los efectos secundarios que pueden estar pasando y tomar en cuenta los paralelización cuando, pero sería muy duro, a ciencia cierta, probablemente no factible en práctica. Esta es la razón por la que la paralelización, y el hecho de saber que todo sigue funcionando correctamente, es el dolor de cabeza del programador en Java.
Los lenguajes funcionales (por ejemplo, Clojure en JVM) son una buena respuesta a este tema. Las funciones puras sin efectos secundarios junto con las estructuras de datos persistent ("efectivamente inmutables") potencialmente permiten la paralelización implícita o casi implícita. Vamos a duplicar cada elemento de una matriz:
(map #(* 2 %) [1 2 3 4 5])
(pmap #(* 2 %) [1 2 3 4 5]) ; The same thing, done in parallel.
Esto es transparente debido a 2 cosas:
- La función
#(* 2 %)
es puro: toma un valor en y da un valor fuera, y eso es todo. No cambia nada, y su salida depende solo de su argumento.
- El vector
[1 2 3 4 5]
es inmutable: no importa quién lo está mirando o cuándo es el mismo.
Es posible hacer funciones puras en Java, pero 2), la inmutabilidad, es el talón de Aquiles aquí. No hay matrices inmutables en Java. Para ser pedante, nada es inmutable en Java porque incluso los campos final
se pueden cambiar utilizando la reflexión. Por lo tanto, no se puede garantizar que la salida (¡o entrada!) De un cálculo no se modifique por la paralelización -> entonces la paralelización automática es generalmente inviable.
El mudo "elementos duplicar" ejemplo se extiende a un procesamiento complejo arbitrariamente, gracias a la inmutabilidad:
(defn expensivefunction [v x]
(/ (reduce * v) x))
(let [v [1 2 3 4 5]]
(map (partial expensivefunction v) v)) ; pmap would work equally well here!
Normalmente, un ExecutorService o tenedor/JOIN es una mejor elección. Dado que el tiempo que lleva distribuir las tareas entre hilos es alto, es muy poco probable que un compilador automático sea mejor que un código escrito a mano. A menudo, para bucles simples, el uso de múltiples hilos es más lento. –
Muchos otros compiladores hacen este tipo de cosas en estos días, no veo ninguna razón por la que una JVM/JIT no debería. –
Desde mi experiencia práctica, la JVM es bastante buena en esto. – dagnelies