Size matters not!

Se você já mexeu um cadinho com o compilador NASM (Netwide Assembler), já deve ter reparado que os executáveis gerados por ele são quase 5 kB menores que os mesmos códigos feitos em C. Um exemplo em NASM e outro em C:

; testa.asm
section .text

global _start
_start:
  mov eax,1        ; exit(0);
  xor ebx,ebx
  int 0x80

E o mesmo código em C:

/* testc.c */
int main(void) { return 0; }

Compilando e linkando os dois códigos:

$ nasm -f elf testa.asm -o testa.o
$ ld testa.o -o testa

$ gcc -o testc testc.c

$ strip -s testa
$ strip -s testc
$ ls -l
total 20
-rwxr-xr-x 1 fred fred  244 2011-07-07 15:15 testa
-rw-r--r-- 1 fred fred   75 2011-07-07 15:13 testa.asm
-rwxr-xr-x 1 fred fred 5500 2011-07-07 15:15 testc
-rw-r--r-- 1 fred fred   29 2011-07-07 15:15 testc.c

Códigos em C são linkados com um arquivo objeto chamado crt1.o. Isso é fácil perceber quando vocẽ tenta usar o gcc como linker para o object file testa.o:

$ gcc -o test test.o
test.o: In function `_start':
test.asm:(.text+0x0): multiple definition of `_start'
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crt1.o:(.text+0x0): first defined here
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status

CRT, neste contexto, significa C RunTime. É um arquivo objeto que inicializa e finaliza um programa em C. Ou seja: crt1.o implementa algumas rotinas que são chamadas antes do main() e depois dele também. Esse arquivo-objeto certifica-se de inicializar os parâmetros argc e argv de main(), de inicializar variáveis globais da biblioteca libc, dentre outras tarefas de incialização, bem como fazer algum housekeeping depois que main() é encerrado.

O código em ASM não tem nada disso.

Isso não significa que o código em C é mais lento do que o código equivalente em ASM. De fato, o compilador GCC faz um belo trabalho na etapa de otimização de código.

Um dos problemas de criar códigos inteiros em ASM é que existem muitas características específicas para cada arquitetura que não são levadas em conta pelo desenvolvedor. Isso pode causar um código mais lento em ASM do que o mesmo código em C… Tive essa experiência em primeira mão quando um conhecido meu resolveu brincar com animação 2D, em ASM… Ele me passou o código e eu o refiz inteiramente em C… Meu código ficou umas 100 vezes mais rápido. Não porque esse conhecido não soubesse ASM, mas pela técnica usada por ele…

Programar em ASM é, antes de tudo, demanda conhecimento aprofundado de arquiteturas e do funcionamento interno (até certo ponto) do processador e da interação deste com caches, memória, etc. Coisa que está bem consolidada nas rotinas de otimização do GCC. Quando você usa a opção -mtune, do GCC, está dizendo ao compilador duas coisas:

  1. Use o sub-conjunto de instruções disponíveis para o processador que estou informando;
  2. Otimize para este processador

No compilador assembly isso não é feito. O “montador” (que é o nome dado para o compilador assembly) assume que vocẽ sabe o que faz. A única “otimização” efetuada pelo montador é quanto ao cálculo de endereços efetivos. No MASM, ao usar a opção -Ox, o montador executa a compilação em 2 ou mais passos, para ter certeza que alguns endereços serão “simplificados”. Por exemplo:

  jmp there
loop:
  ; faz algo aqui
there:
  dec ecx
  jnz loop

Na primeira passagem o montador não sabe que “there” está longe ou perto do JMP. Então o endereço reservado para there na instrução JMP é de 4 bytes (32 bits). Só que existe uma variação de JMP que aceita offsets relativos, que são bem mais curtos do que o JMP tradicional. Somente no segundo passo é que o montador sabe que there está perto o suficiente para usar essa outra variação de JMP…

Mas, se você quer multiplicar o valor de EAX por 3 e faz algo assim:

mov ebx,3
imul ebx

O NASM não modificará esse código para:

lea eax,[2*eax+eax]

Que é diversas vezes mais veloz que a implementação original… O compilador GCC fará esse tipo de otimização para você…

Depois de tudo isso, o que quero dizer é: Não é porque o código ficou pequeno isso signifique que é o melhor código. Ou, como diria o Mestre Yoda: “Size matters not!”

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s