# Le guide rapide pour optimiser les programmes GNOME

Voici une brève introduction à l'optimisation, à la fois le comment et le pourquoi. Les détails des divers outils et techniques sont laissés pour des articles à venir, mais nous donnerons ici un ensemble de trucs et astuces.

## Qu'optimisons nous ?

Quand nous optimisons pour GNOME, voici une première chose dont il faut se souvenir : nous ne tentons pas d'améliorer un programme, nous essayons de mieux satisfaire l'utilisateur.

Meilleur est le programme, plus les gens sont satisfaits, mais il est des améliorations qui les satisfont plus que d'autres : réactivité, temps de démarrage, facilité d'accès aux commandes et le fait de ne pas avoir l'ordinateur qui utilise la mémoire virtuelle dès que plus de deux programmes sont ouverts.

Une optimisation traditionnelle s'attaque à des concepts comme l'utilisation de la CPU, la taille du code, le nombre de clics de souris et la mémoire utilisée par le programme. Cette deuxième énumération est corrélée avec celle de l'alinéa précédent, cependant il y a une différence importante : l'utilisateur de GNOME ne se soucie absolument pas des critères de cette deuxième énumération, mais beaucoup plus de ceux de la première. En optimisant les programmes GNOME, nous réduisons l'utilisation de la CPU, de la mémoire et beaucoup d'autres choses, mais ce sont des aboutissements et non des finalités. Nous optimisons pour les personnes.

## Réalisation d'une optimisation

Le paragraphe précédent a omis de citer un qualifiant important : on ne peut optimiser que ce qui est mesurable. La satisfaction n'est pas quantifiable. Mais, vous pouvez mesurer un temps de démarrage et dire si vous l'avez amélioré. Nous avons quelques espoirs qu'une satisfaction s'ensuive.

L'optimisation est le processus qui consiste à mesurer, affiner et mesurer à nouveau. Donc la première chose à faire est de trouver une mesure de ce que vous optimisez. L'idéal est que cette quantité soit représentée par un chiffre unique, par exemple : le temps nécessaire à la réalisation d'une tâche. Cette mesure constitue le test d'évaluation, elle est l'unique moyen de dire si vous êtes gagnant ou perdant. La différence est énorme entre un programme qui devrait être rapide et un qui l'est.

Une fois que vous avez un point de référence, il faut savoir pourquoi le code ne fonctionne pas aussi bien qu'il le devrait. Il est tentant de le faire par inspection : en regardant simplement le code pour essayer de repérer quelque chose d'améliorable. Vous serez invariablement dans l'erreur. La seule façon d'être sûr est d'utiliser un profileur pour obtenir la décomposition détaillée de ce que votre programme fait réellement.

Habituellement vous cernerez les problèmes dans de petites portions de code. Repérez la pire et centrez votre attention d'abord sur elle. Quand vous avez terminé, relancez le profileur et recommencez. À mesure que vous avancez, les progrès réalisés à chaque étape sont de plus en plus faibles, jusqu'au moment où vous aurez à décider si les résultats sont assez bons. Si vos efforts aboutissent à ne serait-ce que 10 % d'améliorations, alors vous êtes bien au delà du point où vous auriez pu vous arrêter.

Ayez une vue d'ensemble. Par exemple, plutôt que d'essayer de rendre plus rapide un morceau du code, demandez-vous plutôt s'il doit être exécuté. Peut-il être combiné avec une autre partie de code ? Les résultats de calculs antérieurs peuvent-ils être sauvegardés et réutilisés ? Ce code a-t-il même besoin d'être optimisé, s'il est à un endroit où l'utilisateur ne va jamais le parcourir ? Pire encore, le code peut déjà avoir été optimisé et effectuer maintenant des calculs lourds pour éviter d'avoir à les faire plus tard. Un morceau de code ne s'exécute pas isolément, pas plus que le processus d'optimisation.

## Conseils pratiques

### Les Fondamentaux

- Relancez le test de performance après chaque changement apporté au code ; gardez un journal de toutes les modifications en notant la manière dont elles affectent le test de performance. Procéder ainsi vous permet d'annuler les erreurs et vous aide à ne pas les répéter.

- Assurez vous que le code est correct et ne contient pas de bogues avant de l'optimiser. Vérifiez qu'il est toujours correct et sans bogues après optimisation.

- Optimisez au général avant d'optimiser les détails.

- Utilisez le bon algorithme. L'exemple classique des ouvrages est de conseiller l'utilisation d'un tri rapide plutôt qu'un tri à bulle. Il en existe beaucoup d'autres, certains économisent la mémoire, d'autres la CPU. Également, voyez quels sont les raccourcis possibles : vous pouvez faire plus rapide que le tri rapide si vous êtes prêt à quelques compromis.

- L'optimisation est un arbitrage. Mettre en cache des résultats accélère les calculs, mais augmente l'utilisation de la mémoire. Enregistrer les données sur disque économise la mémoire, mais prend plus de temps quand il faut les rappeler.

- Mettez en concurrence les optimisations sur un large éventail d'entrées. À défaut, il est courant de se retrouver avec une portion de code soigneusement optimisée pour un fichier donné et pas les autres.

- Évitez des opérations coûteuses : la multiplication de petites lectures sur les disques, l'utilisation de grosses quantités de mémoire qui nécessitent l'emploi de la mémoire virtuelle, les écritures ou lectures inutiles sur disque dur. Le réseau est également lent. Évitez aussi les opérations graphiques qui ont besoin d'une réponse du serveur X.

### Pièges pour imprudents

- Méfiez-vous des effets collatéraux. Il y a souvent d'étranges interactions entre diverses parties du code, l'accélération d'une partie peut ralentir l'autre.

- Pendant le chronométrage du temps, même sur un système peu chargé, les événements en dehors du programme ajoutent du bruit au résultat. Moyennez-les sur plusieurs exécutions. Si le code est très court, la résolution du chronomètre peut devenir aussi un problème. Dans ce cas, mesurez le temps que l'ordinateur met pour exécuter le code 100 ou 1000 fois. Si le temps que vous enregistrez est plus long que quelques secondes, cela devrait être correct.

- Il est très facile d'être induit en erreur par le profileur. On raconte des histoires de personnes optimisant la boucle d'attente du système d'exploitation parce que c'est là que l'ordinateur passait tout son temps ! N'optimisez pas le code des choses dont l'utilisateur n'a rien à faire.

- N'oubliez pas les ressources sur le serveur X. Le décompte de la mémoire utilisée par votre programme ne comprend pas les images `pixmap` stockées par les processus du serveur X, mais ces images dépensent encore de la mémoire. Utilisez xrestop pour prendre connaissance des ressources utilisées par votre programme.

### Conseils niveau système

- Quand vous optimisez l'utilisation de la mémoire, faites la différence entre usage de crête et usage moyen. Que de la mémoire soit allouée de manière pratiquement permanente est en règle générale mauvais. Que de la mémoire soit allouée sur de brèves périodes est tout à fait acceptable . Des outils comme massif utilisent plutôt le concept espace*temps, produit de la mémoire utilisée multiplié par la durée de son allocation.

- Chronométrez des portions de code simplifiées qui n'exécutent que les choses que vous savez être essentielles ; vous obtiendrez ainsi une limite inférieure absolue du temps que votre code prendra. Par exemple, pour optimiser une boucle, chronométrez la boucle d'attente. Si elle est encore trop longue, les micro-optimisations, aussi nombreuses soient-elles, ne vous seront d'aucun secours : vous devez modifier votre conception. Assurez-vous que le compilateur n'a pas optimisé par ailleurs votre boucle d'attente.

- Déplacez le code en dehors des boucles. Une portion de code légèrement plus complexe exécutée une seule fois est beaucoup plus rapide qu'une portion de code simple exécutée une centaine de fois. Évitez d'appeler trop souvent du code lent.

- Utilisez toutes les astuces possibles avec le compilateur. Servez-vous du mot-clé `const`, de `G_INLINE_FUNC` pour des fonctions courtes fréquemment appelées. Envisagez l'utilisation de `G_GNUC_PURE`, `G_LIKELY` et d'autres diverses macros de `glib`. Utilisez les macros au lieu de mots-clés spécifiques à gcc pour assurer la portabilité.

- N'utilisez pas le langage assembleur. Il n'est pas portable et, quand il est plus rapide sur un processeur, ce n'est pas garanti qu'il le soit sur tous les processeurs qui supportent cette architecture (par ex. Athlon versus Pentium 4).

- Ne réécrivez pas une routine existante de bibliothèque, à moins d'être sûr qu'elle est inutilement lente. Beaucoup de routines de bibliothèques utilisant intensivement la CPU ont déjà été optimisées. Inversement, certaines routines de bibliothèque sont lentes, en particulier celles faisant appel au système d'exploitation.

- Minimisez le nombre de bibliothèques auxquelles vous liez votre code. Moins il y a de bibliothèques à lier, plus le programme se lancera rapidement. Ce n'est pas facile à faire avec GNOME.

### Astuces niveau abstractions

- Utilisez la concomitance. Ce conseil ne vise pas uniquement la simultanéité des processus multiples, mais le fait qu'il faut mettre à profit le temps que l'utilisateur passe à réfléchir à ses actions futures pour anticiper quelques calculs. Faites des opérations en attendant le chargement des données disque. Exploitez la multiplicité des ressources, utilisez-les toutes ensemble.

- Trichez. L'utilisateur doit imaginer un ordinateur rapide, aucune importance qu'il le soit effectivement ou non. L'important est le délai entre commande et réponse ; peu importe que la réponse soit calculée par avance, en cache ou mise au point plus tard à un moment plus opportun pourvu que l'utilisateur obtienne ce qu'il attend.

- Faites quelque chose pendant la boucle d'attente. C'est plus facile à programmer qu'une pleine utilisation de processus multiples, mais faites toujours faire les choses hors de la vue de l'utilisateur. Attention cependant, si vous passez trop de temps dans la boucle d'attente, votre programme deviendra mollasson. Donc, redonnez régulièrement le contrôle à la boucle principale.

- Si tout ce qui précède échoue, avouez à l'utilisateur que le code est lent et programmez une barre de progression. Ils ne seront pas aussi satisfaits que par la présentation des résultats, mais ils sauront au moins que le programme ne s'est pas arrêté brutalement et qu'ils peuvent aller chercher une tasse de café.