As linguagens de programação funcionais têm mais oportunidade de otimizar o tempo de compilação?

10

Eu estava lendo o livro "Programação Funcional para o Mundo Real". Tudo começou com a comparação entre linguagens de programação imperativas e funcionais. E afirmou como 'valores' e 'expressões' na programação funcional são diferentes de 'variáveis' e 'funções' da programação imperativa. A partir da discussão, desenvolvi uma ideia que -

As linguagens de programação funcional têm mais oportunidade de otimizar o tempo de compilação do que suas contrapartes imperativas.

É verdade?

Gulshan
fonte

Respostas:

16

Linguagens de programação funcional fazem muito mais otimização do tempo de compilação. Uma das razões é a pureza - a simultaneidade é trivial porque não há estado. Portanto, o compilador pode pegar duas ramificações e concurrenciá-las facilmente, sem alterar o comportamento do programa.

Ao mesmo tempo, qualquer coisa que possa ser calculada sem estado (ou seja, qualquer coisa não monádica em haskell) pode ser calculada antecipadamente pelo compilador, mas esses cálculos podem ser caros e, portanto, provavelmente são feitos apenas parcialmente.

Além disso, qualquer coisa que não seja necessária computacionalmente pode ser completamente otimizada para fora do programa.

alternativo
fonte
11
+1: A programação livre de efeitos colaterais é muito, muito fácil de otimizar.
S.Lott
@mathepic: na verdade, a paralelização (em Haskell) ocorre no tempo de compilação e no tempo de execução. O tempo de compilação decide se vale a pena criar um shard (thread seed) e o tempo de execução processa shards o máximo possível, dependendo do número de threads que você especificou. Se você especificar apenas um único encadeamento, os shards serão criados, mas serão processados ​​um após o outro.
26511 Matthieu M.
2
@mathepic: outro uso da pureza -> preguiça e ausência de computação. Se você puder provar que o valor não é necessário, retire completamente o cálculo. Se for necessário, use um thunk preguiçoso. Se você sabe que será necessário, calcule-o sempre em frente (para evitar a sobrecarga preguiçosa). Pureza é (apenas) amazing :)
Matthieu M.
@Matthieu good point
alternativa
11
@Masse Até a mônada de IO é pura. É apenas que o resultado da "execução" da mônada de IO não é.
alternativa
7

O fato de haver, em princípio, mais possibilidades de otimização do tempo de compilação para linguagens funcionais do que para suas contrapartes imperativas é provavelmente verdade.

Mais interessante, porém, é se elas são realmente implementadas nos compiladores atuais e qual a relevância dessas otimizações na prática (ou seja, desempenho final do código idiomático da 'vida real (TM)') em um ambiente de produção, com configurações previsíveis a priori do compilador.

por exemplo, os envios de Haskell para o famoso jogo Computer Language Benchmarks (por pior que seja - mas não é como se houvesse - no momento - algo significativamente melhor por aí) dão a impressão de que uma quantidade significativa de tempo foi gasta em otimizações manuais, que confrontadas com a alegação de "possíveis otimizações do compilador devido a insert some property about FP languages here", fazem parecer que as otimizações são (atualmente pelo menos) mais uma possibilidade teórica do que uma realidade relevante.

Eu ficaria feliz em estar errado neste ponto.

Alexander Battisti
fonte
11
O motivo das otimizações manuais no Haskell tem mais a ver com o fato de que certas operações "diretas" consomem um pouco de tempo (de uma perspectiva de complexidade) no Haskell. Por exemplo, suponha que você queira obter o último elemento de uma lista. Em Java, essa é uma operação bastante simples; em Haskell, a abordagem ingênua exige que você caminhe por toda a lista até chegar ao fim (devido à natureza preguiçosa das listas), tornando-a uma operação O (n). Aquela (parcialmente), onde otimizações manuais vêm em.
mipadi
2
Estou certo de que existem razões válidas para as otimizações feitas manualmente por Haskell, mas elas são necessárias para operações "diretas", dando a impressão de que as maiores oportunidades para otimizar o código (atualmente) são relevantes apenas na teoria.
21811 Alexander Battisti
6
É mais a diferença entre otimizar algoritmos e otimizar o código gerado. Tomemos um exemplo C: se estou escrevendo um algoritmo de pesquisa, posso escrever um algoritmo ingênuo que simplesmente percorre uma lista ou posso usar a pesquisa binária. Nos dois casos, o compilador otimizará meu código, mas não transformará um algoritmo de pesquisa ingênuo em uma pesquisa binária. Muitas das otimizações manuais no código Haskell têm mais a ver com a otimização dos algoritmos, do que com a otimização do código gerado.
Mipadi
2
@mipadi, mas parece que a versão direta do algoritmo Haskell não é tão boa quanto a versão direta dos algoritmos de outras linguagens. (Eu suspeito que devido à incompatibilidade entre o modelo funcional e a arquitetura do computador). Mesmo que seja bom em gerar código, não é suficiente para superar esse problema.
Winston Ewert
>> por pior que seja << - Você sabe se é ruim ou não? O que você quer dizer com sugerir que é "ruim"? Por favor, seja específico.
Igouy 15/05/12
2

No estilo funcional, o fluxo de valores através de um programa é muito, muito visível (para o compilador e o programador). Isso dá ao compilador muita margem de manobra para decidir onde armazenar valores, quanto tempo mantê-los por perto e assim por diante.

Em uma linguagem imperativa, o compilador promete ao programador um modelo em que a maioria das variáveis ​​corresponde a locais reais na memória que permanecem por um tempo definido. Potencialmente, qualquer instrução pode ler (ou gravar em!) Qualquer um desses locais; portanto, o compilador pode substituir apenas os locais de memória pela alocação de registros, mesclar duas variáveis ​​em um único local de armazenamento ou executar otimizações semelhantes depois de realizar uma análise cuidadosa de onde caso contrário, no programa essa variável pode ser referenciada.

Agora, existem duas advertências:

  • A comunidade da linguagem de programação gastou (desperdiçou?) Muito esforço nos últimos cinquenta anos no desenvolvimento de maneiras inteligentes de fazer essa análise. Existem truques bem conhecidos, como coloração de registros e assim por diante, para que alguns dos casos mais fáceis sejam executados na maioria das vezes; mas isso cria compiladores grandes e lentos e uma troca constante da complexidade da compilação pela qualidade do código resultante
  • Ao mesmo tempo, a maioria das linguagens de programação funcionais também não é puramente funcional; muitas das coisas que os programas realmente precisam fazer, como responder a E / S, são inerentemente não funcionais; portanto, nenhum compilador pode ficar completamente livre desses truques e nenhuma linguagem os evita completamente - até o Haskell, que é um pouco demais. puro para o meu gosto (Sua milhagem pode variar) pode apenas controlar e isolar as partes não funcionais do seu código, não evitá-las por completo.

Mas, para responder à pergunta geral, sim, um paradigma funcional oferece ao compilador muita liberdade para otimizar que ele não possui em um cenário imperativo.

jimwise
fonte
Tudo em Haskell é funcional, é apenas que você mainé uma função de transformação de estado, e não algo que usa o próprio estado.
alternativa
Sim e não - conceitualmente, é bastante correto dizer que um programa Haskell é uma função pura sobre as interações do usuário com esse programa (e o estado do gerador de números aleatórios do sistema, a latência da rede hoje e qualquer outro inerentemente entradas não funcionais às quais o programa responde), mas na prática é uma distinção sem diferença.
precisa saber é o seguinte
@jimwise A transparência referencial é uma diferença muito grande.
alternativa
Exceto que você realmente não o possui, pelo menos para o objetivo discutido aqui. O ponto de operações como IO, ou geração de números aleatórios, é que eles devem retornar um valor diferente a cada vez que eles estão invocado com as mesmas entradas, e isso limita inerentemente raciocínio sobre eles funcionalmente, tanto para o programador ou o compilador.
jimwise
11
@mathepic Sim, é claro que você pode conceitualmente visualizar a aleatoriedade ou a entrada do usuário (ou latência da rede ou carga do sistema) como um fluxo infinito ou uma função de estado para estado, mas não é uma visão que se presta a um raciocínio útil sobre o comportamento do programa. você ou seu compilador - Bob Harper aborda bem esse assunto em um post recente em seu blog sobre o novo currículo de CS da CMU para programação funcional.
jimwise