CPUs sind zu einem gewissen Grad unter Berücksichtigung der Software konzipiert, die die Leute implizit oder explizit dafür schreiben.
Wenn Sie sich den Entwurf von Befehlssatzarchitekturen ansehen, scheint es mir, dass sie sehr "zwingend" sind, in dem Sinne, dass jeder Befehl einen Befehl im imperativen Stil codiert. Es scheint mir auch, dass sich die aktuellen Befehlssatzarchitekturen teilweise basierend auf der Art des Code-Programmierers entwickelt haben.
Wenn man eine CPU von Grund auf neu entwerfen würde, wenn man wüsste, dass sie nur Programme ausführen würde, die in einem funktionalen Programmierstil geschrieben sind, wie würde diese CPU anders als die existierenden CPUs aufgebaut sein?
Antworten:
Eigentlich wurde es gemacht: https://en.wikipedia.org/wiki/Lisp_machine
Ein Aspekt im CPU-Design für FP ist die Speicherbereinigung. GC ist sehr wichtig für funktionale Sprachen. Gängige Implementierungen erfordern, dass der GC zwischen Zeigern und Nicht-Zeiger-Daten unterscheiden kann. Tatsächlich bedeutet das, ein zusätzliches Bit in Ihren Daten zu speichern. Dies ist der Grund, warum beispielsweise OCaml-Ganzzahlen auf 32-Bit-Architekturen nur 31-Bit und auf 64-Bit-Architekturen nur 63-Bit sind. Ganzzahlige Arithmetik beinhaltet dann umständliche zusätzliche Verschiebevorgänge. Andere Sprachen (oder andere OCaml-Datentypen) verschwenden möglicherweise ganze Maschinenwörter für dieses zusätzliche Bit und verwenden daher 128 Bit für 64-Bit-Ganzzahlen. Eine CPU, die von Haus aus auf GC ausgelegt ist, verfügt möglicherweise über einen 65-Bit-Datenbus, jedoch über 64-Bit-Arithmetik.
Allerdings haben viele nicht funktionierende Sprachen auch eine Garbage Collection und würden daher von entsprechenden Architekturen profitieren.
Eine andere Sache, die mir einfällt, ist die Tatsache, dass die Speichernutzung von FP in der Regel viel stärker gestreut ist als die von imperativen Programmen. Hauptsächlich, weil es weniger natürlich ist, Arrays zu verwenden. Infolgedessen profitieren diese Programme weniger davon, zusammenhängende Speicherbereiche zwischenzuspeichern. Eine FP-CPU kann also unterschiedliche Caching-Strategien verwenden.
quelle
Es würde entweder nichts ändern oder massive Paralleleinstellungen wie bei Reduceron und seinem Nachfolger PilGRIM 1 mit einem riesigen Stapel nutzen.
Die Aussage, dass sich nichts ändern würde, erscheint zunächst kühn, aber da die CPU sequenziell ist, gibt es einen Übersetzungsprozess (Kompilierung), der die verfügbare Hardware aus Effizienzgründen in vollem Umfang nutzt. Sollte es eine andere Architektur geben, wären einige Vorgänge schneller, andere benötigen Hacking-Tricks, um sie zu beschleunigen.
Architektur, die einen Unterschied machen würde, würde eine schnellere Ausführung von Kartenoperationen und Listen erfordern (nicht die gesamte Story, aber es reicht aus, um den Effekt zu zeigen). Es gibt keine Möglichkeit, sich dynamisch ändernde Hardware zu erstellen, um Listen nativ auszuführen, sodass diese in einem zusammenhängenden Speicher abgelegt werden. Wir bleiben bei der Array-Darstellung irgendeiner Form. Damit die Karte in einer nicht sequentiellen Einstellung ausgeführt werden kann, kehren wir zu Reduceron zurück. So effektiv eine zentrale Verarbeitung für aufeinanderfolgende Anweisungen und Unterstützung für die parallele Verarbeitung.
Was möglicherweise anders ist, ist die Möglichkeit, mehrere Funktionen zu laden und auszuführen, ohne dass Frames jonglieren müssen. Wenn Sie jedoch mehrere Einheiten für Funktionen hinzufügen, wird der Zugriff auf den Speicher beeinträchtigt.
Zur Antwort von kne hinzufügen, wäre es von Vorteil, den GC als Coprozessor zu betreiben, es wäre eine sehr nette Funktion.
1: PilGRIM ist in Boeijink A., Hölzenspies PKF, Kuper J. (2011) beschrieben. In: Hage J., Morazán MT (Hrsg.) Implementierung und Anwendung funktionaler Sprachen. IFL 2010. Lecture Notes in Computer Science, Bd. 6647. Springer, Berlin, Heidelberg .
quelle
Zunächst ein kleiner Witz: Da das Ausführen eines 100% igen Funktionsprogramms niemals etwas Nützliches bewirken kann, würde es genügen, nur eine NOP-Anweisung zu haben. (Ich öffne das für Flammenkriege).
Es wird also einige wichtige Anweisungen für IO und die übliche Unterstützung für die imperative Programmierung geben müssen.
Ansonsten hängt es teilweise von der tatsächlich verwendeten Sprache ab. Die beiden, die ich im Kopf habe, sind Haskell und Erlang.
Ich würde glauben, dass Haskell von der Unterstützung für Listen und Karten profitieren könnte. Eine Liste könnte durch bestimmte Hardware-Speicherzuordnungen unterstützt werden, wodurch die verknüpfte Liste in einen aufeinanderfolgenden Satz von Adressen umgewandelt wird. Das erste Element könnte an der Adresse n sein, das zweite an der Adresse n + 1 und so weiter. Um das erste Element aus der Liste zu entfernen, ändern Sie einfach den Zeiger n. Wenn Sie schließlich den Zeiger n löschen, kann der gesamte Speicher freigegeben werden. Maps können als assoziative Arrays unterstützt werden. Geben Sie den Suchwert ein, und das Speichersystem gibt den Artikel zurück. Keine Notwendigkeit für iterative Suchen.
Erlang wiederum könnte von der Unterstützung von Nachrichten / Prozessen und der Endrekursion mit vollem Status profitieren. Nachrichten und Prozesse können auf verschiedene Arten unterstützt werden, beispielsweise durch eine extrem große Anzahl von Prozessorkernen. Die Schwanzrekursion könnte durch eine Speichersteuerung verbessert werden, die weiß, dass der Zustand viel schneller kopiert werden kann, wobei möglicherweise keine großen Datenmengen kopiert werden, sondern lediglich die Zeiger des Speichersystems geändert werden.
quelle