Stratégies d’optimisation des performances de dernier recours

Il y a déjà beaucoup de questions de performances sur ce site, mais il me semble que presque toutes sont très spécifiques à un problème et assez étroites. Et presque tous répètent le conseil pour éviter une optimisation prématurée.

Assumons:

  • le code fonctionne déjà correctement
  • les algorithmes choisis sont déjà optimaux pour les circonstances du problème
  • le code a été mesuré et les routines incriminées ont été isolées
  • toutes les tentatives d’optimisation seront également mesurées pour s’assurer qu’elles n’empirent pas

Ce que je recherche ici, ce sont des stratégies et des astuces pour obtenir les derniers pourcentages dans un algorithme critique quand il ne rest plus qu’à faire quoi que ce soit.

Dans l’idéal, essayez de faire en sorte que les réponses ne soient pas liées à la langue et indiquez les aspects négatifs des stratégies suggérées, le cas échéant.

J’appendai une réponse avec mes propres suggestions initiales et j’attends avec impatience tout ce que la communauté Stack Overflow peut penser.

OK, vous définissez le problème là où il semblerait qu’il n’y ait pas beaucoup de place à l’amélioration. C’est assez rare, selon mon expérience. J’ai essayé d’expliquer cela dans un article du Dr. Dobbs en novembre 1993, en partant d’un programme non sortingvial conventionnel et bien conçu, sans perte évidente, et en le faisant passer par une série d’optimisations jusqu’à ce que son temps soit réduit de 48 heures. secondes à 1,1 seconde et la taille du code source a été réduite d’un facteur de 4. Mon outil de diagnostic était le suivant . La séquence de changements était la suivante:

  • Le premier problème rencontré était l’utilisation de grappes de listes (maintenant appelées “iterators” et “classes de conteneur”) représentant plus de la moitié du temps. Celles-ci ont été remplacées par un code assez simple, ce qui ramène le temps à 20 secondes.

  • Maintenant, le plus grand preneur de temps est la création de listes. En pourcentage, ce n’était pas si grand avant, mais maintenant c’est parce que le plus gros problème a été supprimé. Je trouve un moyen de l’accélérer et le temps passe à 17 secondes.

  • Maintenant, il est plus difficile de trouver des coupables évidents, mais il y en a quelques-uns que je peux faire quelque chose, et le temps passe à 13 secondes.

Maintenant, je semble avoir frappé un mur. Les échantillons me disent exactement ce que ça fait, mais je n’arrive pas à trouver quoi que ce soit que je puisse améliorer. Ensuite, je réfléchis à la conception de base du programme, à sa structure pilotée par les transactions, et je demande si toutes les recherches de liste effectuées sont réellement imposées par les exigences du problème.

Ensuite, je me suis lancé dans une nouvelle conception, où le code du programme est réellement généré (via des macros de préprocesseur) à partir d’un ensemble de sources plus petit, et dans lequel le programme ne détermine pas constamment des choses que le programmeur sait assez prévisibles. En d’autres termes, n’interprétez pas la séquence des choses à faire, comstackz-la.

  • Cette nouvelle conception est effectuée en réduisant le code source d’un facteur de 4 et le temps est réduit à 10 secondes.

Maintenant, parce que ça devient tellement rapide, c’est difficile à échantillonner, alors je lui donne 10 fois plus de travail, mais les temps suivants sont basés sur la charge de travail d’origine.

  • Un diagnostic plus approfondi révèle qu’il passe du temps dans la gestion des files d’attente. Ces modifications réduisent le temps à 7 secondes.

  • L’impression diagnostique que j’avais faite était devenue un facteur important. Rincez cela – 4 secondes.

  • Maintenant, les plus grands preneurs de temps sont les appels à malloc et gratuits . Recycler des objects – 2,6 secondes.

  • En continuant à échantillonner, je trouve toujours des opérations qui ne sont pas ssortingctement nécessaires – 1,1 seconde.

Facteur d’accélération total: 43,6

Maintenant, il n’y a pas deux programmes identiques, mais dans les logiciels non-jouets, j’ai toujours vu une progression comme celle-ci. D’abord, vous obtenez les trucs faciles, et puis le plus difficile, jusqu’à ce que vous atteignez un sharepoint rendement décroissant. La perspicacité que vous gagnez pourrait bien mener à une nouvelle conception, entamant une nouvelle série d’accélérations, jusqu’à ce que vous atteigniez à nouveau des rendements décroissants. Maintenant, c’est le point auquel il pourrait être judicieux de se demander si ++i ou i++ ou for(;;) ou while(1) sont plus rapides: le genre de questions que je vois si souvent sur SO.

PS On peut se demander pourquoi je n’ai pas utilisé de profileur. La réponse est que presque chacun de ces «problèmes» était un site d’appel de fonction, dont les échantillons de stack étaient précis. Les profileurs, même aujourd’hui, viennent à peine de penser que les instructions et les instructions d’appel sont plus importantes à localiser et plus faciles à corriger que les fonctions complètes. J’ai en fait construit un profileur pour faire cela, mais pour une réelle intimité avec ce que le code fait, il n’y a pas de substitut pour bien mettre les doigts dessus. Ce n’est pas un problème que le nombre d’échantillons soit petit, car aucun des problèmes rencontrés n’est si minime qu’il est facile de les rater.

ADDED: jerryjvl a demandé quelques exemples. Voici le premier problème. Il consiste en un petit nombre de lignes de code distinctes, prenant ensemble plus de la moitié du temps:

  /* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */ if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){ . . . /* FOR EACH OPERATION REQUEST */ for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){ . . . /* GET CURRENT TASK */ ptask = ILST_NTH(ptop->tasklist, ptop->current_task) 

Ils utilisaient le cluster de listes ILST (similaire à une classe de liste). Ils sont mis en œuvre de la manière habituelle, avec “dissimulation d’informations”, ce qui signifie que les utilisateurs de la classe n’étaient pas censés se soucier de la manière dont ils étaient mis en œuvre. Lorsque ces lignes ont été écrites (sur environ 800 lignes de code), on n’a pas pensé à l’idée que ces lignes pourraient constituer un «goulot d’étranglement» (je déteste ce mot). Ils sont simplement la manière recommandée de faire les choses. Il est facile de dire, après coup, que cela aurait dû être évité, mais dans mon expérience, tous les problèmes de performance sont comme ça. En général, il est bon d’essayer d’éviter de créer des problèmes de performance. Il est même préférable de trouver et de corriger ceux qui ont été créés, même s’ils “auraient dû être évités” (avec le recul). J’espère que cela donne un peu de la saveur.

Voici le deuxième problème, en deux lignes distinctes:

  /* ADD TASK TO TASK LIST */ ILST_APPEND(ptop->tasklist, ptask) . . . /* ADD TRANSACTION TO TRANSACTION QUEUE */ ILST_APPEND(trnque, ptrn) 

Ce sont des listes de construction en ajoutant des éléments à leurs fins. (La solution consistait à collecter les éléments dans des tableaux et à créer les listes en une seule fois.) Ce qui est intéressant, c’est que ces déclarations ne coûtent que 3/48 de l’heure d’origine, c’est-à-dire fait un gros problème au début . Cependant, après avoir éliminé le premier problème, ils ont coûté 3/20 du temps et sont maintenant devenus des “poissons plus gros”. En général, c’est comme ça que ça se passe.

J’appendai que ce projet a été réalisé à partir d’un projet réel sur lequel j’ai aidé. Dans ce projet, les problèmes de performance étaient beaucoup plus dramatiques (tout comme les accélérations), comme l’appel d’une routine d’access à la firebase database dans une boucle interne pour voir si une tâche était terminée.

Référence ajoutée: Le code source, original et repensé, se trouve sur http://www.ddj.com , pour 1993, dans le fichier 9311.zip, les fichiers slug.asc et slug.zip.

EDIT 2011/11/26: Il existe maintenant un projet sourceforge contenant le code source dans Visual C ++ et une description détaillée de la façon dont il a été réglé. Il ne traverse que la première partie du scénario décrit ci-dessus, et ne suit pas exactement la même séquence, mais obtient tout de même une accélération de l’ordre de 2 à 3.

Suggestions:

  • Pré-calculer plutôt que recalculer : toutes les boucles ou tous les appels répétés contenant des calculs ayant une plage d’entrées relativement limitée, envisagez de faire une recherche (tableau ou dictionnaire) contenant le résultat de ce calcul pour toutes les valeurs de la plage valide. consortingbutions. Ensuite, utilisez plutôt une simple recherche dans l’algorithme.
    Inconvénients : si peu de valeurs pré-calculées sont réellement utilisées, cela peut aggraver les choses. De plus, la recherche peut prendre beaucoup de mémoire.
  • N’utilisez pas de méthodes de bibliothèque : la plupart des bibliothèques doivent être écrites pour fonctionner correctement dans un large éventail de scénarios, et effectuer des vérifications nulles sur les parameters, etc. En ré-implémentant une méthode, vous pouvez supprimer beaucoup de logique ne s’applique pas dans les circonstances exactes dans lesquelles vous l’utilisez.
    Vers le bas : écrire du code supplémentaire signifie plus de surface pour les bogues.
  • Utilisez des méthodes de bibliothèque : pour me contredire, les bibliothèques de langues sont écrites par des personnes beaucoup plus intelligentes que vous ou moi; les chances sont qu’ils l’ont fait mieux et plus vite. Ne l’implémentez pas vous-même, sauf si vous pouvez le rendre plus rapide (par exemple: toujours mesurer!)
  • Triche : dans certains cas, même si un calcul exact peut exister pour votre problème, vous n’avez peut-être pas besoin d ‘«exact», parfois une approximation peut être «assez bonne» et beaucoup plus rapide dans la transaction. Demandez-vous, est-ce que cela a vraiment de l’importance si la réponse est de 1%? 5%? même 10%?
    Vers le bas : Eh bien … la réponse ne sera pas exacte.

Si vous ne pouvez plus améliorer les performances, voyez si vous pouvez améliorer les performances perçues .

Vous ne pourrez peut-être pas rendre votre algorithme fooCalc plus rapide, mais il existe souvent des moyens pour que votre application semble plus sensible à l’utilisateur.

Quelques exemples:

  • anticiper ce que l’utilisateur va demander et commencer à travailler dessus avant cela
  • afficher les résultats à mesure qu’ils entrent, au lieu de tout à la fois à la fin
  • Compteur de progression précis

Cela ne rendra pas votre programme plus rapide, mais cela rendra vos utilisateurs plus heureux avec la vitesse dont vous disposez.

Je passe la majeure partie de ma vie à cet endroit. Les grandes lignes permettent d’exécuter votre profileur et de le faire enregistrer:

  • Cache manque . Le cache de données est la principale source de blocage dans la plupart des programmes. Améliorez le taux de réussite du cache en réorganisant les structures de données incriminées pour obtenir une meilleure localisation; compresser les structures et les types numériques pour éliminer les octets gaspillés (et donc les récupérations de cache gaspillées); pré-récupérer les données dans la mesure du possible pour réduire les blocages.
  • Load-hit-stores . Les hypothèses du compilateur sur l’aliasing du pointeur et les cas où les données sont déplacées entre les ensembles de registres déconnectés via la mémoire peuvent provoquer un certain comportement pathologique qui entraîne l’effacement du pipeline du processeur entier sur une charge. Trouvez des endroits où les flottants, les vecteurs et les ints sont jetés les uns aux autres et les éliminent. Utilisez __ressortingct libéralement pour promettre le compilateur à propos de l’aliasing.
  • Opérations microcodées . La plupart des processeurs ont des opérations qui ne peuvent pas être mises en pipeline, mais exécutent plutôt une minuscule sous-routine stockée dans la ROM. Les exemples sur le PowerPC sont la multiplication par nombres entiers, la division et le décalage par variable. Le problème est que le pipeline entier s’arrête lorsque cette opération est en cours d’exécution. Essayez d’éliminer l’utilisation de ces opérations ou, au moins, de les répartir dans leurs opérations de pipeline constituées afin que vous puissiez bénéficier d’une répartition superscalaire sur tout le rest de votre programme.
  • Prédictions de twig . Celles-ci vident aussi le pipeline. Trouvez des cas où le processeur passe beaucoup de temps à recharger le tuyau après une twig, et utilisez les indications de twig si elles sont disponibles pour le faire prédire plus souvent. Ou mieux encore, remplacez les twigs par des mouvements conditionnels chaque fois que cela est possible, en particulier après les opérations en virgule flottante car leur canal est généralement plus profond et la lecture des indicateurs de condition après fcmp peut provoquer un blocage.
  • Opérations séquentielles à virgule flottante . Faites ces SIMD.

Et encore une chose que j’aime faire:

  • Définissez votre compilateur pour qu’il affiche les listes d’assemblages et examinez ce qu’il émet pour les fonctions de point d’access dans votre code. Toutes ces optimisations intelligentes qu’un “bon compilateur devrait pouvoir faire automatiquement pour vous”? Les chances sont que votre compilateur réel ne les fait pas. J’ai vu GCC émettre vraiment du code WTF.

Jetez plus de matériel dessus!

Plus de suggestions:

  • Évitez les E / S : toute E / S (disque, réseau, ports, etc.) sera toujours beaucoup plus lente que tout code effectuant des calculs. Supprimez donc les E / S dont vous n’avez pas absolument besoin.

  • Déplacer les E / S en amont : Chargez toutes les données dont vous avez besoin pour un calcul initial afin de ne pas avoir à attendre des E / S répétitives dans le cœur d’un algorithme critique (et peut-être en conséquence de répéter disque recherche, lors du chargement de toutes les données en un seul coup peut éviter de chercher).

  • Retarder les E / S : N’écrivez pas vos résultats tant que le calcul n’est pas terminé, stockez-les dans une structure de données, puis videz-les en une fois à la fin du travail.

  • Filetage I / O : Pour ceux qui ont le courage de combiner le ‘I / O up-front’ ou ‘Delay I / O’ avec le calcul réel en déplaçant le chargement dans un thread parallèle, vous pouvez travailler pendant que vous chargez plus de données sur un calcul sur les données que vous avez déjà, ou pendant que vous calculez le lot de données suivant, vous pouvez écrire simultanément les résultats du dernier lot.

Étant donné que la plupart des problèmes de performances impliquent des problèmes de firebase database, je vais vous donner quelques informations spécifiques à prendre en compte lors du réglage des requêtes et des procédures stockées.

Évitez les curseurs dans la plupart des bases de données. Évitez de boucler aussi. La plupart du temps, l’access aux données doit être basé sur les ensembles et non sur l’enregistrement. Cela inclut de ne pas réutiliser une seule procédure stockée d’enregistrement lorsque vous souhaitez insérer 1 000 000 d’enregistrements à la fois.

N’utilisez jamais select *, ne renvoyez que les champs dont vous avez réellement besoin. Cela est particulièrement vrai s’il y a des jointures car les champs de jointure seront répétés et provoqueront ainsi une charge inutile sur le serveur et sur le réseau.

Évitez d’utiliser des sous-requêtes corrélées. Utilisez des jointures (y compris des jointures à des tables dérivées lorsque cela est possible) (je sais que cela est vrai pour Microsoft SQL Server, mais testez les conseils lorsque vous utilisez un backend différent).

Index, index, index. Et obtenez ces statistiques mises à jour si applicable à votre firebase database.

Rendez la requête sargable . Cela signifie éviter les choses qui rendent impossible l’utilisation des index, comme l’utilisation d’un caractère générique dans le premier caractère d’une clause like ou d’une fonction dans la jointure ou la partie gauche d’une instruction where.

Utilisez des types de données corrects. Il est plus rapide d’effectuer des calculs de date sur un champ de date que d’essayer de convertir un type de données chaîne en un type de données date, puis d’effectuer le calcul.

Ne jamais mettre une boucle d’aucune sorte dans un déclencheur!

La plupart des bases de données ont un moyen de vérifier comment l’exécution de la requête sera effectuée. Dans Microsoft SQL Server, cela s’appelle un plan d’exécution. Cochez les premiers pour voir où se trouvent les zones problématiques.

Examinez la fréquence d’exécution de la requête ainsi que le temps d’exécution nécessaire pour déterminer ce qui doit être optimisé. Parfois, vous pouvez obtenir plus de performances, d’un simple ajustement à une requête qui s’exécute des millions de fois par jour, qu’en effaçant une requête long_running qui ne s’exécute qu’une fois par mois.

Utilisez un outil de profileur pour savoir ce qui est réellement envoyé vers et depuis la firebase database. Je peux me rappeler une fois dans le passé où nous ne pouvions pas comprendre pourquoi la page était si lente à charger lorsque la procédure stockée était rapide et à travers le profilage que la page Web demandait la requête plusieurs fois au lieu d’une fois.

Le profileur vous aidera également à trouver qui bloque qui. Certaines requêtes qui s’exécutent rapidement lors de l’exécution seule peuvent devenir très lentes en raison de verrous provenant d’autres requêtes.

Le facteur limitant le plus important aujourd’hui est la bande passante limitée . Les multicores ne font qu’empirer les choses, car la bande passante est partagée entre les cœurs. En outre, la zone de puce limitée consacrée à la mise en œuvre des caches est également divisée entre les cœurs et les threads, aggravant encore ce problème. Enfin, la signalisation inter-puces nécessaire à la cohérence des différents caches augmente également avec le nombre croissant de cœurs. Cela ajoute également une pénalité.

Ce sont les effets que vous devez gérer. Parfois, par micro-gestion de votre code, mais parfois par une reflection et une refactorisation minutieuses.

Beaucoup de commentaires mentionnent déjà le code convivial du cache. Il y a au moins deux saveurs distinctes de ceci:

  • Évitez les latences de récupération de mémoire.
  • Baisse de la pression du bus mémoire (bande passante).

Le premier problème concerne spécifiquement la régularité de vos modèles d’access aux données, ce qui permet au pré-parsingur matériel de fonctionner efficacement. Évitez l’allocation dynamic de mémoire qui répartit vos objects de données en mémoire. Utilisez des conteneurs linéaires au lieu de listes liées, de hachages et d’arbres.

Le deuxième problème concerne l’amélioration de la réutilisation des données. Modifiez vos algorithmes pour travailler sur des sous-ensembles de vos données qui correspondent au cache disponible, et réutilisez ces données autant que possible pendant qu’il est encore dans le cache.

Le regroupement des données et le fait que vous utilisez toutes les données contenues dans les lignes de cache dans les boucles dynamics vous aideront à éviter ces autres effets et à adapter davantage de données utiles dans le cache.

  • Quel matériel utilisez-vous? Pouvez-vous utiliser des optimisations spécifiques à la plate-forme (comme la vectorisation)?
  • Pouvez-vous obtenir un meilleur compilateur? Par exemple, passer de GCC à Intel?
  • Pouvez-vous faire fonctionner votre algorithme en parallèle?
  • Pouvez-vous réduire les erreurs de cache en réorganisant les données?
  • Pouvez-vous désactiver les assertions?
  • Micro-optimiser pour votre compilateur et votre plateforme. Dans le style de “at an if / else, mettez d’abord la déclaration la plus commune”
  • Routines en ligne (élimination des appels / retours et des parameters)
  • Essayez d’éliminer les tests / commutateurs avec des recherches de tableaux (s’ils sont plus rapides)
  • Déroulez les boucles (périphérique de Duff) au point où elles entrent simplement dans le cache du processeur
  • Localiser l’access à la mémoire pour ne pas faire sauter votre cache
  • Localiser les calculs associés si l’optimiseur ne le fait pas déjà
  • Éliminez les invariants de boucle si l’optimiseur ne le fait pas déjà

Vous devriez probablement considérer la “perspective Google”, c’est-à-dire déterminer comment votre application peut être largement parallélisée et concurrente, ce qui impliquera inévitablement de répartir votre application sur différents ordinateurs et réseaux, de façon avec le matériel que vous lui lancez.

D’autre part, les employés de Google sont également connus pour avoir déployé beaucoup de main-d’œuvre et de ressources pour résoudre certains problèmes liés aux projets, outils et infrastructures qu’ils utilisent, comme par exemple l’optimisation de programmes complets pour gcc. piratage des équipements internes gcc afin de le préparer aux scénarios de cas d’utilisation typiques de Google.

De même, profiler une application ne signifie plus simplement profiler le code du programme, mais également tous ses systèmes et infrastructures (réseaux, commutateurs, serveurs, grappes RAID) afin d’identifier les redondances et le potentiel d’optimisation du sharepoint vue du système.

Bien que j’apprécie la réponse de Mike Dunlavey, en fait, c’est une excellente réponse avec un exemple à l’appui, je pense que cela pourrait s’exprimer très simplement:

Découvrez d’abord ce qui prend le plus de temps et comprenez pourquoi.

C’est le processus d’identification des cochons de temps qui vous aide à comprendre où vous devez affiner votre algorithme. C’est la seule réponse indépendante du langage que je puisse trouver pour un problème qui doit déjà être optimisé. En supposant que vous voulez être indépendant de l’architecture dans votre quête de vitesse.

Donc, même si l’algorithme peut être optimisé, sa mise en œuvre peut ne pas l’être. L’identification vous permet de savoir quelle est la partie: algorithme ou implémentation. Donc, quel que soit le moment le plus opportun, votre candidat principal à la révision. Mais comme vous dites que vous voulez réduire les derniers pourcentages, vous voudrez peut-être aussi examiner les parties moins importantes, les parties que vous n’avez pas examinées de près au début.

Enfin, un peu d’essais et d’erreurs avec les performances de différentes manières d’implémenter la même solution, ou des algorithmes potentiellement différents, peut apporter des informations qui aident à identifier les pertes de temps et les gains de temps.

HPH, asoudmove.

  • Lorsque vous en arrivez au point où vous utilisez des algorithmes efficaces, il vous faut plus de vitesse ou de mémoire . Utilisez la mise en cache pour “payer” en mémoire pour plus de rapidité ou utilisez des calculs pour réduire l’empreinte mémoire.
  • Si possible (et plus rentable), lancez le matériel au niveau du problème – un processeur plus rapide, plus de mémoire ou de HD pourrait résoudre le problème plus rapidement que d’essayer de le coder.
  • Utilisez la parallélisation si possible – exécutez une partie du code sur plusieurs threads.
  • Utilisez le bon outil pour le travail . Certains langages de programmation créent un code plus efficace, l’utilisation du code managé (Java / .NET) accélère le développement, mais les langages de programmation natifs créent un code d’exécution plus rapide.
  • Micro optimiser . Seuls étaient applicables vous pouvez utiliser l’assemblage optimisé pour accélérer de petits morceaux de code, en utilisant des optimisations SSE / vectorielles aux bons endroits peuvent considérablement augmenter les performances.

Diviser et conquérir

Si le jeu de données en cours de traitement est trop volumineux, parcourez-le en morceaux. Si vous avez bien fait votre code, l’implémentation devrait être facile. Si vous avez un programme monolithique, maintenant vous savez mieux.

Tout d’abord, comme mentionné dans plusieurs réponses précédentes, apprenez ce qui mord votre performance: mémoire, processeur, réseau, firebase database ou autre chose. Selon cela …

  • … si c’est la mémoire – trouvez l’un des livres écrits il y a longtemps par Knuth, l’un des “The Art of Computer Programming”. Il est fort probable que ce soit une question de sorting et de recherche – si ma mémoire est erronée, vous devrez découvrir comment gérer le stockage lent des données sur bande. Transformez mentalement sa mémoire / paire de bandes dans votre paire de cache / mémoire principale (ou dans une paire de cache L1 / L2) respectivement. Étudiez toutes les astuces qu’il décrit – si vous ne trouvez pas quelque chose qui résout votre problème, engagez un informaticien professionnel pour mener une recherche professionnelle. Si votre problème de mémoire est dû au hasard avec les FFT (cache des index avec index inversés lorsque vous faites des papillons radix-2), n’embauchez pas de scientifique. Optimisez manuellement les passes une à une jusqu’à ce que vous obteniez ou gagnez à impasse. Vous avez mentionné la suppression jusqu’à quelques pour cent, n’est-ce pas? Si c’est peu, vous gagnerez probablement.

  • … si c’est processeur – passez en langage d’assemblage. Etude de la spécification du processeur: qu’estce qui compte , VLIW, SIMD. Les appels de fonction sont très probablement des mangeurs de tiques remplaçables. Apprendre les transformations de la boucle – pipeline, dérouler. Les multiplications et les divisions peuvent être remplaçables / interpolées avec des décalages de bits (les multiplications par petits entiers peuvent être remplaçables par des ajouts). Essayez des astuces avec des données plus courtes – si vous êtes chanceux, une instruction avec 64 bits peut s’avérer remplaçable avec deux sur 32 ou même 4 sur 16 ou 8 sur 8 bits. Essayez également des données plus longues – par exemple, vos calculs de flottement peuvent se révéler plus lents que les calculs doubles sur un processeur donné. Si vous avez des éléments sortinggonomésortingques, combattez-les avec des tables pré-calculées; N’oubliez pas que le sinus de petite valeur peut être remplacé par cette valeur si la perte de précision se situe dans les limites autorisées.

  • … si c’est un réseau – pensez à compresser les données que vous passez dessus. Remplacez le transfert XML par des binarys. Protocoles d’étude Essayez UDP au lieu de TCP si vous pouvez gérer en quelque sorte la perte de données.

  • … si c’est la firebase database, eh bien, allez dans n’importe quel forum de firebase database et demandez conseil. Grille de données en mémoire, optimisation du plan de requête, etc., etc.

HTH 🙂

Je pense que cela a déjà été dit différemment. Mais lorsque vous utilisez un algorithme utilisant beaucoup de processeurs, vous devez tout simplifier dans la boucle la plus interne au désortingment de tout le rest.

Cela peut sembler évident pour certains, mais c’est quelque chose sur lequel j’essaie de me concentrer indépendamment de la langue avec laquelle je travaille. Si vous traitez par exemple des boucles nestedes et que vous trouvez une opportunité de prendre du code à un niveau, vous pouvez dans certains cas accélérer considérablement votre code. Comme autre exemple, il y a des petites choses à considérer, comme travailler avec des entiers au lieu de variables à virgule flottante quand vous le pouvez, et utiliser la multiplication plutôt que la division chaque fois que vous le pouvez. Encore une fois, ce sont des choses à considérer pour votre boucle la plus intérieure.

Parfois, vous pouvez trouver utile d’effectuer vos opérations mathématiques sur un nombre entier à l’intérieur de la boucle interne, puis de le réduire à une variable à virgule flottante avec laquelle vous pourrez travailler par la suite. C’est un exemple de sacrifier la vitesse dans une section pour améliorer la vitesse dans une autre, mais dans certains cas, le gain peut en valoir la peine.

Caching! Un moyen peu coûteux (dans les efforts du programmeur) de faire presque n’importe quoi plus rapidement consiste à append une couche d’abstraction de mise en cache à n’importe quelle zone de mouvement de données de votre programme. Qu’il s’agisse d’E / S ou simplement de passage / création d’objects ou de structures. Il est souvent facile d’append des caches aux classes d’usine et aux lecteurs / rédacteurs.

Parfois, le cache ne vous rapportera pas grand chose, mais c’est une méthode simple pour append la mise en cache partout, puis la désactiver là où cela ne vous aide pas. I’ve often found this to gain huge performance without having to micro-parsing the code.

Very difficult to give a generic answer to this question. It really depends on your problem domain and technical implementation. A general technique that is fairly language neutral: Identify code hotspots that cannot be eliminated, and hand-optimize assembler code.

Last few % is a very CPU and application dependent thing….

  • cache architectures differ, some chips have on-chip RAM you can map directly, ARM’s (sometimes) have a vector unit, SH4’s a useful masortingx opcode. Is there a GPU – maybe a shader is the way to go. TMS320 ‘s are very sensitive to twigs within loops (so separate loops and move conditions outside if possible).

The list goes on…. But these sorts of things really are the last resort…

Build for x86, and run Valgrind /Cachegrind against the code for proper performance profiling. Or Texas Instruments’ CCStudio has a sweet profiler. Then you’ll really know where to focus…

Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?

For any non-offline projects, while having best software and best hardware, if your throughoutput is weak, then that thin line is going to squeeze data and give you delays, albeit in milliseconds… but if you are talking about the last drops, that’s a some drops gained, 24/7 for any packge sent or received.

I’ve spent some time working on optimising client/server business systems operating over low-bandwidth and long-latency networks (eg satellite, remote, offshore), and been able to achieve some dramatic performance improvements with a fairly repeatable process.

  • Measure : Start by understanding the network’s underlying capacity and topology. Talking to the relevant networking people in the business, and make use of basic tools such as ping and traceroute to establish (at a minimum) the network latency from each client location, during typical operational periods. Next, take accurate time measurements of specific end user functions that display the problematic symptoms. Record all of these measurements, along with their locations, dates and times. Consider building end-user “network performance testing” functionality into your client application, allowing your power users to participate in the process of improvement; empowering them like this can have a huge psychological impact when you’re dealing with users frustrated by a poorly performing system.

  • Analyze : Using any and all logging methods available to establish exactly what data is being transmitted and received during the execution of the affected operations. Ideally, your application can capture data transmitted and received by both the client and the server. If these include timestamps as well, even better. If sufficient logging isn’t available (eg closed system, or inability to deploy modifications into a production environment), use a network sniffer and make sure you really understand what’s going on at the network level.

  • Cache : Look for cases where static or infrequently changed data is being transmitted repetitively and consider an appropriate caching strategy. Typical examples include “pick list” values or other “reference entities”, which can be surprisingly large in some business applications. In many cases, users can accept that they must restart or refresh the application to update infrequently updated data, especially if it can shave significant time from the display of commonly used user interface elements. Make sure you understand the real behaviour of the caching elements already deployed – many common caching methods (eg HTTP ETag) still require a network round-sortingp to ensure consistency, and where network latency is expensive, you may be able to avoid it altogether with a different caching approach.

  • Parallelise : Look for sequential transactions that don’t logically need to be issued ssortingctly sequentially, and rework the system to issue them in parallel. I dealt with one case where an end-to-end request had an inherent network delay of ~2s, which was not a problem for a single transaction, but when 6 sequential 2s round sortingps were required before the user regained control of the client application, it became a huge source of frustration. Discovering that these transactions were in fact independent allowed them to be executed in parallel, reducing the end-user delay to very close to the cost of a single round sortingp.

  • Combine : Where sequential requests must be executed sequentially, look for opportunities to combine them into a single more comprehensive request. Typical examples include creation of new entities, followed by requests to relate those entities to other existing entities.

  • Compress : Look for opportunities to leverage compression of the payload, either by replacing a textual form with a binary one, or using actual compression technology. Many modern (ie within a decade) technology stacks support this almost transparently, so make sure it’s configured. I have often been surprised by the significant impact of compression where it seemed clear that the problem was fundamentally latency rather than bandwidth, discovering after the fact that it allowed the transaction to fit within a single packet or otherwise avoid packet loss and therefore have an outsize impact on performance.

  • Repeat : Go back to the beginning and re-measure your operations (at the same locations and times) with the improvements in place, record and report your results. As with all optimisation, some problems may have been solved exposing others that now dominate.

In the steps above, I focus on the application related optimisation process, but of course you must ensure the underlying network itself is configured in the most efficient manner to support your application too. Engage the networking specialists in the business and determine if they’re able to apply capacity improvements, QoS, network compression, or other techniques to address the problem. Usually, they will not understand your application’s needs, so it’s important that you’re equipped (after the Analyse step) to discuss it with them, and also to make the business case for any costs you’re going to be asking them to incur. I’ve encountered cases where erroneous network configuration caused the applications data to be transmitted over a slow satellite link rather than an overland link, simply because it was using a TCP port that was not “well known” by the networking specialists; obviously rectifying a problem like this can have a dramatic impact on performance, with no software code or configuration changes necessary at all.

Not nearly as in depth or complex as previous answers, but here goes: (these are more beginner/intermediate level)

  • obvious: dry
  • run loops backwards so you’re always comparing to 0 rather than a variable
  • use bitwise operators whenever you can
  • break repetitive code into modules/functions
  • cache objects
  • local variables have slight performance advantage
  • limit ssortingng manipulation as much as possible

Impossible to say. It depends on what the code looks like. If we can assume that the code already exists, then we can simply look at it and figure out from that, how to optimize it.

Better cache locality, loop unrolling, Try to eliminate long dependency chains, to get better instruction-level parallelism. Prefer conditional moves over twigs when possible. Exploit SIMD instructions when possible.

Understand what your code is doing, and understand the hardware it’s running on. Then it becomes fairly simple to determine what you need to do to improve performance of your code. That’s really the only truly general piece of advice I can think of.

Well, that, and “Show the code on SO and ask for optimization advice for that specific piece of code”.

If better hardware is an option then definitely go for that. Autrement

  • Check you are using the best comstackr and linker options.
  • If hotspot routine in different library to frequent caller, consider moving or cloning it to the callers module. Eliminates some of the call overhead and may improve cache hits (cf how AIX links strcpy() statically into separately linked shared objects). This could of course decrease cache hits also, which is why one measure.
  • See if there is any possibility of using a specialized version of the hotspot routine. Downside is more than one version to maintain.
  • Look at the assembler. If you think it could be better, consider why the comstackr did not figure this out, and how you could help the comstackr.
  • Consider: are you really using the best algorithm? Is it the best algorithm for your input size?

The google way is one option “Cache it.. Whenever possible don’t touch the disk”

Here are some quick and dirty optimization techniques I use. I consider this to be a ‘first pass’ optimization.

Learn where the time is spent Find out exactly what is taking the time. Is it file IO? Is it CPU time? Is it the network? Is it the Database? It’s useless to optimize for IO if that’s not the bottleneck.

Know Your Environment Knowing where to optimize typically depends on the development environment. In VB6, for example, passing by reference is slower than passing by value, but in C and C++, by reference is vastly faster. In C, it is reasonable to try something and do something different if a return code indicates a failure, while in Dot Net, catching exceptions are much slower than checking for a valid condition before attempting.

Indexes Build indexes on frequently queried database fields. You can almost always trade space for speed.

Avoid lookups Inside of the loop to be optimized, I avoid having to do any lookups. Find the offset and/or index outside of the loop and reuse the data inside.

Minimize IO try to design in a manner that reduces the number of times you have to read or write especially over a networked connection

Reduce Abstractions The more layers of abstraction the code has to work through, the slower it is. Inside the critical loop, reduce abstractions (eg reveal lower-level methods that avoid extra code)

Spawn Threads for projects with a user interface, spawning a new thread to preform slower tasks makes the application feel more responsive, although isn’t.

Pre-process You can generally trade space for speed. If there are calculations or other intense operations, see if you can precompute some of the information before you’re in the critical loop.

Sometimes changing the layout of your data can help. In C, you might switch from an array or structures to a structure of arrays, or vice versa.

Tweak the OS and framework.

It may sound an overkill but think about it like this: Operating Systems and Frameworks are designed to do many things. Your application only does very specific things. If you could get the OS do to exactly what your application needs and have your application understand how the the framework (php,.net,java) works, you could get much better out of your hardware.

Facebook, for example, changed some kernel level thingys in Linux, changed how memcached works (for example they wrote a memcached proxy, and used udp instead of tcp ).

Another example for this is Window2008. Win2K8 has a version were you can install just the basic OS needed to run X applicaions (eg Web-Apps, Server Apps). This reduces much of the overhead that the OS have on running processes and gives you better performance.

Of course, you should always throw in more hardware as the first step…

pass by reference instead of by value

Reduce variable sizes (in embedded systems)

If your variable size is larger than the word size on a specific architecture, it can have a significant effect on both code size and speed. For example, if you have a 16 bit system, and use a long int variable very often, and later realize that it can never get outside the range (−32.768 … 32.767) consider reducing it to short int.

From my personal experience, if a program is ready or almost ready, but we realize it takes up about 110% or 120% of the target hardware’s program memory, a quick normalization of variables usually solves the problem more often than not.

By this time, optimizing the algorithms or parts of the code itself can become frustratingly futile:

  • reorganize the whole structure and the program no longer works as intended, or at least you introduce a lot of bugs.
  • do some clever sortingcks: usually you spend a lot of time optimizing something, and discover no or very small decrease in code size, as the comstackr would have optimized it anyway.

Many people make the mistake of having variables which exactly store the numerical value of a unit they use the variable for: for example, their variable time stores the exact number of milliseconds, even if only time steps of say 50 ms are relevant. Maybe if your variable represented 50 ms for each increment of one, you would be able to fit into a variable smaller or equal to the word size. On an 8 bit system, for example, even a simple addition of two 32-bit variables generates a fair amount of code, especially if you are low on registers, while 8 bit additions are both small and fast.