Hasta cierto punto, las CPU están diseñadas teniendo en cuenta el software que la gente escribirá para él, implícita o explícitamente.
Me parece que si nos fijamos en el diseño de arquitecturas de conjuntos de instrucciones, son muy "imperativas", en el sentido de que cada instrucción codifica un comando de estilo imperativo. También me parece que las arquitecturas actuales del conjunto de instrucciones han evolucionado en parte según el tipo de código que producen los programadores.
Si uno diseñara una CPU desde cero, sabiendo que solo ejecutaría programas escritos en un estilo de programación funcional, ¿cómo se diseñaría esa CPU de manera diferente a las CPU existentes?
computer-architecture
functional-programming
usuario56834
fuente
fuente
Respuestas:
En realidad, se ha hecho: https://en.wikipedia.org/wiki/Lisp_machine
Un aspecto en el diseño de CPU para FP es la recolección de basura. GC es muy importante para los lenguajes funcionales. Las implementaciones comunes requieren que el GC pueda distinguir entre punteros y datos sin puntero. Efectivamente, eso significa almacenar un bit extra a lo largo de sus datos. Esta es la razón por la que, por ejemplo, los enteros OCaml son solo 31 bits en arquitecturas de 32 bits y 63 bits en arquitecturas de 64 bits. La aritmética de enteros implica operaciones de desplazamiento extra incómodas. Otros idiomas (u otros tipos de datos OCaml) pueden desperdiciar palabras completas de la máquina para ese bit adicional, por lo tanto, usan 128 bits para enteros de 64 bits. Una CPU que está diseñada de forma nativa para GC podría tener un bus de datos de 65 bits pero una aritmética de 64 bits.
Dicho esto, muchos lenguajes no funcionales también tienen recolección de basura y, por lo tanto, se beneficiarían de las arquitecturas respectivas.
Otra cosa que viene a la mente es que el uso de memoria de FP generalmente está mucho más disperso que el de los programas imperativos. Principalmente porque es menos natural usar matrices. En consecuencia, estos programas se benefician menos de almacenar en caché fragmentos de memoria contiguos. Entonces, una CPU FP podría usar diferentes estrategias de almacenamiento en caché.
fuente
No cambiaría nada o aprovecharía la configuración paralela masiva como en Reduceron y su sucesor PilGRIM 1 con una gran pila.
La declaración de que no cambiaría nada parece audaz al principio, pero dado que la CPU es secuencial, existe un proceso de traducción (compilación) que utiliza el hardware disponible en su extensión para mayor eficiencia. Debería haber otra arquitectura, algunas operaciones serían más rápidas, algunas necesitarían trucos de piratería para acelerarlo.
La arquitectura que marcaría la diferencia requeriría la operación del mapa y las listas para ejecutarse más rápido (no toda la historia, pero es suficiente para mostrar el efecto). No existe la posibilidad de crear hardware de cambio dinámico para ejecutar listas de forma nativa, por lo que se almacena en la memoria de ustedes. Nos atenemos a la representación de matriz de alguna forma. Para el mapa, para ejecutar en una configuración no secuencial, volvemos a Reduceron. Así que efectivamente un procesamiento central para instrucciones consecutivas y soporte para procesamiento paralelo.
Lo que podría ser diferente es la posibilidad de cargar múltiples funciones y ejecutarlas sin malabares de cuadros, pero agregar múltiples unidades para funciones crearía un desastre con el acceso a la memoria.
Además de la respuesta de rodillas, el GC sería beneficioso para funcionar como coprocesador, sería una característica muy buena.
1: PilGRIM se describe adecuadamente en Boeijink A., Hölzenspies PKF, Kuper J. (2011) Presentación de PilGRIM: un procesador para ejecutar lenguajes funcionales perezosos. En: Hage J., Morazán MT (eds) Implementación y aplicación de lenguajes funcionales. IFL 2010. Lecture Notes in Computer Science, vol 6647. Springer, Berlín, Heidelberg .
fuente
Primero un poco de broma: como ejecutar un programa 100% funcional nunca puede hacer nada útil, sería suficiente tener solo una instrucción NOP. (Abro esto para las guerras de llamas).
Por lo tanto, deberá haber algunas instrucciones imperativas para IO y el soporte habitual para la programación imperativa.
De lo contrario, depende en parte del idioma real utilizado. Los dos en mi cabeza son Haskell y Erlang.
Creo que Haskell podría beneficiarse del soporte para listas y mapas. Una lista podría estar respaldada por asignaciones específicas de memoria de hardware, convirtiendo la lista vinculada en un conjunto consecutivo de direcciones. El primer elemento puede estar en la dirección n, el segundo en la dirección n + 1 y así sucesivamente. Para eliminar el primer elemento de la lista, simplemente cambie el puntero n. Finalmente, cuando elimina el puntero n, se puede liberar toda la memoria. Los mapas podrían ser compatibles como matrices asociativas: ingrese el valor de búsqueda y el sistema de memoria devolverá el elemento. No es necesario realizar búsquedas iterativas.
Erlang a su vez podría beneficiarse del soporte de mensajes / procesos y la recursividad de cola con estado completo. Los mensajes y procesos pueden ser soportados de varias maneras, un ejemplo podría ser tener una cantidad extremadamente grande de núcleos de procesamiento. La recursividad de la cola podría mejorarse mediante un controlador de memoria que sepa que permite copiar el estado mucho más rápido, tal vez no copiando grandes cantidades de datos, sino simplemente modificando los punteros del sistema de memoria.
fuente