mslomp
(usa Slackware)
Enviado em 29/09/2008 - 00:51h
com a permissão do colega Teixeira, gostaria de destrinchar duas de suas afirmações:
1 - "o código em C deixa menos "lixo" e isso é importante na velocidade e na segurança de execução"
esse é um dos aspectos positivos de C que está intimamente ligado à simplicidade de sua implementação. C possui uma estrutura sintática sem firulas que, embora tente se aproximar da linguagem humana, não perde sua porção fria e maquinal. sob essa ótica é possível arriscar afirmar que sim, C está próxima do hardware, porém sem se distanciar muito do que chamamos alto nível. à medida que você aumentar a distância do hardware (e conseqüentemente diminuir a distância do alto nível), o aumento das abstrações da linguagem será tal que sua interpretação e tradução para linguagem do hardware tornar-se-á mais complexa e imprecisa. é como traduzir um texto do inglês para o português através de um software. a tradução ficará esquisita, porém inteligível a partir do contexto. contudo, compiladores e interpretadores ainda não atingiram a capacidade de guiarem-se por contexto, o que com certeza poderia eliminar um pouco do "lixo" produzido. muito se fala sobre inteligência atrificial, e ela de fato pode, deve e já está implementada em softwares, incluindo compiladores capazes de decidir os caminhos mais curtos para a execução de uma tarefa. porém, ainda que um dia ela (AI) possa atingir um estado de arte, ainda haverá uma burrice artificial nata em cada uma daquelas pastilhas de silício.
2 - "quanto mais próximo da linguagem de execução (binária), melhor para o processador"
essa afirmação está bastante ligada à idéia anterior, porém abre caminho para demonstrá-la na prática, e aqui entra a importância do nosso amigo compilador, afinal é ele quem irá traduzir nosso código humano em código de máquina. um compilador escrito de forma que não se preocupe com o "lado de lá" da coisa, produzirá códigos às vezes "idiotas" do ponto de vista racional e ótimo. e para piorar, o código gerado é estático. uma vez transformado em executável, será executado daquela forma para sempre, por mais "burro" que seja tal código. o processador apenas executará, não analisará nada e nem aprenderá com os erros. por isso, devemos dar a ele a solução o mais mastigada possível, e quem fará esse papel de "professor" é o compilador.
vou ilustrar utilizando um método de otimização chamado common subexpression elimination (cse), por ser facilmente observável.
tomemos as duas seguintes sentenças:
x = a + b + c;
y = a + c + d;
para nós, humanos, supondo a, b, c, d conhecidos, o resultado saltaria aos olhos, porém vejamos como nosso amigo processador resolveria o problema, desconsiderando qualquer otimização. utilizarei uma sintaxe assembly modo intel e sem rigores de endereçamento a fim de facilitar a visualização. os números nos comentários (após cada ; ) correspondem ao número de ciclos de máquina requeridos por cada operação.
; calcula x
mov eax,a ;1
add eax,b ;3
add eax,c ;3
mov x,eax ;1
; calcula y
mov edx,a ;1
add edx,c ;3
add edx,d ;3
mov y,edx ;1
total: 16 ciclos
ok. observando novamente as expressões, soa-nos lógico que temos uma expressão em comum em ambas (a + c)
x = a + b + c;
y = a + c + d;
podemos então criar uma outra variável temporária que conterá essa expressão comum:
z = a + c;
assim:
x = b + z;
y = z + d;
daremos agora uma colher de chá ao nosso amigo ensinando-lhe o truque através do cse e vejamos como ele resolverá o problema:
; calcula z
mov eax,a ;1
add eax,c ;3
; calcula x (z está em eax)
mov ecx,eax ;1
add ecx,b ;3
mov x,ecx ;1
; calcula y
mov edx,eax ;1
add edx,d ;3
mov y,edx ;1
total: 14 ciclos
maravilha! reduzimos o número de ciclos exigidos para a tarefa e eliminamos um pouco do lixo!
agora aumente o nível de abstração para as expressões, utilizando os métodos pirotécnicos de algumas linguagens modernas e tente eliminar o lixo... se bobear, produzirá ainda mais lixo. elas facilitam o nosso lado, mas podem tornar o lado de lá um inferno.