Les fonctions pures sont connues pour faciliter la parellisation. En quoi la programmation fonctionnelle la rend-elle intrinsèquement adaptée à l'exécution parallèle?
Les compilateurs tels que Javac sont-ils assez intelligents pour détecter quand une méthode est une fonction pure? On peut toujours implémenter des classes qui implémentent des interfaces fonctionnelles telles que Function , mais ont des effets secondaires.
functional-programming
Naveen
la source
la source
NullPointerException
. Les avantages des optimisations basées sur cela sont également probablement assez faibles pour les applications Java typiques.Réponses:
Il ne s'agit pas de "suffisamment intelligent". C'est ce qu'on appelle l' analyse de pureté et cela est impossible dans le cas général: cela équivaut à résoudre le problème de l'arrêt.
Maintenant, bien sûr, les optimiseurs font des choses prouvablement impossibles tout le temps, "prouvablement impossible dans le cas général" ne signifie pas qu'il ne fonctionne jamais, cela signifie seulement qu'il ne peut pas fonctionner dans tous les cas. Donc, il existe en fait des algorithmes pour vérifier si une fonction est pure ou non, c'est juste que le plus souvent le résultat sera "je ne sais pas", ce qui signifie que pour des raisons de sécurité et d'exactitude, vous devez supposer que cette fonction particulière pourrait être impure.
Et même dans les cas où elle exerce ses travaux, les algorithmes sont complexes et coûteux.
C'est donc le problème n ° 1: cela ne fonctionne que pour des cas spéciaux .
Problème n ° 2: bibliothèques . Pour qu'une fonction soit pure, elle ne peut appeler que des fonctions pures (et ces fonctions ne peuvent appeler que des fonctions pures, et ainsi de suite). Javac ne connaît évidemment que Java et ne connaît que le code qu'il peut voir. Donc, si votre fonction appelle une fonction dans une autre unité de compilation, vous ne pouvez pas savoir si elle est pure ou non. S'il appelle une fonction écrite dans une autre langue, vous ne pouvez pas le savoir. S'il appelle une fonction dans une bibliothèque qui n'est peut-être même pas encore installée, vous ne pouvez pas le savoir. Etc.
Cela ne fonctionne que lorsque vous avez une analyse complète du programme, lorsque le programme entier est écrit dans la même langue et que tout est compilé en une seule fois. Vous ne pouvez utiliser aucune bibliothèque.
Problème n ° 3: planification . Une fois que vous avez déterminé quelles parties sont pures, vous devez toujours les planifier sur des threads séparés. Ou pas. Le démarrage et l'arrêt des threads sont très coûteux (en particulier en Java). Même si vous conservez un pool de threads et ne les démarrez pas ou ne les arrêtez pas, le changement de contexte de thread est également coûteux. Vous devez être sûr que le calcul s'exécutera beaucoup plus longtemps que le temps nécessaire pour planifier et changer de contexte, sinon vous perdrez les performances, pas les gagnerez.
Comme vous l'avez probablement deviné maintenant, déterminer combien de temps un calcul prendra est impossible dans le cas général (nous ne pouvons même pas déterminer si cela prendra un temps fini, sans parler du temps) et difficile et coûteux même dans le cas particulier.
A côté: Javac et optimisations . Notez que la plupart des implémentations de javac n'effectuent pas réellement beaucoup d'optimisations. La mise en œuvre d'Oracle de javac, par exemple, repose sur le moteur d'exécution sous-jacent pour effectuer des optimisations . Cela conduit à un autre ensemble de problèmes: disons, javac a décidé qu'une fonction particulière est pure et elle est assez chère, et donc elle la compile pour être exécutée sur un thread différent. Ensuite, l'optimiseur de la plate-forme (par exemple, le compilateur HotSpot C2 JIT) arrive et optimise l'ensemble de la fonction. Maintenant, vous avez un fil vide qui ne fait rien. Ou, imaginez, encore une fois, javac décide de planifier une fonction sur un autre thread, et l'optimiseur de plateforme pourrait optimisez-le complètement, sauf qu'il ne peut pas effectuer d'inlining au-delà des limites de thread, et donc une fonction qui pourrait être complètement optimisée est maintenant exécutée inutilement.
Donc, faire quelque chose comme ça n'a de sens que si vous avez un seul compilateur effectuant la plupart des optimisations en une seule fois, afin que le compilateur connaisse et puisse exploiter toutes les différentes optimisations à différents niveaux et leurs interactions les unes avec les autres.
Notez que, par exemple, le compilateur JIT HotSpot C2 effectivement fait effectuer une auto-vectorisation, qui est aussi une forme d'auto-parallélisation.
la source
definition
, l'utilisation d'un disparatedefinition
depurity
est probablement obscureStringBuilder
) est un non-sens, donc je le rejetterais et supposerais simplement, l'OP écrit javac mais signifie Hotspot. Votre problème # 2 est une très bonne raison contre l'optimisation de quoi que ce soit dans javac.La réponse votée n'a pas noté une chose. La communication synchrone entre les threads est extrêmement coûteuse. Si la fonction est capable d'être exécutée à un rythme de plusieurs millions d'appels par seconde, cela vous fait plus de mal de la paralléliser plutôt que de la laisser telle quelle.
La forme la plus rapide de communication synchrone entre threads, utilisant des boucles occupées avec des variables atomiques, est malheureusement peu énergivore. Si vous devez recourir à des variables de condition pour économiser de l'énergie, les performances de votre communication inter-threads en souffrent.
Ainsi, le compilateur n'a pas seulement besoin de déterminer si une fonction est pure, il devrait également estimer le temps d'exécution de la fonction pour voir si la parallélisation est un gain net. En outre, il devrait choisir entre des boucles occupées utilisant des variables atomiques ou des variables de condition. Et il faudrait créer des fils derrière votre dos.
Si vous créez les threads dynamiquement, c'est encore plus lent que d'utiliser des variables de condition. Ainsi, le compilateur devra configurer un certain nombre de threads déjà en cours d'exécution.
Donc, la réponse à votre question est non , les compilateurs ne sont pas assez "intelligents" pour auto-paralléliser des fonctions pures, en particulier dans le monde Java. Ils sont intelligents en ne les parallélisant pas automatiquement!
la source