J'ai bien peur que beaucoup de ceux qui s'expriment sur cette discussion n'aient peut-être pas suffisamment connaissance de ce que fait réellement un optimiseur. Vous êtes vous déjà vraiement battu contre un goulet d'étranglement détecté par VTune? C'est rarissime que le problème se situe au niveau des séquences d'instructions... Je trouve myope de se concentrer sur cet aspect minuscule de l'optimisation, pourtant le seul que l'assembleur permette d'attaquer.
Mais même pour en revenir a ce strict sujet, bien évidemment les instructions SSE sont mises en oeuvre automatiquement par les optimiseurs, y compris les dernières évolutions absconses comme SSE4 ou SSSE3, et ce avec un niveau d'optimalité parfait car basé sur un examen exhaustif de toutes les combinaisons possibles accomplissant la séquence de mouvements/calculs demandés. Les optimiseurs déroulent les boucles avec bien plus de subtilité qu'un humain, car ils ont accès à une bien meilleure vue de ce qui se passe en coulisse qui va bien au delà des instructions elles-mêmes (pre-fecth, cache, calcul simultané des deux branchements, prise en compte de l'historique). La création de code dynamique, technique popularisée sur le cas ultra simple de copie mémoire, est totalement anecdotique en termes d'application (même si je me suis bien amusé avec du temps du 68000), va à l'encontre de la sécurisation du code en mémoire, s'expose à l'activation du mode Data Execution Prevention, et en ignorant complètement la gestion des caches et des bus que l'optimiseur prend lui parfaitement en compte, n'a aucune garantie d'être "imbattable" sur un processeur récent.
Quant au fait que le C++ se désoptimise avec le temps, c'est précisément l'inverse qui se passe: tout code assembleur miraculeux capable de battre tel optimiseur sur tel processeur en 2003 s'expose à devenir obsolète si on change quoi que ce soit dans l'environnement, ne serait-ce que la vitesse ou la taille du cache. Les optimiseurs suivent l'évolution de la technologie au plus près, et peuvent modifier le code compilé sur le même source C++ plus tard sur une architecture totalement ou même légèrement différente, alors qu'ils n'ont par définition pas le droit d'améliorer un code assembleur.
Quelques faits de l'assembleur:
- utiliser l'assembleur est exactement équivalent à dire au compilateur de ne pas modifier ce qui est demandé (mode impératif)
- l'assembleur (Windows non x64) ne dispose que du modèle de programmation historique. En particulier, les raffinements des processeurs récents en termes de registre et de niveaux de cache ou de bus à vitesses différentes ne sont tout simplement pas descriptibles.
- dans les processeurs récents, les instructions elles-mêmes et leur séquence ont beaucoup moins d'importance sur la performance que la disposition en mémoire, les bus concernés, et ce qui se trouve dans les différents tubes de pré-calculs.
- donc l'humain est fortement désavantagé par rapport à l'optimiseur
La meilleure façon de résoudre ce problème est de s'exprimer en termes déclaratifs, ce qui convient parfaitement au C++, contrairement à, disons, un langage interpreté ou de très haut niveau. Le mode impératif de l'assembleur est devenu contre-productif du point de vue de la performance, car tout contre-exemple dans le passé s'est trouvé activement chassé puis intégré par les concepteurs d'optimiseurs. Je n'ai vu aucun contre exemple significatif cette année, alors que c'était encore courant à l'époque Pentium, et pléthorique sur 286 ou 68000. Au fait, l'asm en ligne est carrément retiré de certains compilos 64 bits...
Il faut se faire à l'idée que l'assembleur en 2007, c'est le C++. Le bon vieux copro 87, c'est le Shader 4.0. La jonglerie entre registres, c'est la gestion des collisions sur PCIe. Etc.
Partager