A programação funcional não permite programas mais rápidos, como regra geral. O que faz é para programação mais fácil e simultânea mais fácil . Existem duas chaves principais para isso:
- A evitação do estado mutável tende a reduzir o número de coisas que podem dar errado em um programa, e ainda mais em um programa concorrente.
- A evitação de primitivos de sincronização de memória compartilhada e baseada em bloqueio em favor de conceitos de nível superior tende a simplificar a sincronização entre encadeamentos de código.
Um excelente exemplo do ponto # 2 é que, em Haskell, temos uma clara distinção entre paralelismo determinístico vs. concorrência não determinística . Não há melhor explicação do que citando o excelente livro de Simon Marlow, Programação Paralela e Concorrente em Haskell ( as citações são de Capítulo 1 ):
A parallel program is one that uses a multiplicity of computational hardware (e.g., several processor cores) to perform a computation more quickly. The aim is to arrive at the answer earlier, by delegating different parts of the computation to different processors that execute at the same time.
By contrast, concurrency is a program-structuring technique in which there are multiple threads of control. Conceptually, the threads of control execute “at the same time”; that is, the user sees their effects interleaved. Whether they actually execute at the same time or not is an implementation detail; a concurrent program can execute on a single processor through interleaved execution or on multiple physical processors.
Além disso, as citações de Marlow também trazem a dimensão do determinismo :
A related distinction is between deterministic and nondeterministic programming models. A deterministic programming model is one in which each program can give only one result, whereas a nondeterministic programming model admits programs that may have different results, depending on some aspect of the execution. Concurrent programming models are necessarily nondeterministic because they must interact with external agents that cause events at unpredictable times. Nondeterminism has some notable drawbacks, however: Programs become significantly harder to test and reason about.
For parallel programming, we would like to use deterministic programming models if at all possible. Since the goal is just to arrive at the answer more quickly, we would rather not make our program harder to debug in the process. Deterministic parallel programming is the best of both worlds: Testing, debugging, and reasoning can be performed on the sequential program, but the program runs faster with the addition of more processors.
Em Haskell, os recursos de paralelismo e simultaneidade são projetados em torno desses conceitos. Em particular, o que outras linguagens agrupam como um conjunto de recursos, Haskell se divide em duas partes:
-
Recursos e bibliotecas Deterministic para o paralelismo .
-
Recursos e bibliotecas Não determinísticos para simultaneidade .
Se você está apenas tentando acelerar uma computação pura e determinista, ter um paralelismo determinístico geralmente torna as coisas muito mais fáceis. Muitas vezes você faz algo assim:
- Escreva uma função que produza uma lista de respostas, cada uma das quais é dispendiosa, mas não depende muito uma da outra. Isso é Haskell, então as listas são preguiçosas - os valores de seus elementos não são realmente computados até que um consumidor os exija.
- Use a biblioteca Estratégias para consumir os elementos das listas de resultados da sua função em paralelo em vários núcleos .
Eu realmente fiz isso com um dos meus programas de projetos de brinquedos há algumas semanas . Era trivial paralelizar o programa - a principal coisa que tive que fazer foi, na verdade, adicionar algum código que diz "calcule os elementos dessa lista em paralelo" (linha 90), e obtive um impulso de taxa de transferência quase linear em alguns dos meus casos de teste mais caros.
O meu programa é mais rápido do que se eu tivesse usado utilitários multithreading convencionais baseados em bloqueio? Eu duvido muito que sim. A coisa mais legal no meu caso foi ganhar tanto dinheiro com muito pouco dinheiro - meu código é provavelmente muito abaixo do ideal, mas como é tão fácil fazer paralelismo, tenho uma grande aceleração com muito menos esforço do que propriamente criar o perfil e otimizá-lo, e sem risco de condições de corrida. E isso, eu diria, é a principal maneira de a programação funcional permitir que você escreva programas "mais rápidos".