são compiladores como Javac inteligentes o suficiente para detectar quando um método é uma função pura.
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.