Programação

Compensa usar C ou Assembler no lugar de Scala /Java?

Certas características de Scala, em especial a conversão automática de números inteiros em objetos(“autoboxing”), podem exigir maior processamento computacional do que suas contrapartidas usando apenas tipos primitivos. Cada objeto possui um “chassi” ou “envólucro” o qual acomoda as chamadas de métodos, dados de instância e assim por diante. Logo, um simples número inteiro em Scala, o qual poderia ser armazenado em 4 ou 8 bytes, possui na verdade algumas dezenas de bytes e código binário atrelado ao mesmo!

Por isso, em casos extremos, pode ser conveniente implementar rotinas de cálculos intensos em outras linguagens mais próximas da máquina, como assembler ou C. Em C, quando movemos um long int, estamos movendo 8 bytes apenas. Ao trabalharmos com o tamanho da palavra da máquina nativa, também podemos otimizar o alinhamento dos bytes e o tamanho dos chunks lidos pelo processodor, e obter assim performance ainda melhor de acordo com cada CPU.

Porém, estas técnidas oferecerão apenas melhorias lineares na performance, sendo que o principal limitador de cálculos de fatoriais e exponenciais não é de natureza linear ou de “gordura” da linguagem, e sim de natureza algorítmica. Um algoritmo que apresente tempo exponencial em resposta ao tamanho da entrada em C terá problemas análogos em Java, Perl ou qualquer outra linguagem, interpretada ou não.

Ao usar C ou assembler no lugar de Scala, para o mesmo algoritmo, pode-se obter um ganho de minutos, horas ou até dias no processamento, sendo que os problemas exponenciais e fatoriais envolvem grandezas hiper-astronômicas(por exemplo, o número de jogadas possíveis no Xadrez supera o número de atomos do universo!) e normalmente, teoricamente, exigem tempos de processamento que podem superar o tempo de vida do universo se o algoritmo errado for escolhido.

Fatorar chaves criptográficas modestas, como por exemplo o algoritmo AES de “apenas” 256 bits, pode consumir toda a energia do universo apenas para alterar o estado lógico dos portões lógicos de silício o número de vezes requerido para quebrar tal criptografia por força bruta. Neste caso, não adianta em nada usar C ou assembler no lugar de Scala, pois o obstáculo se encontra na abordagem algorítmica, sendo que podem existir formas computacionalmente menos dispendiosas para derrotar tais algoritmos criptográficos.

No entanto há problemas para os quais não conhecemos abordagens alternativas aos algorítmos. Na busca por números primos, por exemplo, podemos usar algumas heurísticas de otimização, como a eliminação de números pares e a filtragem de múltiplos de outros números já testados(“peneira de Eratósthenes”). Para esses problemas, de nada adianta alterar de Scala ou Java para uma linguagem nativa como C ou assembler. Perde-se legibilidade do código fonte, a manutenção do mesmo torna-se problemática, e os ganhos limitam-se a ganhos lineares, restritos apenas à melhor utilização do hardware pela linguagem.

No entanto, para aplicações de tempo real, como aparelhos que devem responder imediatamente a estímulos(aplicações hospitalares ou de segurança em usinas nucleares, por exemplo), onde milisegundos de retardo podem fazer toda a diferença, aí sim devemos buscar em linguagens como C e assembler as frações de ciclos de processamento que necessitamos.

Desta forma, a substituição de Scala/Java por C deve ser criteriosamente decidida de acordo com a finalidade em vista. Caso a intenção seja vencer algoritmos criptográficos ou calcular números astronômicos, os ganhos serão, no máximo, lineares.

Caso a intenção seja garantir um tempo crítico de resposta em tempo real, a opção por uma linguagem nativa pode significar uma vida.

Standard