Pourquoi l'optimisation de l'éditeur de liens est-elle si mauvaise?
-
05-07-2019 - |
Question
Récemment, un collègue m'a fait remarquer que tout compiler dans un seul fichier créait un code beaucoup plus efficace que de compiler des fichiers objet distincts - même avec l'optimisation du temps de liaison activée . En outre, le temps total de compilation du projet a considérablement diminué. Étant donné que l’efficacité du code est l’une des principales raisons de l’utilisation de C ++, cela m’a surpris.
Clairement, lorsque l’archiveur / éditeur de liens crée une bibliothèque à partir de fichiers d’objets ou les relie à un exécutable, même les optimisations les plus simples sont pénalisées. Dans l'exemple ci-dessous, l'intégration en ligne coûte 1,8% de performances lorsqu'elle est effectuée par l'éditeur de liens au lieu du compilateur. Il semble que la technologie du compilateur devrait être suffisamment avancée pour gérer des situations assez courantes comme celle-ci, mais cela ne se produit pas.
Voici un exemple simple d'utilisation de Visual Studio 2008:
#include <cstdlib>
#include <iostream>
#include <boost/timer.hpp>
using namespace std;
int foo(int x);
int foo2(int x) { return x++; }
int main(int argc, char** argv)
{
boost::timer t;
t.restart();
for (int i=0; i<atoi(argv[1]); i++)
foo (i);
cout << "time : " << t.elapsed() << endl;
t.restart();
for (int i=0; i<atoi(argv[1]); i++)
foo2 (i);
cout << "time : " << t.elapsed() << endl;
}
foo.cpp
int foo (int x) { return x++; }
Résultats de l’exécution: 1,8% de performance sur l'utilisation du foo
lié ??au lieu du foo2
en ligne.
$ ./release/testlink.exe 100000000
time : 13.375
time : 13.14
Et oui, les indicateurs d'optimisation de l'éditeur de liens (/ LTCG) sont activés.
La solution
Je ne suis pas un spécialiste du compilateur, mais je pense que le compilateur dispose de beaucoup plus d'informations à optimiser pour pouvoir fonctionner dans une arborescence de langues, par opposition à l'éditeur de liens qui doit se contenter d'opérer sur la sortie de l'objet. beaucoup moins expressif que le code que le compilateur a vu. Par conséquent, les équipes de développement de l'éditeur de liens et du compilateur consacrent moins d'efforts à l'optimisation de l'éditeur de liens pouvant correspondre, en théorie, aux astuces du compilateur.
BTW, je suis désolé d’avoir distrait votre question initiale dans la discussion à venir. Je comprends maintenant que votre question était un peu différente, plus centrée sur les optimisations statiques temps de compilation / temps de compilation possibles / disponibles.
Autres conseils
Votre collègue est obsolète. La technologie existe depuis 2003 (sur le compilateur MS C ++): / LTCG . La génération de code de temps de liaison traite exactement de ce problème. De ce que je sais, le GCC a déjà cette fonctionnalité en tête pour le compilateur de nouvelle génération.
LTCG n'optimise pas seulement le code comme des fonctions en ligne dans plusieurs modules, il réorganise le code pour optimiser l'emplacement du cache et la création de branches pour un chargement spécifique. Voir Optimisations guidées par le profil . Ces options sont généralement réservées aux versions Release car la construction peut prendre des heures: lier un exécutable instrumenté, exécuter une charge de profilage puis se lier à nouveau aux résultats du profilage. Le lien contient des détails sur ce qui est exactement optimisé avec LTCG:
Insertion en ligne & # 8211; Par exemple, s'il y a existe une fonction A qui fréquemment appelle la fonction B et la fonction B est relativement petit, puis guidé par profil optimisations en ligne fonction B en fonction A.
Spéculation d'appels virtuels & # 8211; Si un appel virtuel ou tout autre appel via un pointeur de fonction, vise fréquemment un certaine fonction, un profil guidé l'optimisation peut insérer un appel direct à exécution conditionnelle la fonction fréquemment ciblée, et l'appel direct peut être en ligne.
Enregistrer l'allocation & # 8211; Optimiser avec les données de profil donnent de meilleurs résultats enregistrer l'allocation.
Optimisation des blocs de base & # 8211; Bloc de base l'optimisation permet couramment exécuté blocs de base qui exécutent temporairement dans un cadre donné pour être placé dans le même ensemble de pages (localité). Ce minimise le nombre de pages utilisées, minimisant ainsi la surcharge de mémoire.
Optimisation taille / vitesse & # 8211; Les fonctions où le programme passe beaucoup de temps peut être optimisé pour la vitesse.
Présentation de la fonction & # 8211; Basé sur l'appel graphique et profilé appelant / appelé comportement, fonctions qui ont tendance à être le même chemin d'exécution sont placé dans la même section.
Optimisation conditionnelle des branches & # 8211; Avec les sondes de valeur, guidées par profil optimisations peuvent trouver si un donné la valeur dans une instruction switch est utilisée plus souvent que d'autres valeurs. Ce la valeur peut alors être extraite de la déclaration de commutateur. La même chose peut être faite avec des instructions si / autre où le optimiseur peut commander le si / sinon donc que le bloc if ou else soit placé en premier selon le bloc est plus souvent vrai.
Séparation du code mort & # 8211; Code qui est non appelé lors du profilage est déplacé à une section spéciale qui est ajoutée à la fin de l'ensemble des sections. Ceci conserve efficacement cette section sur les pages souvent utilisées.
Séparation du code EH & # 8211; Le code EH, étant exceptionnellement exécuté, peut souvent être déplacé vers une section séparée lorsque les optimisations guidées par profil peuvent déterminer que les exceptions se produisent uniquement dans des conditions exceptionnelles.
Intrinsèques de la mémoire & # 8211; L'expansion de les intrinsèques peuvent être mieux décidés si peut être déterminé si un intrinsèque est appelé fréquemment. Une boîte intrinsèque également être optimisé en fonction du bloc taille des déplacements ou des copies.
Votre collègue est plus intelligent que la plupart d'entre nous. Même si cela semble être une approche grossière au début, l’inclusion de projet dans un fichier .cpp unique a une chose que les autres approches, comme l’optimisation du temps de liaison, n’a pas et n’aura pas pour longtemps - fiabilité
Cependant, vous avez posé cette question il y a deux ans et je témoigne que beaucoup de choses ont changé depuis (avec g ++ au moins). La dévirtualisation est beaucoup plus fiable, par exemple.