samedi 19 septembre 2015

std::make_shared et allocations

J'avais rapidement et indirectement parlé de std::make_shared ici, et en particulier l'optimisation qui consiste à allouer la structure de contrôle avec l'objet lui même, économisant ainsi une allocation. Prouvons donc que la bibliothèque C++ standard fournie avec g++ le fait effectivement. Prenons un programme qui alloue 100 pointeurs partagés:

#include <memory>
#include <vector>

int main()
{
  std::vector<std::shared_ptr<int> > v;

  for(int i = 0; i < 100; ++i)
  {
    //v.push_back(std::make_shared<int>(5));
    v.push_back(std::shared_ptr<int>(new int(5)));
  }
  
  return 0;
}

Faisons tourner la chose avec valgrind. Quand la première ligne à l'intérieur de la boucle est commentée, c'est à dire que l'on alloue un entier, puis qu'on le passe à un pointeur partagé, valgrind nous indique ceci:

==4484== HEAP SUMMARY:
==4484==     in use at exit: 72,704 bytes in 1 blocks
==4484==   total heap usage: 209 allocs, 208 frees, 79,584 bytes allocated

Le chiffre important, ici, c'est les 209 allocations: 100 allocations d'entiers, 100 allocations de structure de contrôle, et j'aurais envie de dire 8 allocations pour le vecteur et 1 allocation en dehors de notre contrôle. Maintenant, commentons la deuxième ligne et décommentons la première. Valgrind nous dit alors:

==4491== HEAP SUMMARY:
==4491==     in use at exit: 72,704 bytes in 1 blocks
==4491==   total heap usage: 109 allocs, 108 frees, 79,184 bytes allocated

Yay! Nous avons effectivement une seule allocation dans la boucle au lieu de 2, le make_shared a donc bien alloué la structure de contrôle avec l'objet. À noter que la taille totale allouée est légèrement plus basse, peut-être grâce à des histoires d'alignement?