Blog<Sam>

copy(MyLife.begin(), MyLife.end(), ostream_iterator<string>(www, “\n”));

Archive pour 'Programmation' Categorie


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…

Publié dans Programmation | Taggé: , , | Aucun commentaire »

Sortie du Features pack pour Visual C++ 2008

Publié par Sam sur 8 avril, 2008

La nouvelle vient d’être annoncée sur le blog de la team Visual C++ : Le fameux Features Pack destiné à Visual Studio 2008, qui était en version Beta depuis plusieurs mois, vient de sortir en version finale. Destiné uniquement aux développeurs C++ (une fois n’est pas coutume, les nouveautés ayant été plutôt concentrées sur le C# ces dernières années), il consiste en une mise à jour majeure des MFC et une implémentation partielle du TR1 (premier Technical Report , sorte de brouillon datant de 2005 de la future norme du C++).

Le Features Pack est gratuit mais ne s’installera pas sur les versions Express. Il peut être téléchargé à partir du site de Microsoft.

Les MFC permettent maintenant, entre autres fonctionnalités, de créer des applications ayant le “look&feel” de Visual Studio, Internet Explorer, et Office. La mise à jour contient en particulier les composants suivants (liste loin d’être exhaustive) :

  • Interfaces ayant l’apparence de Office 2007 (rubans)
  • Interfaces ayant l’apparence de Office XP/2003 (barre d’outils, menus, …)
  • Look Visual Studio avec son système d’ancrage de fenêtres très sophistiqué
  • Support des thèmes Vista
  • Customisation “à la volée” des menus, comme dans Visual Studio
  • Amélioration de la manipulation du système de fichiers via de nouvelles classes

Une description illustrée mais également non-exhaustive des nouveautés des MFC, réalisée il y a plusieurs semaines, est disponible ici sur le blog de la team Visual C++.

En ce qui concerne le TR1, Visual C++ est à mon avis un peu en retard, car GCC propose depuis longtemps sa propre implémentation du TR1, Boost.TR1 existe depuis belle lurette (ce qui est logique puisque la majorité des extensions introduites par le TR1 proviennent de bibliothèques Boost, en premier lieu les pointeurs intelligents), et Dinkumware (fournisseur de Visual C++ justement) l’a implémenté en intégralité depuis fort longtemps. De plus, leur implémentation n’est pas complète, puisqu’il manque la compatibilité avec la dernière norme du C (comme stdint.h, très utile, qui permet d’avoir des types de taille fixe), ainsi que les nouvelles fonctions mathématiques (section 5.2 du TR1, cependant il a finalement été décidé qu’elles ne feraient pas partie du prochain standard, donc ce n’est pas grave).

Pour une liste de questions/réponses à propos de l’implémentation du TR1 dans ce Features Pack, c’est par là que ça passe. Le TR1 lui-même est disponible en intégralité ici (format PDF, 1.42 Mo).

Publié dans Programmation | Taggé: , , , | Aucun commentaire »

C++ : Sortie de Boost 1.35

Publié par Sam sur 6 avril, 2008

boostBoost, la bibliothèque C++ incontournable, n’avait pas été mise à jour depuis juillet 2007. C’est désormais chose faite, et cette nouvelle version majeure apporte de nombreuses nouveautés (listées ici) toutes plus intéressantes les unes que les autres, un vrai bonheur pour tout programmeur C++.

Je l’utilise intensivement, en particulier pour les pointeurs intelligents, tout bonnement indispensables, mais aussi pour les threads, la génération de nombres aléatoires, et bien plus encore. Désormais, nous utilisons cette version dans Exo².

Voici la ligne de commande que j’ai utilisée pour construire Boost 1.35 avec/pour Visual C++ 2008 :

bjam --stagedir=output threading=single,multi link=static,shared runtime-link=static,shared debug release stage --without-wave --without-python

De nombreux avertissements, mais aucune erreur, et aucun problème lors de la compilation et l’édition des liens dans nos projets, ce qui est très appréciable, car Visual C++ 2008 n’est pourtant pas encore officiellement supporté. A propos des fichiers générés, ceux commençant par le préfixe “libboost” sont les bibliothèques statiques, et ceux commençant uniquement par “boost” sont leur équivalent dynamique.

Boost çaybon mangez-en !!!

Publié dans Programmation | Taggé: , , | Aucun commentaire »

IRC dans Visual Studio

Publié par Sam sur 5 avril, 2008

Avec son système d’add-ins, Visual Studio est virtuellement extensible à l’infini, et permet d’avoir un evironnement encore plus intégré qu’à l’origine. Aujourd’hui j’ai découvert un add-in permettant l’intégration d’un channel IRC dans une fenêtre ancrable : Jedi Visual Studio IRC Add-in

Cet outil est limité à un seul channel, à paramêtrer dans les options (qui sont intégrées dans la page d’options générale de Visual Studio), et a beaucoup de limitations, mais c’est suffisant pour surveiller un channel tout en programmant, ce qui évite d’avoir à switcher sans arrêt. On peut tout de même envoyer des messages, envoyer/recevoir des MP, et changer de nom. Personnellement je me connecte deux fois au même channel : une fois dans Visual Studio, et une fois dans mon client IRC habituel (X-Chat 2).

Pour l’adapater à Visual Studio 2008 il suffit d’éditer le fichier .AddIn et de remplacer 8.0 par 9.0.

A quand MSN dans Visual Studio ? Et bien en fait c’est déjà possible, il suffit d’utiliser MSN Web Messenger direcement dans l’explorateur intégré à Visual Studio (qui utilise Internet Explorer), mais l’intérêt est tout de même assez limité :)

Publié dans Programmation | Taggé: , , | Aucun commentaire »

Exo²::Network, mes premiers pas dans la programmation réseau

Publié par Sam sur 6 mars, 2008

PaquetsAh, le réseau… Ma bête noire ! Je hais tout ce qui se rapporte au réseau. Souvent, lorsque quelque chose m’énerve dans un programme, c’est dû au réseau. Dans les jeux particulièrement, avec les problèmes de prédiction et le lag, sans compter les déconnexions. En plus, on ne peut souvent rien faire : soit ça marche, soit ça ne marche pas, mais ce n’est pas en bidouillant votre PC que vous allez avoir 5 de ping à votre jeu online. Cela crée un sentiment d’impuissance extrême, car même si vous avez une machine très puissante, vous ne pouvez rien faire, j’ai bien essayé de secouer les câbles RJ45 pour que les paquets avancent plus vite, mais rien n’y fait.

Niveau programmation aussi, c’est la galère : non seulement les paquets peuvent être perdus, mais en plus ils peuvent arriver dans le désordre. Utiliser TCP résout partiellement ce problème, mais TCP force les paquets à tous arriver et les met forcément dans le bon ordre, cela ralentit considérablement les performances (dans un jeu vidéo si un paquet est perdu on fait avec, on ne s’amuse pas à attendre qu’il soit renvoyé). Il faut donc utiliser UDP, qui souffre de ces problèmes. Les API, de plus, sont très très pauvres, même sous Windows dont les sockets sont basés sur ceux de BSD (on retrouve donc la plupart des fonctions BSD utilisable sous Linux comme Windows, avec quelques légères différences et ajouts). Cela peut paraître simple au début (peu de fonctions), mais en fait, il faut fournir un travail considérable pour écrire un système sécurisé, rapide, fiable, adaptable et souple, d’envoi/réception de paquets en UDP.

HouseEn parlant de sécurité et de fiabilité, si tout le monde ment est un principe important pour Dr. House (merci Raph pour l’analogie), il l’est tout autant pour le codage de la partie serveur : Il ne faut faire aucune supposition sur les clients, penser à tout ce qu’il peuvent faire, imaginer que tous les clients connectés vous en veulent à vous et vos enfants, pour espérer coder un serveur fiable qui ne va pas tomber en miettes à la première attaque ou au premier bug d’un client.

Depuis quelques jours donc, j’ai décidé de coder la partie réseau de mon projet de mini-moteur de jeu, Exo². Le premier problème est de choisir l’API ou la bibliothèque à utiliser. Notre ligne de conduite pour Exo² (nous sommes deux sur le projet, moi et Raph) est de ne rien utiliser qui ressemble à un moteur : uniquement des API (OpenAL, DirectX…), ou des bibliothèques réalisant des fonctions précises importantes, longues et compliquées à coder correctement, pour peu de valeur ajoutée si on l’avait fait nous-même (zlib, boost, …). De plus ces bibliothèques doivent être sous une licence non restrictive permettant de les utiliser dans un projet commercial fermé (licences de type LGPL, BSD, MIT, ZLIB, …), pour qu’on reste libre de passer d’un jour à l’autre en closed-source (pour le moment Exo² est sous GPL).

Pour le réseau, j’ai vite choisi enet : une librairie assez légère qui définit et implémente un protocole de transmission basé sur UDP, permettant entre autres l’envoi et la réception de paquets de manière à la fois fiable et non-fiable (on peut combiner les deux pour une même connection), c’est-à-dire en s’assurant que les paquets arrivent ou pas. Dans tous les cas, ils sont automatiquement fragmentés et rassemblés, et leur réception est assurée d’arriver dans le même ordre qu’à leur envoi. Elle est pas belle la vie ? Utiliser cette bibliothèque me permet de me concentrer sur le moteur réseau proprement dit, sans avoir à coder pendant des mois l’envoi et la réception de paquets UDP pour obtenir un résultat qui aura sans doutes plus de failles que enet.

J’ai décidé de commencer par faire une liste de commandes simples que je souhaite pouvoir réaliser :
- net_createserver pour créer un server
- net_join pour se connecter à un serveur
- net_disconnect pour se déconnecter
- net_kick pour déconnecter un client
- net_clientlist pour obtenir la liste des clients.

Ces objectifs permettent d’obtenir rapidement un début d’architecture réseau :
- CNetwork, la classe centrale du système
- CNetClient, la représentation d’un client coté client
- CNetPeer, la représentation d’un client coté serveur
- CNetServer, la représentation d’un serveur coté serveur

Fidèle depuis peu à certains principe d’eXtreme Programming (en particulier YAGNI), je ne code que ce dont j’ai besoin, c’est pourquoi il manque probablement de nombreuses classes qui verront le jour plus tard, quand j’en aurai besoin.

DoSEn regardant le tutoriel d’enet, cela n’a pas l’air très compliqué, mais en réalité on s’aperçoit vite que c’est largement insuffisant, comme la plupart des tutoriels pour n’importe quel sujet d’ailleurs : ils ne sont pas dans le contexte. Lorsque vous intégrez le code d’un tutoriel dans votre application, vous devez l’adapter au contexte, et vous vous rendez compte qu’il faut souvent faire de grandes modifications. Par exemple leurs demandes de connexions et déconnexions sont synchrones, c’est-à-dire qu’ils bloquent le programme. Bien sûr je n’ai pas envie que le jeu “freeze” à chaque fois qu’il essaie de se connecter à un serveur, et il faut donc que tout soit asynchrone.

Bref, après quelques jours j’ai réussi à avoir quelque chose d’à peu prêt correct qui ne fait aucune supposition sur le client, et permet au serveur d’accepter/refuser des connexions tout en maintenant une liste de clients (avec une limitation paramétrable), et au client de se connecter et se déconnecter. Le seul gros problème que je vois actuellement est l’attaque par déni de service puisque n’importe qui peut rester connecté au serveur et donc facilement atteindre le nombre maximum de clients grâce à des connexions multiples, mais je me pencherai là-dessus beaucoup plus tard.

Le code correspondant à ce que je viens de raconter est disponible sur Codeplex, version 9927 d’Exo², mais j’ai déjà modifié tout ça.

Exo²

Publié dans Programmation | Taggé: , , , , , | Aucun commentaire »

Intégrer l’aide d’autres produits et SDKs dans celle de Visual Studio

Publié par Sam sur 3 mars, 2008

L’aide de Visual Studio se base sur la plateforme d’aide Microsoft Help 2 utilisant le programme Document Explorer. Cette plateforme était à l’origine destinée uniquement à Visual Studio, mais en fait quelques autres produits commencent à l’utiliser comme successeur du bon vieux CHM comme par exemple les derniers IDE Borland.
Là où ça devient intéressant, c’est que l’aide de Visual Studio est en fait un ensemble de collections d’aides, et qu’il est possible d’ajouter à cet ensemble toute collection compatible avec le Document Explorer !

Voici la marche à suivre :
- Dans l’index de l’aide de Visual Studio, rechercher “Visual Studio 2008 Combined Help” (en remplaçant le 2008 par la version utilisée évidemment)
- cocher les produits à intégrer
- relancer Visual Studio

Cela m’a permi d’intégrer l’aide de DirectX à celle de Visual Studio, ainsi que l’aide d’OpenAL.
Maintenant je peux faire “F1″ dans Visual Studio sur une fonction de DirectX et avoir le résultat !

Aide de DirectX intégrée à celle de Visual Studio
Aide de DirectX intégrée à celle de Visual Studio

Publié dans Programmation | Taggé: , , , , , | Aucun commentaire »