Como uma CPU projetada exclusivamente para programação funcional seria diferente?


14

As CPUs são projetadas tendo em mente o software que as pessoas escreverão para ela, implícita ou explicitamente.

Parece-me que, se você examinar o design das arquiteturas de conjuntos de instruções, elas são muito "imperativas", no sentido de que cada instrução codifica um comando de estilo imperativo. Parece-me também que as arquiteturas atuais do conjunto de instruções evoluíram parcialmente com base no tipo de código que os programadores produzem.

Se alguém projetasse uma CPU do zero, sabendo que apenas executaria programas escritos em um estilo de programação funcional, como essa CPU seria projetada de maneira diferente das CPUs existentes?


9
John Backus, em "A programação pode ser libertada do estilo von Neumann?" menciona poucos desses trabalhos (seção 15).
Dmitri Urbanowicz

Procure máquinas de redução (de gráfico) ou visite sua biblioteca de pesquisa local na esperança de encontrar uma cópia do livro esgotado de W. Kluge, The Organization of Reduction, Data Flow, and Control Flow Systems (MIT Press, 1992).
Kai

2
Também o livro de Koopman, An Architecture for Combinator Graph Reduction (AP, 1990). Examinar as máquinas Lisp provavelmente também vale a pena. pt.wikipedia.org/wiki/Lisp_machine
Pseudônimo

Penso fundamentalmente que nossas máquinas sempre serão imperativas à medida que são executadas ao longo do tempo, mutando seu estado.
orlp 31/07/19

Alguns recursos úteis da CPU seriam suporte nativo para thunks e salto mais eficiente. Além disso, a CPU pode conseguir alguns atalhos, sabendo que determinados locais na memória não serão substituídos em um determinado escopo e a CPU não precisaria manter uma pilha da mesma maneira que em idiomas baseados em pilha.
Reintegrar Monica

Respostas:


2

Na verdade, isso foi feito: https://en.wikipedia.org/wiki/Lisp_machine

Um aspecto no design da CPU para FP é a coleta de lixo. O GC é muito importante para linguagens funcionais. Implementações comuns exigem que o GC possa distinguir entre dados de ponteiros e não-ponteiros. Efetivamente, isso significa armazenar um bit extra ao longo dos seus dados. Esse é o motivo pelo qual, por exemplo, números inteiros OCaml são apenas 31 bits em arquiteturas de 32 bits e 63 bits em arquiteturas de 64 bits. A aritmética de número inteiro envolve operações de deslocamento extra desajeitadas. Outros idiomas (ou outros tipos de dados OCaml) podem desperdiçar palavras inteiras da máquina para esse bit extra, usando 128 bits para números inteiros de 64 bits. Uma CPU projetada nativamente para o GC pode ter um barramento de dados de 65 bits, mas aritmética de 64 bits.

Dito isto, muitas linguagens não funcionais também têm coleta de lixo e, portanto, se beneficiariam das respectivas arquiteturas.

Outra coisa que vem à mente é que o uso de memória do FP normalmente é muito mais disperso do que o de programas imperativos. Principalmente porque é menos natural usar matrizes. Em conseqüência, esses programas lucram menos com o armazenamento em cache de pedaços contíguos de memória. Portanto, uma CPU FP pode usar diferentes estratégias de cache.


1

Isso não mudaria nada ou utilizaria uma configuração paralela massiva, como em Reduceron e seu sucessor PilGRIM 1, com uma pilha enorme.

A afirmação de que isso mudaria nada parece ousado no começo, mas como a CPU é seqüencial, existe um processo de conversão (compilação) que usa o hardware disponível para aumentar sua eficiência. Haverá outra arquitetura, algumas operações seriam mais rápidas, outras precisariam de truques de hackers para acelerar.

A arquitetura que faria a diferença exigiria que a operação e as listas do mapa funcionassem mais rapidamente (não a história toda, mas basta mostrar o efeito). Não há possibilidade de criar hardware dinâmico e dinâmico para executar listas de forma nativa; portanto, elas são armazenadas na memória contígua. Aderimos à representação de matriz de alguma forma. Para o mapa, para rodar em um cenário não sequencial - voltamos ao Reduceron. Tão eficazmente um processamento central para instruções consecutivas e suporte para processamento paralelo.

O que pode ser diferente é a possibilidade de carregar várias funções e executá-las sem malabarismo de quadros - mas adicionar várias unidades para funções criaria uma bagunça no acesso à memória.

Somando a resposta de kne, o GC seria benéfico para rodar como coprocessador, seria um recurso muito interessante.

1: O PilGRIM é descrito adequadamente em Boeijink A., Hölzenspies PKF, Kuper J. (2011) Apresentando o PilGRIM: um processador para a execução de linguagens funcionais preguiçosas. In: Hage J., Morazán MT (eds) Implementation and Application of Functional Languages. IFL 2010. Lecture Notes in Computer Science, vol. 6647. Springer, Berlim, Heidelberg .


"Não há possibilidade de tornar a recursão nativa". Você poderia explicar por que isso é? Parece surpreendente a princípio para mim.
User56834 03/08/19

Além disso, o reduceron é algo que poderia ser uma CPU difícil, em vez de rodar em um FPGA?
User56834 03/08/19

Meu mau, eu quis dizer recursão nativa , mas é provavelmente irrelevante. Eu tenho que revisar um pouco.
Mal

0

Primeiro, uma piada: como a execução de um programa 100% funcional nunca pode fazer algo útil, seria suficiente ter apenas uma instrução NOP. (Eu abro isso para guerras de chamas).

Portanto, será necessário haver algumas instruções imperativas para o IO e o suporte usual para a programação imperativa.

Caso contrário, depende em parte do idioma real usado. Os dois que estão na minha cabeça são Haskell e Erlang.

Eu acreditaria que Haskell poderia se beneficiar do suporte a listas e mapas. Uma lista pode ser suportada por mapeamentos de memória de hardware específicos, transformando a lista vinculada em um conjunto consecutivo de endereços. O primeiro elemento pode estar no endereço n, o segundo no endereço n + 1 e assim por diante. Para remover o primeiro elemento da lista, basta alterar o ponteiro n. Finalmente, quando você exclui o ponteiro n, toda a memória pode ser liberada. Os mapas podem ser suportados como matrizes associativas - digite o valor da pesquisa e o sistema de memória retorna o item. Não há necessidade de pesquisas iterativas.

Erlang, por sua vez, poderia se beneficiar do suporte de mensagens / processos e recursão final com estado completo. Mensagens e processos podem ser suportados de várias maneiras; um exemplo pode ser o de ter uma quantidade extremamente grande de núcleos de processamento. A recursão da cauda poderia ser melhorada por um controlador de memória sabendo que o estado pode ser copiado muito mais rápido, talvez não copiando grandes pedaços de dados, mas modificando os ponteiros do sistema de memória.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.