vendredi 3 juin 2011

Valgrind massif - Mémoire un peu trop vive

Je me suis mis à utiliser massif de la suite Valgrind, destiné à mesurer l'utilisation mémoire d'un programme au cours du temps, plus spécifiquement du tas (c'est à dire de la mémoire allouée via malloc/new).

L'outil est très bien tourné: il suffit de compiler son programme avec -g, et de le faire tourner sous massif. Un petit rapport est généré, contenant un certain nombre de snapshots avec le détail de l'utilisation mémoire, et la liste des appels l'ayant générée.

C'est là que l'on retrouve nos bonnes vieilles structures de données: les std::set et les std::map sont des structures tout à fait épatantes, mais l'overhead est significatif. L'on retrouvera ainsi quelles structures bénéficieraient d'une transformation vers un std::deque trié, par exemple.

C'est également une bonne manière de trouver les buffers qui grandissent perpétuellement. Un bout de code qui lirait les paquets venus du réseau pourrait par exemple être implémenté avec un vecteur effectuant des resize() pour redimensionner à la taille du paquet. Mais si resize() peut augmenter la capacité si besoin, il ne la réduit jamais! La seule manière de réduire proprement un vecteur, c'est de l'échanger avec un vecteur tout neuf. Démonstration:


#include <iostream>
#include <vector>

int main()
{
std::vector<int> a;

std::cout << "size\tcapacity\n";
std::cout << a.size() << "\t" << a.capacity() << "\n";

a.resize(10000);

std::cout << a.size() << "\t" << a.capacity() << "\n";

a.resize(1);

std::cout << a.size() << "\t" << a.capacity() << "\n";

std::vector<int> b(a.begin(), a.end());
std::swap(b, a);

std::cout << a.size() << "\t" << a.capacity() << "\n";

std::cout.flush();

return 0;
}

À l'exécution:

size capacity
0 0
10000 10000
1 10000
1 1

Aucun commentaire: