web-dev-qa-db-fra.com

Liaison statique vs liaison dynamique

Existe-t-il des raisons de performance convaincantes pour choisir la liaison statique sur la liaison dynamique ou inversement dans certaines situations? J'ai entendu ou lu ce qui suit, mais je ne connais pas suffisamment le sujet pour en garantir la véracité.

1) La différence de performances d'exécution entre la liaison statique et la liaison dynamique est généralement négligeable.

2) (1) n'est pas vrai si vous utilisez un compilateur de profilage qui utilise les données de profil pour optimiser les hotpaths du programme, car avec une liaison statique, le compilateur peut optimiser à la fois votre code et le code de la bibliothèque. Avec la liaison dynamique, seul votre code peut être optimisé. Si vous passez la plupart du temps à exécuter du code de bibliothèque, cela peut faire une grande différence. Sinon, (1) s'applique toujours.

374
Eloff
  • La liaison dynamique peut réduire la consommation totale de ressources (si plusieurs processus se partagent la même bibliothèque (y compris la version dans "the same", bien sûr)). Je crois que c'est l'argument qui fait sa force dans la plupart des environnements. Ici, "ressources" inclut l'espace disque, la RAM et l'espace cache. Bien sûr, si votre éditeur de liens dynamique n’est pas suffisamment flexible, il existe un risque de enfer de DLL .
  • La liaison dynamique signifie que les corrections de bogues et les mises à niveau des bibliothèques se propagent pour améliorer votre produit sans que vous ayez à expédier quoi que ce soit.
  • Les plugins appellent toujours pour des liens dynamiques .
  • La liaison statique signifie que vous pouvez savoir que le code fonctionnera dans des environnements très limités (au début du processus de démarrage ou en mode de secours).
  • La liaison statique peut rendre les fichiers binaires plus faciles à distribuer à divers environnements utilisateur (à coût d'envoi d'un programme plus important et gourmand en ressources).
  • La liaison statique peut permettre des temps de démarrage légèrement plus rapides , mais cela dépend degré sur la taille et la complexité de votre programme et sur les détails de la stratégie de chargement du système d’exploitation.

Quelques modifications pour inclure les suggestions très pertinentes dans les commentaires et dans d'autres réponses. Je tiens à souligner que la façon dont vous résolvez ce problème dépend en grande partie de l'environnement dans lequel vous envisagez de fonctionner. Les systèmes intégrés minimaux risquent de ne pas disposer de suffisamment de ressources pour prendre en charge les liaisons dynamiques. Les petits systèmes légèrement plus grands peuvent bien prendre en charge la liaison dynamique, car leur mémoire est suffisamment petite pour rendre les RAM économies de la liaison dynamique très attractives. Comme le note Mark, les ordinateurs grand public disposent d’énormes ressources, et vous pouvez probablement laisser les problèmes de commodité vous guider dans votre réflexion à ce sujet.


Pour résoudre les problèmes de performance et d’efficacité: cela dépend .

Classiquement, les bibliothèques dynamiques nécessitent une sorte de couche de colle qui signifie souvent une double dépose ou une couche supplémentaire d'indirection dans l'adressage des fonctions et peut coûter un peu de vitesse (mais le temps d'appel de fonction est-il une partie importante de votre temps d'exécution ???).

Toutefois, si vous exécutez plusieurs processus qui appellent tous beaucoup la même bibliothèque, vous pouvez enregistrer les lignes de cache (et ainsi gagner sur les performances d'exécution) en utilisant la liaison dynamique par rapport à l'utilisation de la liaison statique. (À moins que les systèmes d'exploitation modernes ne soient suffisamment intelligents pour remarquer des segments identiques dans des fichiers binaires liés statiquement. Cela semble difficile, quelqu'un le sait?)

Un autre problème: le temps de chargement. Vous payez des frais de chargement à un moment donné. Lorsque vous payez, ce coût dépend du fonctionnement du système d'exploitation ainsi que des liens que vous utilisez. Peut-être préférez-vous différer le paiement jusqu'à ce que vous sachiez que vous en avez besoin.

Notez que la liaison statique-vs-dynamique est traditionnellement et non un problème d'optimisation, car ils impliquent tous les deux une compilation séparée en fichiers objet. Toutefois, cela n’est pas nécessaire: un compilateur peut en principe "compiler" "des bibliothèques statiques" sur un formulaire AST digéré, et les "lier" en ajoutant ces AST à ceux générés pour le code principal. , optimisant ainsi l'optimisation globale. Aucun des systèmes que j'utilise ne le fait, je ne peux donc pas dire si cela fonctionne bien.

La façon de répondre aux questions de performance est toujours en testant (et utilisez un environnement de test aussi semblable que possible à l'environnement de déploiement).

330
dmckee

1) est basé sur le fait que l'appel d'une fonction DLL utilise toujours un saut indirect supplémentaire. Aujourd'hui, c'est généralement négligeable. À l'intérieur de la DLL, il y a encore de la surcharge sur les processeurs i386, car ils ne peuvent pas générer de code indépendant de la position. Sur AMD64, les sauts peuvent être relatifs au compteur de programme, il s’agit donc d’une amélioration considérable.

2) Ceci est correct. Avec des optimisations guidées par le profilage, vous pouvez généralement obtenir des performances de l'ordre de 10 à 15%. Maintenant que la vitesse du processeur a atteint ses limites, il vaut peut-être la peine de le faire.

J'ajouterais: (3) l'éditeur de liens peut organiser les fonctions dans un regroupement plus efficace du cache, afin de minimiser les échecs coûteux du niveau de cache. Cela pourrait également affecter particulièrement le temps de démarrage des applications (en fonction des résultats que j'ai vus avec le compilateur Sun C++)

Et n'oubliez pas qu'avec les DLL, aucune élimination de code mort ne peut être effectuée. Selon la langue, le code DLL risque de ne pas être optimal non plus. Les fonctions virtuelles sont toujours virtuelles car le compilateur ne sait pas si un client le remplace.

Pour ces raisons, au cas où il n'y aurait pas vraiment besoin de DLL, utilisez simplement la compilation statique.

EDIT (pour répondre au commentaire, par soulignement utilisateur)

Voici une bonne ressource sur le problème de code indépendant de la position http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

Comme expliqué, x86 ne les prend pas pour autant que je sache, sauf pour les plages de sauts de 15 bits et non pour les sauts et les appels inconditionnels. C’est pourquoi les fonctions (génératrices) de plus de 32K ont toujours été un problème et nécessitaient des trampolines intégrés.

Mais sur les systèmes d'exploitation x86 populaires tels que Linux, vous n'avez pas besoin de vous soucier de savoir si le fichier SO/DLL n'est pas généré avec le commutateur gcc-fpic (qui impose l'utilisation des tables de saut indirect). Parce que si vous ne le faites pas, le code est simplement corrigé comme un éditeur de liens normal le déplacerait. Ce faisant, il rend le segment de code non partageable et nécessite un mappage complet du code du disque dans la mémoire et sa manipulation avant de pouvoir être utilisé (vider la plupart des caches, frapper des TLB), etc. quand cela a été considéré lent ... trop lent.

Donc, vous n'auriez plus aucun avantage.

Je ne me souviens pas quel système d’exploitation (Solaris ou FreeBSD) me posait des problèmes avec mon système de compilation Unix car je ne le faisais pas et je me demandais pourquoi il s’était écrasé tant que j’avais appliqué -fPIC à gcc.

66
Lothar

La liaison dynamique est le seul moyen pratique de respecter certaines conditions de licence, telles que LGPL .

64
Mark Ransom

Je suis d'accord avec les points mentionnés par dnmckee, plus:

  • Les applications liées statiquement peuvent être plus faciles à déployer, car il existe peu ou pas de dépendances de fichiers supplémentaires (.dll/.so) susceptibles de poser des problèmes lorsqu'elles sont manquantes ou installées au mauvais endroit.
43
stakx

Une des raisons de créer une construction liée de manière statique est de vérifier que l’exécutable est complètement fermé, c’est-à-dire que toutes les références de symbole sont résolues correctement.

Faisant partie d’un grand système construit et testé à l’aide d’une intégration continue, les tests de régression effectués de nuit ont été exécutés à l’aide d’une version liée statiquement des exécutables. De temps en temps, nous voyions qu'un symbole ne serait pas résolu et que le lien statique échouerait même si l'exécutable lié de manière dynamique établirait une liaison avec succès.

Cela se produisait généralement lorsque les symboles profondément ancrés dans les bibliothèques partagées avaient un nom mal orthographié et ne pouvaient donc pas être liés statiquement. L'éditeur de liens dynamique ne résout pas complètement tous les symboles, que vous utilisiez une évaluation en profondeur d'abord ou en largeur d'abord. Vous pouvez donc terminer avec un exécutable lié dynamiquement qui n'a pas de fermeture complète.

32
Rob Wells

1/J'ai participé à des projets dans lesquels la liaison dynamique par rapport à la liaison statique avait été analysée et où la différence n'était pas assez petite pour passer à la liaison dynamique (je ne faisais pas partie du test, je connais juste la conclusion)

2/La liaison dynamique est souvent associée à un PIC (code indépendant de la position, code qu’il n’est pas nécessaire de modifier en fonction de l’adresse à laquelle il est chargé). Selon l'architecture, le PIC peut entraîner un ralentissement supplémentaire mais est nécessaire pour tirer parti du partage d'une bibliothèque liée de manière dynamique entre deux exécutables (et même deux processus du même exécutable si le système d'exploitation utilise la randomisation de l'adresse de charge comme mesure de sécurité). Je ne suis pas sûr que tous les systèmes d'exploitation permettent de séparer les deux concepts, mais Solaris et Linux le sont, ainsi que ISTR, comme le fait HP-UX.

3/Je suis allé sur d'autres projets qui utilisaient la liaison dynamique pour la fonction "easy patch". Mais ce "correctif facile" facilite un peu la distribution des petites corrections et complique le cauchemar des versions. Nous finissions souvent par pousser tout et suivre les problèmes sur le site du client car la mauvaise version était un jeton.

Ma conclusion est que j'avais utilisé des liens statiques excepté:

  • pour des choses comme les plugins qui dépendent de la liaison dynamique

  • lorsque le partage est important (grandes bibliothèques utilisées simultanément par plusieurs processus comme le runtime C/C++, les bibliothèques d'interface graphique, ... qui sont souvent gérées indépendamment et pour lesquelles l'ABI est strictement défini)

Si l'on veut utiliser le "correctif facile", je dirais que les bibliothèques doivent être gérées comme les grandes bibliothèques ci-dessus: elles doivent être presque indépendantes avec une ABI définie qui ne doit pas être modifiée par des correctifs.

21
AProgrammer

This discutez en détail des bibliothèques partagées sur Linux et de ses conséquences sur les performances.

20
nos

C'est assez simple, vraiment. Lorsque vous apportez une modification à votre code source, voulez-vous attendre 10 minutes pour qu'il soit généré ou 20 secondes? Vingt secondes, c'est tout ce que je peux supporter. Au-delà de cela, je sors l'épée ou commence à réfléchir à la manière dont je peux utiliser une compilation et une liaison séparées pour la ramener dans la zone de confort.

11
Hans Passant

Sur les systèmes de type Unix, la liaison dynamique peut rendre difficile l’utilisation d’une application "racine" avec les bibliothèques partagées installées à des emplacements éloignés. En effet, l'éditeur de liens dynamique ne fait généralement pas attention à LD_LIBRARY_PATH ou à son équivalent pour les processus dotés de privilèges root. Parfois, une liaison statique sauve la journée.

Sinon, le processus d'installation doit localiser les bibliothèques, mais cela peut rendre difficile la coexistence de plusieurs versions du logiciel sur la machine.

10
Jonathan Leffler

La liaison dynamique nécessite du temps supplémentaire pour que le système d'exploitation trouve la bibliothèque dynamique et la charge. Avec la liaison statique, tout est ensemble et il s’agit d’un chargement unique en mémoire.

Voir aussi DLL Hell . Il s'agit du scénario dans lequel la DLL que le système d'exploitation charge n'est pas celle fournie avec votre application ou la version attendue par celle-ci.

7
Thomas Matthews

Le meilleur exemple de liaison dynamique est lorsque la bibliothèque dépend du matériel utilisé. Dans les temps anciens, la bibliothèque de mathématiques C avait été choisie dynamique, afin que chaque plate-forme puisse utiliser toutes les capacités du processeur pour l'optimiser.

Un exemple encore meilleur pourrait être OpenGL. OpenGl est une API implémentée différemment par AMD et NVidia. Et vous ne pouvez pas utiliser une implémentation NVidia sur une carte AMD, car le matériel est différent. Vous ne pouvez pas relier OpenGL de manière statique à votre programme, à cause de cela. La liaison dynamique est utilisée ici pour permettre à l'API d'être optimisée pour toutes les plateformes.

7
Arne

Un autre problème qui n’a pas encore été abordé est la correction de bogues dans la bibliothèque.

Avec la liaison statique, vous devez non seulement reconstruire la bibliothèque, mais également relier et redistribuer l'exécutable. Si la bibliothèque n’est utilisée que dans un seul exécutable, le problème ne se pose peut-être pas. Mais plus le nombre d'exécutables devant être liés et redistribués est important, plus la douleur est grande.

Avec la liaison dynamique, il vous suffit de reconstruire et de redistribuer la bibliothèque dynamique et vous avez terminé.

5
R Samuel Klatchko

les liens statiques ne vous donnent qu'un seul exe, afin d'effectuer un changement dont vous avez besoin pour recompiler tout votre programme. Alors que dans les liens dynamiques, vous devez apporter des modifications uniquement à la dll et lorsque vous exécutez votre fichier exe, les modifications seraient prises en charge au moment de l'exécution. Il est plus facile de fournir des mises à jour et des correctifs de bogues par des liens dynamiques (par exemple: Windows).

3
Govardhan Murali

Il existe un nombre important et croissant de systèmes dans lesquels un niveau extrême de liaisons statiques peut avoir un impact positif énorme sur les applications et les performances du système.

Je fais référence à ce que l’on appelle souvent les "systèmes embarqués", dont beaucoup utilisent de plus en plus des systèmes d’exploitation à usage général, et ces systèmes sont utilisés pour tout ce qui est imaginable.

Un exemple extrêmement courant concerne les périphériques utilisant des systèmes GNU/Linux utilisant Busybox . J'ai poussé ceci à l'extrême avec NetBSD en construisant une image système amorçable i386 (32 bits) qui inclut à la fois un noyau et son système de fichiers racine, ce dernier contenant un unique lien statique (par crunchgen) binaire avec des liens physiques vers tous les programmes contenant tous (bien au dernier décompte 274) du système complet standard programmes (la plupart sauf la chaîne d'outils), et sa taille est inférieure à 20 méga octets (et s'exécute probablement très confortablement dans un système ne disposant que de 64 Mo de mémoire (même avec le système de fichiers racine non compressé et entièrement en RAM), bien que je n’ai pas pu en trouver un aussi petit pour le tester).

Il a été mentionné dans des publications précédentes que le temps de démarrage d’un binaire statique est plus rapide (et que cela peut être un lot plus rapide), mais ce n’est qu’une partie de la image, en particulier lorsque tout le code objet est lié au même fichier, et encore plus lorsque le système d'exploitation prend en charge la pagination à la demande du code directement à partir du fichier exécutable. Dans ce scénario idéal, le temps de démarrage des programmes est littéralement négligeable car presque toutes les pages de code seront déjà en mémoire et utilisées par le shell (et et init d’autres processus en arrière-plan en cours d’exécution), même si le programme demandé n’a jamais été exécuté depuis le démarrage, car il ne faut peut-être qu’une seule page de mémoire pour remplir les conditions d’exécution du programme.

Cependant, ce n'est toujours pas toute l'histoire. De plus, j’ai généralement créé et utilisé les installations du système d’exploitation NetBSD pour mes systèmes de développement complet en liant tous les fichiers binaires de manière statique. Même si cela prend énormément d’espace disque supplémentaire (environ 6,6 Go au total pour x86_64 avec tout, y compris toolchain et X11 liés statiquement) (en particulier si l’on conserve des tables de symboles de débogage complètes pour tous les programmes à environ 2,5 Go), le résultat reste s'exécute plus rapidement dans l'ensemble et utilise même moins de mémoire pour certaines tâches qu'un système classique à liaison dynamique qui prétend partager des pages de code de bibliothèque. Le disque est bon marché (même le disque le plus rapide) et la mémoire utilisée pour mettre en cache les fichiers fréquemment utilisés est également relativement peu coûteuse, mais les cycles de processeur ne le sont pas vraiment, et payer le coût de démarrage ld.so pour chaque processus démarrant chaque fois qu’il démarrera, il faudra des heures et des heures de cycle de traitement de la part des tâches qui nécessitent le démarrage de nombreux processus, en particulier lorsque les mêmes programmes sont utilisés à répétition, tels que les compilateurs sur un système de développement. Les programmes de chaînes d'outils liés statiquement peuvent réduire les temps de construction multi-architecture de système d'exploitation complet pour mes systèmes de heures . Je n’ai pas encore construit la chaîne d’outils dans mon binaire ed crunchgen ', mais je soupçonne que cela économisera encore du temps de compilation en raison du gain remporté par le cache du processeur.

2
Greg A. Woods

La liaison statique inclut les fichiers dont le programme a besoin dans un seul fichier exécutable.

La liaison dynamique est ce que vous considéreriez comme d'habitude, cela rend un exécutable qui nécessite toujours des DLL et que ces fichiers se trouvent dans le même répertoire (ou les DLL pourraient se trouver dans le dossier système).

(DLL = lien dynamique bibliothèque)

Les exécutables liés dynamiquement sont compilés plus rapidement et ne nécessitent pas autant de ressources.

1
Nykal