1 / 71

Performance des logiciels

Performance des logiciels. Besoins et stratégies (avec des exemples de Steve McConnell « Code Complete ») Vladimir Makarenkov (Université du Québec à Montréal). Nécessité d’avoir des logiciels performants. Applications critiques Applications scientifiques Informatique embarquée

makala
Download Presentation

Performance des logiciels

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Performance des logiciels Besoins et stratégies (avec des exemples de Steve McConnell « Code Complete ») Vladimir Makarenkov (Université du Québec à Montréal)

  2. Nécessité d’avoir des logiciels performants • Applications critiques • Applications scientifiques • Informatique embarquée • Besoin de temps de réponse très courts • Services distants • Avantage concurrentiel • … La performance doit faire partie des spécifications

  3. Ne pas optimiser le code si ce n’est pas nécessaire • Les méthodes d’optimisation manuelles produisent souvent un code peu maintenable et de faible qualité • La recherche d’optimisation demande des ressources de haut niveau • L’optimisation peut introduire des erreurs difficilement retraçables

  4. Facteurs influençant les performances • Choix des algorithmes • Choix des structures de données • Choix du langage et du compilateur • Choix du matériel • Importance des accès mémoire ou E/S • Qualité du code & expertise du programmeur

  5. Principe de Pareto (80 / 20) • Pareto: « 80% des richesses sont possèdes par 20% de la population » (Peut s’appliquer à toute sorte de domaines) • Knuth: moins de 4% du code compte pour plus de 50% du temps d’exécution • Rechercher les portions critiques (« profiling ») • Rechercher les structures et types adaptés aux besoins et moins gourmands en ressources • Modulariser l’application • Facilite le profilage et les modifications locales • Utiliser des compilateurs offrant des options d’optimisation • Optimiser les détails du code (code-tuning)

  6. Règles d’optimisation • 1re règle d’optimisation • Ne rien faire • 2e règle d’optimisation • Ne rien faire • 3e règle d’optimisation (pour experts seulement) • Ne rien faire maintenant • Attendre d’avoir une version finale entièrement opérationnelle.

  7. Optimisation par le compilateur • Souvent suffisant pour atteindre les objectifs • Meilleure que l’optimisation manuelle • L’optimisation par le compilateur peut améliorer les performances de plus de 40% • L’optimisation manuelle se limite à 15 - 30% dans le meilleur des cas. • L’optimisation manuelle peut entrer en conflit avec des options du compilateur • Choisir le compilateur en conséquence

  8. Comparaison des performances de certains compilateurs (en secondes)

  9. Mesures de performance • Permettent de circonscrire les portions de code critiques • Il faut des mesures précises • Il faut mesurer ce qui nous intéresse • Attention aux délais dûs aux OS, des programmes en arrière plan, etc. • Utiliser des outils de profilage • Garder les mesures pour les tests subséquents • Refaire les mesures après chaque modification

  10. Outils d’évaluation • Unix cc –p nom_du_fichier -> a.out -> mon.out (voir avec prof) gcc –pg nom_du_fichier -> a.out -> gmon.out (voir avec gprof) • Windows • ANTS Profiler • JProbe • Gestion du temps du langage • Java classe Date, Time, currentTimeMillis() • C clock, time

  11. Quand optimiser ? • Si la performance ne correspond pas aux attentes ou aux spécifications • Si le gain en vaut la peine • Le temps passé à optimiser ne doit pas être supérieur au gain réalisé pendant toute la durée de vie du programme

  12. Quand optimiser ? (2) • Si la performance apporte une plus value importante au logiciel • Quand le code est finalisé et fonctionnel • Après avoir trouvé les points critiques

  13. Analyse de performances : comment ? • Sur un code complètement implémenté et testé • Sur une version « release » (optimisée par le compilateur …) • Avec des données représentatives • Le cycle d’optimisation:

  14. Sources d’inefficacité classiques • Boucles • Sortir les calculs, tests et opérations qui ne dépendent pas des itérations de la boucle for (i = 0; i < image.with()*image.height(); i++){…} vs int longueur = image.with()*image.height() for (i = 0; i < longueur; i++) {…}

  15. Sources d’inefficacité (2) • Boucles • Ordre des boucles imbriquées • Placez la boucle la plus active à l’intérieur for (i = 0; i < 1000; i++) for(j = 0; j < 10; j++) {…} vs for (j = 0; j < 10; j++) { for(i = 0; i < 1000; i++) {…} }

  16. Sources d’inefficacité (3) • Boucles • Dérouler les boucles for (i = 1; i < 4; i++) { a[i] = 0; } vs a[1] = 0; a[2] = 0; a[3] = 0; • Ordre de parcours de tableaux • Profiter de l’antémémoire (i.e. mémoire cache)

  17. Remarques • L’option d’optimisation par le compilateur peuteffectuer quelques unes des optimisations citées dans les exemples précédents (et suivants), telles que l’identification des invariants de boucles, déroulement de boucles simples ou l’élimination des sous–expressions communes dans une ligne de code. Cependant, si ces modifications n’altèrent pas la lisibilité du code, elles peuvent être faites manuellement. • L’optimisation de la gestion de l’antémémoire (i.e. mémoire cache) dépens de la manière dont le processeur gère la mémoire. C’est une optimisation de bas niveau étroitement liée au matériel.

  18. Sources d’inefficacité (4) • Test • Utiliser les opérateurs court-circuitants • && et || if ((c >= ‘0’) && (c <= ‘9’)) {…} • Traiter les cas usuels et fréquents en premier

  19. Sources d’inefficacité (5) • Calculs • Garder le résultat d’un calcul plutôt que de le refaire x = (sin(y) + 1) / (sin(y) – 1); vs monSin = sin(y); x = (monSin + 1) / (monSin – 1);

  20. Sources d’inefficacité (6) • Calculs • Utiliser des opérations moins coûteuses • Additions vs multiplication • Multiplication par l’inverse vs division • … x = pow(y,2)/2; vs x = (y*y)*0.5;

  21. Sources d’inefficacité (7) • Calculs • Éviter les calculs inutiles • sqrt(x) < sqrt(y) donne le même résultat que x < y • Faire un prétraitement des données avant de faire une opération coûteuse • Effectuer un précalcul des résultats courants • Ex: tableau de sinus / cosinus pour des valeurs courantes

  22. Sources d’inefficacité (8) • Types de données • Utiliser le type de données approprié • Utiliser le type de données le moins gourmand qui répond aux besoins • char < short < int < long < float < double (attention: pas toujours vrai)

  23. Sources d’inefficacité (9) • Types de données • Éviter les nombres à point flottant si ce n’est pas indispensable • Ex: le calcul de coordonnées à l’écran en float n’a pas de sens, 1.234567 pixels = 1 pixel • Faire attention à la gestion des chaînes de caractères • Java et C# créent des instances de classes pour chaque modification d’une chaîne et en font la copie • C parcourt la chaîne au complet pour en calculer sa longueur

  24. Sources d’inefficacité (10) • E/S & accès distants • Préférer le travail sur des données en mémoire, éviter les accès disque, réseaux, bases de données… • Utiliser la mémoire cache • Ex: images dans les navigateurs

  25. Sources d’inefficacité (11) • Erreurs et oublis dans le code • Code de débogage oublié • Libération de la mémoire • Indexation des bases de données • Passage par valeur vs par référence • Ex: les tableaux demandent une copie s’ils sont passés par valeur

  26. Comparaison du temps d’accès sur un tableau de 100 éléments (en secondes) • Accès aléatoire • Accès séquentiel

  27. Procédure d’optimisation • 1) Développer du code maintenable et facile à comprendre • 2) En cas de problèmes de performance • A. Garder une version fonctionnelle du code • B. Profiler l’exécution du système pour trouver les points critiques • C. Déterminer les sources des problèmes. • Sont-ils dûs à une mauvaise architecture, à de mauvais algorithmes, etc.

  28. Procédure d’optimisation (2) • D. Vérifier si le tuning peur apporter une amélioration, sinon garder le code de l’étape 1 • E. Faire le tuning des zones trouvées en C • F. Mesurer chaque modification individuellement • G. Si la modification n’apporte pas de changements significatifs, revenir au code de l’étape A • 3) Répéter l’étape 2 jusqu’à ce que les exigences initiales soient satisfaites

  29. Attention • Ne pas optimiser les prototypes, les tests ou tout code non finalisé • L’utilisation de structures de données adaptées apportent plus de gains que le tuning • La clarté du code doit primer en premier lieu • Commenter les changements. • Risque de « recorriger » lors d’une relecture • Le tuning produit souvent du code peu clair

  30. Attention (2) • « Le mieux est l’ennemi du bien » • Écrire un programme qui répond aux attentes et n’optimiser que les parties critiques • Attention aux mythes • Les recettes de cuisine, les idées préconçues, les vieilles solutions, etc. ne sont pas adaptées au contexte et sont souvent dépassées par les avancées technologiques. • Ne se fier qu’aux tests en situation avec des données représentatives du problème • Testez, testez et retestez

  31. Techniques d’optimisation Code Tuning

  32. Rappels • L’optimisation (tuning) ne touche que des petites portions du code (points critiques) • Les techniques présentées doivent faire l’objet de tests en situation réelle (compilateur, materiel, etc.) • L’optimisation ne doit être faite que sur du code final, s’il ne correspond pas aux spécifications et en dernier recours par rapport à d’autres méthodes

  33. Rappels (2) • Chaque modification doit être testée et mesurée individuellement • Toute modification doit être clairement commentée et expliquée

  34. Exemples de gains sur certaines opérations • Sur des int (compilateur C, C++, source Kernighan et Pike 1999, en nanosecondes) • À tester sur votre configuration

  35. Arrêter de tester si on connaît la réponse Exemple de recherche inutile: for(i=0; i<taille; i++){ if (entree[i] < 0){ entreeNeg = true; break; } } Utiliser des opérateurs court-circuitants si possible (sinon 2 tests séparés) if ((5 < x) && (x<10)) {…} if (5 < x) if (x < 10) {…} Optimisation des opérations logiques

  36. Optimisation des opérations logiques (2) switch(entree) { case ‘+’: case ‘=‘ : {…} case ‘0’:… case ‘9‘: {…} case ‘,’: case ‘?‘:…:{…} case ‘A’:… case ‘Z‘: {…} … } switch(entree) { case ‘A’:… case ‘Z‘: {…} case ‘,’: case ‘?‘:…:{…} case ‘0’:… case ‘9‘: {…} case ‘+’: case ‘=‘ : {…} … } • Ordonner les tests par leur fréquence

  37. Optimisation des opérations logiques (3) • Ordonner les tests par leur fréquence

  38. Comparaison du if et du case selon différents langages

  39. Optimisation des opérations logiques (4) • Substituer des expressions logiques compliquées par des tables de valeurs • Utiliser des transformations logiques pour minimiser les opérations (INF 1130)

  40. Optimisation des opérations logiques (5) • Utiliser l’évaluation paresseuse • Évaluer les expressions le plus près possible de leur utilisation • Garder les résultats en mémoire si on doit les utiliser plusieurs fois • Utiliser des langages adaptés (ex: Haskell, Prolog, etc.)

  41. A B 1 1 2 1 2 2 3 0 C Optimisation des opérations logiques (6) if (( a && !c)||(a && b && c)) { category = 1;} else if (( b && !a)||(a && c && !b)) { category = 2;} else if ((c && !a && !b)) { category = 3;} else { category = 0;} • La catégorie de l’objet est définie selon son appartenance à un ou plusieurs des 3 groupes (voir le schéma à droite)

  42. Optimisation des opérations logiques (7) // définit categoryTable static int categoryTable [2][2][2] = { // !b!c !bc b!c bc 0, 3, 2, 2, // !a 1, 2, 1, 1 // a }; … category = categoryTable[a][b][c]; • Remplacer les expressions compliquées par des tableaux

  43. Optimisation des boucles • Ce sont des sources importantes de gain (ou perte) de performances • Unswitching • Faire les tests qui ne dépendent pas de la boucle à l’extérieur (Attention, donne parfois du mauvais code)

  44. Optimisation des boucles (2) for (i = 0; i < count; i++) { if (sumType == SUMTYPE_NET) { netSum = netSum + amount[i]; } else { grossSum = grossSum + amount[i]; } }

  45. Optimisation des boucles (3) if (sumType == SUMTYPE_NET) { for (i = 0; i < count; i++) { netSum = netSum + amount[i]; } } else { for (i = 0; i < count; i++) { grossSum = grossSum + amount[i]; } }

  46. Optimisation des boucles (4)

  47. Optimisation des boucles (5) for(i=0; i < nbEmployes; i++){ nomEmploye[i] = “”; } … for(i=0; i < nbEmployes; i++){ salaireEmploye[i] = 0; } for(i=0; i < nbEmployes; i++){ nomEmploye[i] = “”; salaireEmploye[i] = 0; } • Fusion de boucles • Regrouper les portions de code qui travaillent sur le même ensemble d’éléments

  48. Optimisation des boucles (6) • Fusion de boucles

  49. Optimisation des boucles (7) i = 0; while (i < count) { a[i] = i; i = i + 1; } i = 0; while (i < count - 2) { a[i] = i; a[i + 1] = i + 1; a[i + 2] = i + 2; i = i + 3; } if ( i <= count - 1) { a[count - 1] = count - 1; } if ( i == count - 2) { a[count - 2] = count - 2; } • Déroulement

  50. Optimisation des boucles (8) • Déroulement

More Related