Se sabe que las funciones puras facilitan la parelización. ¿Qué tiene la programación funcional que la hace inherentemente adaptada a la ejecución paralela?
¿Son los compiladores como Javac lo suficientemente inteligentes como para detectar cuándo un método es una función pura? Siempre se pueden implementar clases que implementan interfaces funcionales como Function , pero tienen efectos secundarios.
functional-programming
Naveen
fuente
fuente
NullPointerException
s. Los beneficios de las optimizaciones basadas en esto también son probablemente bastante pequeños para las aplicaciones Java típicas.Respuestas:
No se trata de "lo suficientemente inteligente". Esto se llama análisis de pureza y es demostrablemente imposible en el caso general: es equivalente a resolver el problema de detención.
Ahora, por supuesto, los optimizadores hacen cosas demostrablemente imposibles todo el tiempo, "demostrablemente imposibles en el caso general" no significa que nunca funcione, solo significa que no puede funcionar en todos los casos. Entonces, de hecho, existen algoritmos para verificar si una función es pura o no, es solo que la mayoría de las veces el resultado será "No sé", lo que significa que, por razones de seguridad y corrección, debe asumir que esta función particular podría ser impura.
E incluso en los casos en que se hace el trabajo, los algoritmos son complejos y costosos.
Entonces, ese es el problema # 1: solo funciona para casos especiales .
Problema # 2: Bibliotecas . Para que una función sea pura, solo puede llamar funciones puras (y esas funciones solo pueden llamar funciones puras, y así sucesivamente). Javac obviamente solo sabe sobre Java, y solo sabe sobre el código que puede ver. Entonces, si su función llama a una función en otra unidad de compilación, no puede saber si es pura o no. Si llama a una función escrita en otro idioma, no puede saberlo. Si llama a una función en una biblioteca que aún podría no estar instalada, no puede saberlo. Y así.
Esto solo funciona cuando tiene un análisis de todo el programa, cuando todo el programa está escrito en el mismo idioma y todo se compila de una vez. No puedes usar ninguna biblioteca.
Problema # 3: programación . Una vez que haya descubierto qué partes son puras, todavía tiene que programarlas para separar los hilos. O no. Iniciar y detener subprocesos es muy costoso (especialmente en Java). Incluso si mantiene un grupo de subprocesos y no los inicia o detiene, el cambio de contexto de subprocesos también es costoso. Debe asegurarse de que el cálculo se ejecutará significativamente más tiempo que el tiempo que lleva programar y cambiar de contexto, de lo contrario perderá rendimiento, no lo ganará.
Como probablemente ya haya adivinado, averiguar cuánto tiempo llevará un cálculo es imposible en el caso general (ni siquiera podemos determinar si tomará una cantidad de tiempo finita, y mucho menos cuánto tiempo) y difícil y costoso incluso en El caso especial.
Aparte: Javac y optimizaciones . Tenga en cuenta que la mayoría de las implementaciones de javac en realidad no realizan muchas optimizaciones. La implementación de javac de Oracle, por ejemplo, se basa en el motor de ejecución subyacente para realizar optimizaciones . Esto lleva a otro conjunto de problemas: digamos, javac decidió que una función particular es pura y es lo suficientemente costosa, por lo que la compila para que se ejecute en un hilo diferente. Luego, aparece el optimizador de la plataforma (por ejemplo, el compilador HotSpot C2 JIT) y optimiza toda la función. Ahora, tienes un hilo vacío que no hace nada. O, imagine, nuevamente, javac decide programar una función en un hilo diferente, y el optimizador de plataforma podría optimizarlo completamente, excepto que no puede realizar la alineación a través de los límites del hilo, por lo que ahora se ejecuta innecesariamente una función que podría optimizarse completamente.
Entonces, hacer algo como esto solo tiene sentido si tiene un único compilador que realiza la mayoría de las optimizaciones de una sola vez, para que el compilador conozca y pueda explotar todas las diferentes optimizaciones en diferentes niveles y sus interacciones entre sí.
Tenga en cuenta que, por ejemplo, el compilador JIT HotSpot C2 realidad hace realizar alguna auto-vectorización, que es también una forma de auto-paralelización.
fuente
definition
, el uso de un dispardefinition
depurity
probablemente sea oscuroStringBuilder
) no tiene sentido, así que lo descartaría y simplemente asumiría que el OP escribe javac pero significa Hotspot. Su problema # 2 es una buena razón para no optimizar nada en javac.La respuesta votada no pudo notar una cosa. La comunicación síncrona entre hilos es extremadamente costosa. Si la función es capaz de ejecutarse a una velocidad de muchos millones de llamadas por segundo, en realidad le duele más paralelizarla en lugar de dejarla como está.
Desafortunadamente, la forma más rápida de comunicación síncrona entre subprocesos, utilizando bucles ocupados con variables atómicas, es ineficiente energéticamente. Si tiene que recurrir al uso de variables de condición para ahorrar energía, el rendimiento de su comunicación entre subprocesos se ve afectado.
Por lo tanto, el compilador no solo necesita determinar si una función es pura, sino que también debe estimar el tiempo de ejecución de la función para ver si la paralelización es una ganancia neta. Además, necesitaría elegir entre bucles ocupados utilizando variables atómicas o variables de condición. Y necesitaría crear hilos a sus espaldas.
Si crea los hilos dinámicamente, es aún más lento que usar variables de condición. Entonces, el compilador necesitaría configurar una cierta cantidad de hilos que ya se están ejecutando.
Entonces, la respuesta a su pregunta es no , los compiladores no son lo suficientemente "inteligentes" como para paralelizar automáticamente las funciones puras, especialmente en el mundo de Java. ¡Son inteligentes al no paralelizarlos automáticamente!
fuente