En quoi un processeur conçu uniquement pour la programmation fonctionnelle serait-il différent?

14

Les CPU sont dans une certaine mesure conçus en tenant compte du logiciel que les gens vont écrire pour cela, implicitement ou explicitement.

Il me semble que si vous regardez la conception des architectures de jeux d'instructions, elles sont très "impératives", dans le sens où chaque instruction code une commande de style impérative. Il me semble également que les architectures actuelles des jeux d'instructions ont évolué en partie en fonction du type de code produit par les programmeurs.

Si l'on concevait un processeur à partir de zéro, sachant qu'il n'exécuterait que des programmes écrits dans un style de programmation fonctionnel, comment ce processeur serait-il conçu différemment des processeurs existants?

user56834
la source
9
John Backus dans son livre "La programmation peut-elle être libérée du style von Neumann?" mentionne peu de ces œuvres (section 15).
Dmitri Urbanowicz
Recherchez des machines de réduction (graphique) ou visitez votre bibliothèque de recherche locale en espérant trouver une copie du livre épuisé de W. Kluge, The Organization of Reduction, Data Flow, and Control Flow Systems (MIT Press, 1992).
Kai
2
Aussi le livre de Koopman An Architecture for Combinator Graph Reduction (AP, 1990). Explorer les machines Lisp vaut probablement aussi la peine. en.wikipedia.org/wiki/Lisp_machine
Pseudonyme
Je pense que fondamentalement, nos machines seront toujours impératives car elles s'exécutent au fil du temps, mutant leur état.
orlp
Quelques fonctionnalités CPU utiles seraient la prise en charge native des thunks et un saut plus efficace. De plus, le processeur pourrait être en mesure de prendre certains raccourcis sachant que certains emplacements en mémoire ne seront pas remplacés dans une certaine portée et le processeur n'aurait pas besoin de maintenir une pile de la même manière que dans les langages basés sur la pile.
Rétablir Monica le

Réponses:

3

En fait, cela a été fait: https://en.wikipedia.org/wiki/Lisp_machine

Un aspect de la conception du processeur pour FP est la collecte des ordures. GC est très important pour les langages fonctionnels. Les implémentations courantes nécessitent que le GC puisse distinguer les pointeurs des données non pointées. En effet, cela signifie stocker un bit supplémentaire le long de vos données. C'est la raison pour laquelle, par exemple, les entiers OCaml ne sont que de 31 bits sur les architectures 32 bits et de 63 bits sur les architectures 64 bits. L'arithmétique entière implique alors des opérations de décalage supplémentaires maladroites. D'autres langues (ou d'autres types de données OCaml) peuvent gaspiller des mots machine entiers pour ce bit supplémentaire, utilisant ainsi 128 bits pour des entiers 64 bits. Un processeur conçu nativement vers GC peut avoir un bus de données 65 bits mais une arithmétique 64 bits.

Cela dit, de nombreux langages non fonctionnels ont également un ramasse-miettes et bénéficieraient ainsi des architectures respectives.

Une autre chose qui me vient à l'esprit est que l'utilisation de la mémoire de FP est généralement beaucoup plus dispersée que celle des programmes impératifs. Principalement parce qu'il est moins naturel d'utiliser des tableaux. Par conséquent, ces programmes profitent moins de la mise en cache de morceaux de mémoire contigus. Ainsi, un CPU FP peut utiliser différentes stratégies de mise en cache.

genou
la source
1

Cela ne changerait rien ou exploiterait un réglage parallèle massif comme dans Reduceron et son successeur PilGRIM 1 avec une énorme pile.

La déclaration selon laquelle cela ne changerait rien semble audacieuse au premier abord, mais comme le processeur est séquentiel, il existe un processus de traduction (compilation) qui utilise le matériel disponible à son maximum pour plus d'efficacité. Y aura-t-il une autre architecture, certaines opérations seraient plus rapides, certaines nécessiteraient des astuces de piratage pour l'accélérer.

Une architecture qui ferait une différence nécessiterait un fonctionnement de la carte et des listes pour fonctionner plus rapidement (pas toute l'histoire, mais il suffit de montrer l'effet). Il n'y a aucune possibilité de créer du matériel à changement dynamique pour exécuter des listes en mode natif, de sorte que celles-ci soient stockées dans la mémoire contigüe. Nous nous en tenons à la représentation sous forme de tableau d'une certaine forme. Pour la carte, pour fonctionner dans un cadre non séquentiel - nous revenons à Reduceron. Donc, efficacement un traitement central pour les instructions consécutives et un support pour le traitement parallèle.

Ce qui pourrait être différent, c'est la possibilité de charger plusieurs fonctions et de les exécuter sans jongler avec les images - mais l'ajout de plusieurs unités pour les fonctions créerait un gâchis avec l'accès à la mémoire.

Ajoutant à la réponse de kne, le GC serait avantageux de fonctionner comme coprocesseur, ce serait une fonctionnalité très intéressante.

1: PilGRIM est correctement décrit dans Boeijink A., Hölzenspies PKF, Kuper J. (2011) Introducing the PilGRIM: A Processor for Executing Lazy Functional Languages. Dans: Hage J., Morazán MT (eds) Implementation and Application of Functional Languages. IFL 2010. Notes de cours en informatique, vol 6647. Springer, Berlin, Heidelberg .

Mal
la source
"Il n'y a aucune possibilité de rendre la récursivité native". Pourriez-vous expliquer pourquoi c'est? Cela me semble d'abord surprenant.
user56834
De plus, le réducteur est-il quelque chose qui pourrait être un processeur dur, au lieu de fonctionner sur un fpga?
user56834
Mon mauvais, je voulais dire récursivité native , mais elle est probablement hors de propos. Je dois réviser un peu.
Evil
0

D'abord une blague: comme exécuter un programme 100% fonctionnel ne peut jamais rien faire d'utile, il suffirait de n'avoir qu'une instruction NOP. (J'ouvre cela pour les guerres de flammes).

Ainsi, il faudra des instructions impératives pour les E / S et le support habituel pour la programmation impérative.

Sinon, cela dépend en partie de la langue réelle utilisée. Les deux en tête de mon esprit sont Haskell et Erlang.

Je pense que Haskell pourrait bénéficier du support des listes et des cartes. Une liste peut être prise en charge par des mappages de mémoire matérielle spécifiques, transformant la liste liée en un ensemble consécutif d'adresses. Le premier élément peut être à l'adresse n, le second à l'adresse n + 1 et ainsi de suite. Pour supprimer le premier élément de la liste, vous devez simplement changer le pointeur n. Enfin, lorsque vous supprimez le pointeur n, toute la mémoire peut être libérée. Les cartes peuvent être prises en charge sous forme de tableaux associatifs - entrez la valeur de recherche et le système de mémoire renvoie l'élément. Pas besoin de recherches itératives.

Erlang pourrait à son tour bénéficier de la prise en charge des messages / processus et de la récursivité de queue avec état complet. Les messages et les processus peuvent être pris en charge de différentes manières, un exemple pourrait être d'avoir une très grande quantité de cœurs de traitement. La récursivité de la queue pourrait être améliorée par un contrôleur de mémoire sachant que l'état pourrait être copié beaucoup plus rapidement, peut-être pas en copiant de gros morceaux de données mais en modifiant simplement les pointeurs du système de mémoire.

ghellquist
la source