As funções puras são conhecidas por facilitar a paralelização. O que é a programação funcional que a torna inerentemente adaptada à execução paralela?
Compiladores como Javac são inteligentes o suficiente para detectar quando um método é uma função pura? Pode-se sempre implementar classes que implementam interfaces funcionais como Function , mas têm efeitos colaterais.
functional-programming
Naveen
fonte
fonte
NullPointerException
s. Os benefícios das otimizações com base nisso também são provavelmente bastante pequenos para aplicativos Java típicos.Respostas:
Não é uma questão de "inteligente o suficiente". Isso é chamado de Análise de Pureza e é comprovadamente impossível no caso geral: é equivalente a resolver o Problema da Parada.
Agora, é claro, os otimizadores fazem coisas comprovadamente impossíveis o tempo todo, "comprovadamente impossível no caso geral" não significa que nunca funcione, apenas significa que não pode funcionar em todos os casos. Portanto, de fato, existem algoritmos para verificar se uma função é pura ou não; é mais provável que o resultado seja "Não sei", o que significa que, por razões de segurança e exatidão, você deve assumir que essa função específica possa ser impura.
E mesmo nos casos em que faz o trabalho, os algoritmos são complexos e caros.
Então, esse é o problema nº 1: ele funciona apenas para casos especiais .
Problema # 2: Bibliotecas . Para que uma função seja pura, ela só pode chamar funções puras (e essas funções podem chamar apenas funções puras, e assim por diante). Obviamente, Javac sabe apenas sobre Java e apenas sobre código que pode ver. Portanto, se sua função chamar uma função em outra unidade de compilação, não será possível saber se é pura ou não. Se chamar uma função escrita em outro idioma, você não saberá. Se ele chama uma função em uma biblioteca que talvez ainda não esteja instalada, você não pode saber. E assim por diante.
Isso só funciona quando você tem uma análise do programa inteiro, quando o programa inteiro é escrito no mesmo idioma e tudo é compilado de uma só vez. Você não pode usar nenhuma biblioteca.
Problema nº 3: agendamento . Depois de descobrir quais peças são puras, você ainda precisa agendá-las para separar os segmentos. Ou não. Iniciar e parar threads é muito caro (especialmente em Java). Mesmo se você mantiver um conjunto de encadeamentos e não os iniciar ou parar, a alternância de contexto de encadeamento também será cara. Você precisa ter certeza de que o cálculo será executado significativamente mais do que o tempo necessário para agendar e alternar o contexto; caso contrário, você perderá o desempenho e não o obterá.
Como você provavelmente já adivinhou, descobrir o tempo que uma computação levará é comprovadamente impossível no caso geral (não podemos nem imaginar se levará um tempo finito, quanto mais tempo) e difícil e caro, mesmo em o caso especial.
Além: Javac e otimizações . Observe que a maioria das implementações do javac não realiza muitas otimizações. A implementação do javac da Oracle, por exemplo, depende do mecanismo de execução subjacente para fazer otimizações . Isso leva a outro conjunto de problemas: digamos, o javac decidiu que uma função específica é pura e é cara o suficiente e, portanto, a compila para ser executada em um encadeamento diferente. Em seguida, o otimizador da plataforma (por exemplo, o compilador HotSpot C2 JIT) aparece e otimiza toda a função. Agora, você tem um segmento vazio sem fazer nada. Ou, imagine, novamente, o javac decide agendar uma função em um encadeamento diferente, e o otimizador de plataforma pode otimize-o completamente, exceto que ele não pode executar inlining através dos limites do encadeamento e, portanto, uma função que poderia ser otimizada completamente agora é desnecessariamente executada.
Portanto, fazer algo assim só faz sentido se você tiver um único compilador fazendo a maioria das otimizações de uma só vez, para que o compilador conheça e possa explorar todas as otimizações diferentes em diferentes níveis e suas interações entre si.
Note-se que, por exemplo, o compilador HotSpot C2 JIT realmente faz executar alguma auto-vetorização, que também é uma forma de auto-paralelização.
fonte
definition
, usando um disparatedefinition
depurity
provavelmente é obscuraStringBuilder
) não faz sentido, então eu o descartaria e simplesmente assumiria que o OP escreve javac, mas significa Hotspot. Seu problema # 2 é uma boa razão para otimizar qualquer coisa em javac.A resposta votada não conseguiu notar uma coisa. A comunicação síncrona entre threads é extremamente cara. Se a função for capaz de ser executada a uma taxa de muitos milhões de chamadas por segundo, será mais difícil paralelizá-la do que deixá-la como está.
Infelizmente, a forma mais rápida de comunicação síncrona entre threads, usando loops ocupados com variáveis atômicas, é ineficiente em energia. Se você precisar recorrer ao uso de variáveis de condição para economizar energia, o desempenho da sua comunicação entre threads é prejudicado.
Portanto, o compilador não precisa apenas determinar se uma função é pura, mas também estimar o tempo de execução da função para ver se a paralelização é uma vitória líquida. Além disso, seria necessário escolher entre loops ocupados usando variáveis atômicas ou variáveis de condição. E seria necessário criar tópicos nas suas costas.
Se você criar os encadeamentos dinamicamente, será ainda mais lento do que usar variáveis de condição. Portanto, o compilador precisaria configurar um certo número de threads já em execução.
Portanto, a resposta para sua pergunta é não , os compiladores não são "inteligentes" o suficiente para paralelizar automaticamente funções puras, especialmente no mundo Java. Eles são inteligentes, não os paralelizando automaticamente!
fonte