Optimisation C++/Assembleur par l'exemple

Suite à la recopie de mon document sur les optimisations en C++, j'ai eu envie de rejouer avec l'assembleur (ici x86) pour démontrer les notions exposées dans ce document.

Avant tout il faut savoir, comment, demander à gcc de nous compiler un fichier source en assembleur. Cela passe par le paramètre -S :

gcc -S file.cxx

Produira le fichier file.s en assembleur.

Maintenant comparons l'assembleur produit par trois sources assez proche :

int main() 
{ 
  int a=2; 
  a=a<<2; 
  return 0; 
}
int main()
{
  int a=2;
  a=a*4;
  return 0;
}
int main()
{
  int a=2;
  a=a*3;
  return 0;
}

Et comparons l'assembleur produit (on ne garde que les parties non communes aux trois sources assembleur):

.file "mult1.cxx"
sall $2,-4(%rbp)
.file "mult2.cxx"
sall $2,-4(%rbp)
.file "mult3.cxx"
movl -4(%rbp),%edx
movl %edx,%eax
addl %eax,%eax
addl %edx,%eax
movl %eax,-4(%rbp)

On se rend bien compte de deux choses :
- Pour le compilateur gcc/x86, il à fait l'optimisation lui même (Mais à moins de vérifier, il vaut mieux la faire nous dans le code plutôt que de partir du principe que le compilateur le fera pour nous) de remplacer la multiplication d'un facteur de 2 par un décalage de bit a gauche.
- Que le fait de ne pas avoir décomposé la multiplication par 3 en décalage d'un bit à gauche plus une fois a, à générer un code qui va consommer beaucoup plus de cycle CPU que s'y nous l'avions fait. (nous gagnerons à priori une instruction assembleur, pas plus sur cet exemple simple)

Allez, faisons le test :

int main()
{
  int a=2;
  a=a<<1+a;
  return 0;
}
movl -4(%rbp),%eax
addl $1,%eax
movl %eax,%ecx
sall %cl,-4(%rbp)

Voila, l'homme triomphe encore de la machine :)