Bug : 900 Mo de RAM utilisée en trop à cause d’un seul caractère
Publié par Sam sur 9 mai, 2008
Ceci est une photo d’un concert de Jean-Michel Jarre. Rapport avec la programmation ? Aucun, mais j’écoute son album Chronologie en ce moment même. J’en écoute aussi quand je programme, de temps en temps, entre autre musiques électroniques. Et récemment j’ai résolu un bug dans Exo² qui vaudrait une composition musicale tellement il est étonnant. La phase de codage peut être très créative et intéressante, mais la phase de debug est aussi très riche, et demande d’être très perspicace. Quelquefois, elle est même amusante, du moins quand on a fini par trouver que le bug qui nous ennuyait tant venait d’une erreur farfelue. Je me souviens avoir passé plus de 2 heures avec un pote un soir à essayer de comprendre pourquoi notre programme Eiffel plantait, et la raison en était l’équivalent moral d’un oubli de “++i” dans une boucle. Erreur décelable en 5 secondes avec Visual C++, mais l’Eiffel ne nous offrait aucun outil puissant à l’époque, ou alors on ne les connaissait pas. Cette soirée à faire les cent pas dans la salle de TD de notre école et à se prendre littéralement la tête sur ce problème mystique nous aura définitivement dégouté de ce langage en moins de temps qu’il nous en avait fallu pour l’apprendre…
Mais voici le fameux bug qui vaut un article : Raph faisait depuis quelques temps l’ébauche de menu de notre futur mini-jeu basé sur notre mini-moteur Exo² (je vous rassure, tout n’est pas mini chez nous), quand il a décidé de ma montrer à quoi ça ressemblait. Mais avant, petite remarque pleine de désinvolture : “Tu feras gaffe ça mets plus de 20 secondes à se lancer”. Gné ? C’est quoi ce délire ? J’observe le gestionnaire des tâches : OMFG, il grimpe à une vitesse folle !! Arrivé à 950 Mo d’utilisation de mémoire, le programme termine enfin son chargement, et se prépare donc à être sévèrement passé à la moulinette. Quelques lancements en pas à pas plus tard, je me suis aperçu que chaque création d’un composant formant le menu prenait environ une demi-seconde et une augmentation de plusieurs Mo de RAM utilisée. F7, F7, F7, F7… Oh, le gestionnaire de ressources recharge plusieurs fois la même ressource ! [Mode C3PO]Voilà qui est pour le moins… Etrange[/Mode C3PO]. F7, F7… Bingo ! Les coupables étaient devant mes yeux et ne pouvaient plus se cacher :
shared_ptr p;
for (std::list<shared_ptr<IResource> >::iterator it = res->second.Resources.begin(); p != NULL && it != res->second.Resources.end(); ++it)
{
p = boost::dynamic_pointer_cast<T>(*it);
}
Mon système de ressources peut gérer plusieurs types de ressources ayant le même nom (je ne suis d’ailleurs plus très sûr que ce soit un avantage), et ce code a pour but de parcourir toutes les ressources ayant déjà été chargées avec le même nom, en essayant de convertir chacune d’elle vers le type désiré, afin de ne pas avoir à le recharger en mémoire (en général, cette boucle n’effectue qu’une seule itération car il est rare d’avoir deux ressources différentes sur le même nom). Sauf que c’est censé continuer “tant que le pointeur est nul”, autrement dit tant que p == NULL. Test inversé par mégarde, donc ressources constamment rechargées au lieu d’être partagées, en particulier les polices de caractères servant à l’affichage des composants du menu. Voilà pourquoi en remplaçant p != NULL par p == NULL, c’est-à-dire en ne changeant qu’un seul caractère, j’ai gagné 900 Mo de RAM…
Effet papillon…
