Il y a énormément de choses qu’un développeur doit se souvenir en développant. C’est loin d’être un exercice de tout repos. Respect des normes de programmation, mémorisation des bonnes pratiques de développement, connaissance de son framework. J’en passe. En même temps, c’est aussi ce qui fait la différence entre du code de qualité de celui qui ne l’est pas.
Ce qui est pernicieux avec la programmation est dans les choses que nous ne pouvons voir. Dans celles qu’il faut s’imaginer. L’effort dans l’action de la programmation réside principalement dans l’action cognitive de visualiser l’exécution du code qui est sous nos yeux. Là où ça se complique, c’est lorsqu’il y a des abstractions. Il est excessivement difficile de visualiser des abstractions. En supplément, plus il y en a, plus c’est compliqué à y voir clair.
Dernièrement, j’ai passé pas mal de temps à optimiser le temps de chargement d’une application. Dans cet exercice, j’y ai fait quelques constatations sur la façon d’intégrer quelques pratiques de programmation.
Avant tout, les métriques
Une boucle for au lieu d’un ForEach. Une cache par là. Mais qu’en est-il vraiment? Tenter d’optimiser son application sans avoir des données sur la performance de son code est un coup d’épée dans l’eau. Vraiment, c’est comme aller à la pêche sans hameçon.
Utilisez un outil comme dotTrace ou Ants afin d’être en mesure d’avoir visuellement une idée de ce qui se passe avec votre code. Sans cela, le reste ne vaut pas grand-chose. Avec ces outils, vous pouvez voir quelles sections de votre code sont exécutées, à quelle fréquence et leur temps d’exécution.
Comme dirait l’autre. On veut pas le savoir, on veut le voir!
Le volume et la fréquence
En ce qui concerne la performance, tout est une question de volume de données. C’est une perte de temps d’aborder le sujet d’optimisations si vous gérez un petit nombre de données. Tenter d’optimiser une recherche dans un tableau de dix éléments est un peu ridicule. L’intérêt de la notion de complexité d’algorithme varie proportionnellement selon le volume de données que vous avez à gérer.
Pour faire simple, plus une structure de données est utilisée fréquemment pour y faire des accès, plus l’intérêt pour l’optimiser est grand.
Base de données
Une des premières choses que j’ai l’habitude de regarder avec la performance c’est l’accès à la base de données. En général, accéder à la base de données c’est long et sujet à de la latence. Une situation que j’ai eu à optimiser est des appels à la base de données à l’intérieur d’une boucle. Des centaines de petites requêtes étaient exécutées unitairement. Pas cool.
Le calcul est très simple. Supposez que chaque appel à votre base de données nécessite 5 ms. Forcément, votre temps d’attente sera, au minimum, de 5 ms multiplié par votre nombre d’éléments à boucler. Au lieu de boucler un accès à la base de données, tentez de faire des opérations en lots. Soit l’équivalent d’une clause IN en SQL.
À ce sujet, il faut aussi être stratégique avec l’utilisation de la méthode .ToList() et l’invoquer le moins souvent possible. Par exemple, avec Entity Framework, l’utilisation de .ToList() signifie généralement l’instant où vous allez exécuter une requête à la base de données. Il faut éviter le plus possible des appels comme employees.Where(p=>p.Id < 3).ToList().Where(p=>p.Age>4).OrderBy(p=>p.Name).
Collections
Un dernier phénomène que j’ai eu l’occasion de revoir est la recherche d’éléments avec clé dans une liste. Par exemple, une liste d’employés où les éléments de la liste sont, à tous les coups, récupérés par leur identifiant d’employés.
À mon avis, la règle est simple, si la principale façon d’accéder aux éléments d’une liste ou un tableau est par une clé unique, forcément la liste doit être remplacée par un dictionnaire.
Par contre, l’utilisation d’un dictionnaire au lieu d’une liste n’est pas une solution miracle. Il faut être très bien conscient du contexte dans lequel utiliser un dictionnaire. La principale mise en garde est avec LINQ. Si vous utilisez LINQ avec un dictionnaire, sachez que vous perdez le principal intérêt de celui-ci. C’est-à-dire l’indexation.
Avec un dictionnaire, une simple condition LINQ .Where() fera en sorte que vous itérerez sur la totalité des clés. Soyez prudent.