web-dev-qa-db-fra.com

Pourquoi les pointeurs intelligents de comptage de références sont-ils si populaires?

Comme je peux le voir, les pointeurs intelligents sont largement utilisés dans de nombreux projets C++ du monde réel.

Bien qu'une sorte de pointeurs intelligents soit évidemment bénéfique pour prendre en charge RAII et les transferts de propriété, il existe également une tendance à utiliser des pointeurs partagés par défaut, comme moyen de "garbage collection", pour que le programmeur n'ait pas à penser autant à l'allocation.

Pourquoi les pointeurs partagés sont-ils plus populaires que l'intégration d'un bon ramasse-miettes comme Boehm GC ? (Ou êtes-vous d'accord pour dire qu'ils sont plus populaires que les GC réels?)

Je connais deux avantages des GC conventionnels par rapport au comptage de références:

  • Les algorithmes GC classiques n'ont pas de problème avec les cycles de référence .
  • Le compte de référence est généralement plus lent qu'un GC approprié.

Quelles sont les raisons d'utiliser des pointeurs intelligents de comptage de références?

52
Miklós Homolya

Quelques avantages du comptage de références par rapport au ramasse-miettes:

  1. Frais généraux faibles. Les récupérateurs peuvent être assez intrusifs (par exemple, faire geler votre programme à des moments imprévisibles pendant le traitement d'un cycle de collecte des ordures) et assez gourmands en mémoire (par exemple, l'empreinte mémoire de votre processus augmente inutilement à plusieurs mégaoctets avant que la collecte des ordures n'intervienne enfin)

  2. Comportement plus prévisible. Avec le comptage des références, vous avez la garantie que votre objet sera libéré dès que la dernière référence le concernant aura disparu. Avec le garbage collection, en revanche, votre objet sera libéré "à un moment", lorsque le système y arrivera. Pour RAM ce n'est généralement pas un gros problème sur les postes de travail ou les serveurs légèrement chargés, mais pour d'autres ressources (par exemple les descripteurs de fichiers), vous devez souvent les fermer le plus tôt possible pour éviter des conflits potentiels plus tard.

  3. Plus simple. Le comptage des références peut être expliqué en quelques minutes et implémenté en une heure ou deux. Les éboueurs, en particulier ceux qui ont des performances décentes, sont extrêmement complexes et peu de gens les comprennent.

  4. La norme. C++ inclut le comptage de références (via shared_ptr) et ses amis dans la STL, ce qui signifie que la plupart des programmeurs C++ le connaissent et que la plupart du code C++ fonctionnera avec. Il n'y a cependant pas de garbage collector C++ standard, ce qui signifie que vous devez en choisir un et espérer qu'il fonctionne bien pour votre cas d'utilisation - et si ce n'est pas le cas, c'est votre problème à résoudre, pas celui du langage.

En ce qui concerne les inconvénients présumés du comptage de références - la détection des cycles n'est pas un problème, mais un problème que je n'ai jamais personnellement rencontré au cours des dix dernières années d'utilisation du comptage de références. La plupart des structures de données sont naturellement acycliques, et si vous rencontrez une situation où vous avez besoin de références cycliques (par exemple, le pointeur parent dans un nœud d'arbre), vous pouvez simplement utiliser un faible_ptr ou un pointeur C brut pour la "direction arrière". Tant que vous êtes conscient du problème potentiel lorsque vous concevez vos structures de données, ce n'est pas un problème.

Quant aux performances, je n'ai jamais eu de problème avec les performances du comptage de références. J'ai eu des problèmes avec les performances de la récupération de place, en particulier les blocages aléatoires que le GC peut subir, auxquels la seule solution ("ne pas allouer d'objets") pourrait aussi bien être reformulée comme "ne pas utiliser le GC" .

57
Jeremy Friesner

Pour obtenir de bonnes performances d'un GC, le GC doit pouvoir déplacer des objets en mémoire. Dans un langage comme C++ où vous pouvez interagir directement avec les emplacements de mémoire, cela est pratiquement impossible. (Microsoft C++/CLR ne compte pas car il introduit une nouvelle syntaxe pour les pointeurs gérés par GC et est donc effectivement un langage différent.)

Le GC Boehm, bien qu'il soit une idée astucieuse, est en fait le pire des deux mondes: vous avez besoin d'un malloc () qui est plus lent qu'un bon GC, et donc vous perdez le comportement d'allocation/désallocation déterministe sans l'augmentation de performance correspondante d'un GC générationnel . De plus, il est nécessairement conservateur, donc il ne récupérera pas nécessairement toutes vos ordures de toute façon.

Un bon GC bien réglé peut être une bonne chose. Mais dans un langage comme C++, les gains sont minimes et les coûts n'en valent souvent pas la peine.

Il sera intéressant de voir, cependant, que C++ 11 devient plus populaire, si les lambdas et la sémantique de capture commencent à conduire la communauté C++ vers les mêmes types d'allocation et de problèmes de durée de vie des objets qui ont amené la communauté LISP à inventer des GC dans le premier endroit.

Voir aussi ma réponse à une question connexe sur StackOverflow .

26
Daniel Pryden

Comme je peux le voir, les pointeurs intelligents sont largement utilisés dans de nombreux projets C++ du monde réel.

Certes, mais, objectivement, la grande majorité du code est maintenant écrit dans des langages modernes avec des collecteurs de déchets de traçage.

Bien qu'une sorte de pointeurs intelligents soit évidemment bénéfique pour prendre en charge le RAII et les transferts de propriété, il existe également une tendance à utiliser des pointeurs partagés par défaut, comme un moyen de "garbage collection", afin que le programmeur n'ait pas à penser autant à l'allocation. .

C'est une mauvaise idée car vous devez toujours vous soucier des cycles.

Pourquoi les pointeurs partagés sont-ils plus populaires que l'intégration d'un bon ramasse-miettes comme Boehm GC? (Ou êtes-vous d'accord pour dire qu'ils sont plus populaires que les GC réels?)

Oh wow, il y a tellement de choses qui ne vont pas dans votre façon de penser:

  1. Le GC de Boehm n'est pas un GC "propre" dans aucun sens du mot. C'est vraiment horrible. Il est conservateur donc il fuit et est inefficace de par sa conception. Voir: http://flyingfrogblog.blogspot.co.uk/search/label/boehm

  2. Les pointeurs partagés sont, objectivement, loin d'être aussi populaires que GC car la grande majorité des développeurs utilisent maintenant des langages GC'd et n'ont pas besoin de pointeurs partagés. Il suffit de regarder Java et Javascript sur le marché du travail par rapport à C++.

  3. Vous semblez restreindre la considération au C++ parce que, je suppose, vous pensez que GC est un problème tangentiel. Ce n'est pas (la seule façon d'obtenir un GC décent est de concevoir le langage et VM pour lui à partir de au début) donc vous introduisez un biais de sélection. Les gens qui veulent vraiment une bonne collecte des ordures ne collent pas avec C++.

Quelles sont les raisons d'utiliser des pointeurs intelligents de comptage de références?

Vous êtes limité au C++ mais souhaitez avoir une gestion automatique de la mémoire.

4
Jon Harrop

Sous MacOS X et iOS, et avec les développeurs utilisant Objective-C ou Swift, le comptage de références est populaire car il est géré automatiquement et l'utilisation de la récupération de place a considérablement diminué depuis Apple ne prend pas en charge plus (on me dit que les applications utilisant le garbage collection vont se casser dans la prochaine version de MacOS X, et le garbage collection n'a jamais été implémenté dans iOS).

La raison de se débarrasser de la collecte des ordures: Cela n'a jamais fonctionné de manière fiable dans un environnement de style C où les pointeurs pouvaient "s'échapper" vers des zones non accessibles par le garbage collector. Apple croit fermement et croit que le comptage des références est plus rapide. (Vous pouvez faire ici des déclarations sur la vitesse relative, mais personne n'a pu convaincre Apple). Et au final, personne n'a utilisé la collecte des ordures .

La première chose que tout développeur MacOS X ou iOS apprend est de savoir comment gérer les cycles de référence, donc ce n'est pas un problème pour un vrai développeur.

3
gnasher729

Le plus gros inconvénient du garbage collection en C++ est qu'il est tout simplement impossible de bien faire les choses:

  • En C++, les pointeurs ne vivent pas dans leur propre communauté murée, ils sont mélangés avec d'autres données. En tant que tel, vous ne pouvez pas distinguer un pointeur des autres données qui se trouvent juste avoir un motif binaire qui peut être interprété comme un pointeur valide.

    Conséquence: tout ramasse-miettes C++ perdra des objets à collecter.

  • En C++, vous pouvez faire de l'arithmétique des pointeurs pour dériver des pointeurs. En tant que tel, si vous ne trouvez pas de pointeur sur le début d'un bloc, cela ne signifie pas que ce bloc ne peut pas être référencé.

    Conséquence: tout garbage collector C++ doit prendre en compte ces ajustements, en traitant toute séquence de bits qui pointe vers n'importe où dans un bloc, y compris juste après la fin de celui-ci, comme un pointeur valide faisant référence au bloc.

    Remarque: aucun garbage collector C++ ne peut gérer le code avec des astuces comme celles-ci:

    int* array = new int[7];
    array--;    //undefined behavior, but people may be tempted anyway...
    for(int i = 1; i <= 7; i++) array[i] = i;
    

    Certes, cela invoque un comportement non défini. Mais certains codes existants sont plus intelligents que bons, et ils peuvent déclencher une désallocation préliminaire par un garbage collector.