In che modo una CPU progettata esclusivamente per la programmazione funzionale sarebbe diversa?


14

Le CPU sono progettate in una certa misura tenendo presente il software che le persone scriveranno per questo, implicitamente o esplicitamente.

Mi sembra che se si guarda alla progettazione di architetture di insiemi di istruzioni, sono molto "imperativi", nel senso che ogni istruzione codifica un comando di stile imperativo. Mi sembra anche che le architetture del set di istruzioni attuali si siano parzialmente evolute in base al tipo di codice prodotto dai programmatori.

Se si progettasse una CPU da zero, sapendo che avrebbe sempre eseguito programmi scritti in uno stile di programmazione funzionale, come sarebbe quella CPU progettata in modo diverso da quella esistente?


9
John Backus nel suo "La programmazione può essere liberata dallo stile di von Neumann?" menziona alcune di queste opere (sezione 15).
Dmitri Urbanowicz,

Cerca le macchine per la riduzione (grafica) o visita la tua biblioteca di ricerca locale nella speranza di trovare una copia del libro esaurito The Organization of Reduction, Data Flow e Control Flow Systems (MIT Press, 1992) di W. Kluge .
Kai,

2
Anche il libro di Koopman An Architecture for Combinator Graph Reduction (AP, 1990). Anche esaminare le macchine Lisp è probabilmente utile. en.wikipedia.org/wiki/Lisp_machine
Pseudonimo

Penso che fondamentalmente le nostre macchine saranno sempre imperative mentre si eseguono nel tempo, mutando il loro stato.
orlp

Un paio di utili funzionalità della CPU sarebbero il supporto nativo per i thunk e un salto più efficiente. Inoltre, la CPU potrebbe essere in grado di prendere alcune scorciatoie sapendo che determinate posizioni nella memoria non verranno sovrascritte in un determinato ambito e la CPU non avrebbe bisogno di mantenere uno stack allo stesso modo dei linguaggi basati su stack.
Ripristina Monica il

Risposte:


2

In realtà, è stato fatto: https://en.wikipedia.org/wiki/Lisp_machine

Un aspetto nella progettazione della CPU per FP è la garbage collection. GC è molto importante per i linguaggi funzionali. Le implementazioni comuni richiedono che il GC sia in grado di distinguere tra puntatori e dati non puntatori. In effetti, ciò significa archiviare un bit in più lungo i tuoi dati. Questo è il motivo per cui, ad esempio, gli interi OCaml sono solo 31 bit su architetture a 32 bit e 63 bit su architetture a 64 bit. L'aritmetica intera comporta quindi operazioni di cambio extra scomode. Altri linguaggi (o altri tipi di dati OCaml) potrebbero sprecare intere parole macchina per quel bit in più, usando quindi 128 bit per numeri interi a 64 bit. Una CPU progettata nativamente per GC potrebbe avere un bus dati a 65 bit ma aritmetica a 64 bit.

Detto questo, molti linguaggi non funzionali hanno anche la raccolta dei rifiuti e trarrebbero quindi vantaggio dalle rispettive architetture.

Un'altra cosa che viene in mente è che l'utilizzo della memoria di FP in genere è molto più disperso di quello dei programmi imperativi. Principalmente perché è meno naturale usare array. Di conseguenza, questi programmi traggono meno profitto dalla memorizzazione nella cache di blocchi di memoria contigui. Pertanto, una CPU FP potrebbe utilizzare diverse strategie di memorizzazione nella cache.


1

Non cambierebbe nulla o sfrutterebbe un'imponente impostazione parallela come in Reduceron e il suo successore PilGRIM 1 con uno stack enorme.

L'affermazione che all'inizio non cambierebbe nulla sembra in grassetto, ma poiché la CPU è sequenziale, esiste un processo di traduzione (compilazione) che utilizza l'hardware disponibile per la sua estensione per efficienza. Ci sarà un'altra architettura, alcune operazioni sarebbero più veloci, alcune avrebbero bisogno di trucchi per accelerare.

L'architettura che farebbe la differenza richiederebbe che le mappe e gli elenchi funzionino più velocemente (non l'intera storia, ma è sufficiente per mostrare l'effetto). Non è possibile creare hardware che cambia in modo dinamico per eseguire nativamente elenchi, quindi questi vengono archiviati nella memoria contigua. Ci atteniamo alla rappresentazione in serie di una qualche forma. Per la mappa, per l'esecuzione in un'impostazione non sequenziale, torniamo a Reduceron. Così efficacemente un'elaborazione centrale per istruzioni consecutive e supporto per l'elaborazione parallela.

Ciò che potrebbe essere diverso è la possibilità di caricare più funzioni ed eseguirle senza manipolare i frame, ma l'aggiunta di più unità per le funzioni creerebbe un disastro con l'accesso alla memoria.

Aggiungendo alla risposta di kne, il GC sarebbe utile per funzionare come coprocessore, sarebbe una funzionalità molto accurata.

1: PilGRIM è correttamente descritto in Boeijink A., Hölzenspies PKF, Kuper J. (2011) Presentazione di PilGRIM: un processore per l'esecuzione di linguaggi funzionali pigri. In: Hage J., Morazán MT (a cura di) Implementazione e applicazione di linguaggi funzionali. IFL 2010. Appunti di lezione di informatica, vol 6647. Springer, Berlino, Heidelberg .


"Non è possibile rendere nativa la ricorsione". Potresti spiegare perché questo è? All'inizio mi sembra sorprendente.
user56834

Inoltre, il riduttore su qualcosa che potrebbe essere una CPU difficile, invece di funzionare su un FPGA?
user56834

Mio male, intendevo ricorsione nativa , ma è assolutamente irrilevante. Devo rivedere un po '.
Evil

0

Prima di tutto uno scherzo: poiché l'esecuzione di un programma funzionale al 100% non può mai fare nulla di utile, basterebbe avere solo un'istruzione NOP. (Lo apro per le guerre di fiamma).

Quindi, ci saranno alcune istruzioni imperative per l'IO e il solito supporto per la programmazione imperativa.

Altrimenti dipende in parte dalla lingua utilizzata. I due in testa sono Haskell ed Erlang.

Credo che Haskell possa beneficiare del supporto per elenchi e mappe. Un elenco potrebbe essere supportato da specifici mapping di memoria hardware, trasformando l'elenco collegato in un set consecutivo di indirizzi. Il primo elemento potrebbe essere sull'indirizzo n, il secondo sull'indirizzo n + 1 e così via. Per rimuovere il primo elemento dall'elenco, è sufficiente modificare il puntatore n. Infine, quando si elimina il puntatore n, è possibile liberare tutta la memoria. Le mappe potrebbero essere supportate come array associativi: immettere il valore di ricerca e il sistema di memoria restituisce l'elemento. Non sono necessarie ricerche iterative.

A sua volta, Erlang potrebbe beneficiare del supporto di messaggi / processi e ricorsività della coda con il pieno stato. Messaggi e processi potrebbero essere supportati in vari modi, un esempio potrebbe essere quello di avere una quantità estremamente grande di core di elaborazione. La ricorsione della coda potrebbe essere migliorata da un controller di memoria sapendo che è possibile copiare lo stato molto più velocemente, forse non copiando grossi blocchi di dati ma semplicemente modificando i puntatori del sistema di memoria.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.