Stratégies d'optimisation des performances en dernier recours [fermé]

il y a déjà beaucoup de questions sur la performance sur ce site, mais il me semble que presque toutes sont très spécifiques aux problèmes et assez étroites. Et presque tout répéter les conseils pour éviter l'optimisation prématurée.

supposons:

  • le code fonctionne déjà correctement
  • les algorithmes choisis sont déjà optimale pour les circonstances du problème
  • le code a été mesurée, et la délinquance routines ont été isolés
  • toutes les tentatives d'optimisation seront également mesurées pour s'assurer qu'elles n'aggravent pas la situation

ce que je cherche ici, ce sont des stratégies et des astuces pour extraire jusqu'au dernier pour cent dans un algorithme critique quand il n'y a rien d'autre à faire que ce qu'il faut.

idéalement, essayer de faire des réponses langue agnostique, et d'indiquer les côtés négatifs de la stratégies suggérées, le cas échéant.

je vais ajouter une réponse avec mes propres suggestions initiales, et attendre avec impatience tout ce que la communauté de débordement de pile peut penser d'autre.

570
demandé sur jerryjvl 2009-05-29 18:26:59

30 réponses

OK, vous définissez le problème là où il semblerait qu'il n'y ait pas beaucoup de place pour l'amélioration. C'est assez rare, dans mon expérience. J'ai essayé d'expliquer cela dans un article de Dr.Dobbs en novembre '93, en partant d'un programme non-trivial conventionnellement bien conçu sans gaspillage évident et en le faisant passer par une série d'optimisations jusqu'à ce que son temps d'horloge murale a été réduite de 48 secondes à 1,1 secondes, et la taille du code source a été réduite d'un facteur de 4. Mon outil de diagnostic était ce . La séquence des changements était la suivante:

  • le premier problème constaté a été l'utilisation de groupes de listes (maintenant appelés "itérateurs" et "classes de conteneurs") représentant plus de la moitié du temps. Ceux-ci ont été remplacés par du code assez simple, ramenant le temps à 20 secondes.

  • Aujourd'hui, le plus grand preneur de temps est plus listing. En pourcentage, il n'était pas si grand, mais maintenant il est parce que le plus gros problème a été supprimé. Je trouve un moyen d'accélérer, et le temps chute à 17 secondes.

  • maintenant il est plus difficile de trouver des coupables évidents, mais il y a quelques plus petits que je peux faire quelque chose, et le temps chute à 13 sec.

il me semble avoir heurté un mur. Les échantillons me disent exactement ce qu'il fait, mais je n'arrive pas à trouver quoi que ce soit que je puisse améliorer. Puis J'Ai réfléchissez à la conception de base du programme, à sa structure axée sur les transactions, et demandez-vous si toutes les recherches de listes qu'il effectue sont effectivement prescrites par les exigences du problème.

puis j'ai frappé sur une re-conception, où le code de programme est effectivement généré (via macros préprocesseur) à partir d'un plus petit ensemble de source, et dans lequel le programme n'est pas constamment comprendre les choses que le programmeur sait sont assez prévisibles. En d'autres termes, ne pas "interpréter" la séquence de choses à faire, "compiler".

  • cette refonte est effectuée, réduisant le code source d'un facteur de 4, et le temps est réduit à 10 Secondes.

maintenant, parce que ça devient si rapide, c'est difficile à échantillonner, donc je lui donne 10 fois plus de travail à faire, mais les temps suivants sont basés sur la charge de travail originale.

  • plus de diagnostic révèle qu'il passe du temps en la file d'attente-gestion. La doublure réduit le temps à 7 secondes.

  • maintenant un grand preneur de temps est l'impression de diagnostic que j'avais fait. Flush ça-4 secondes.

  • maintenant, les plus grands preneurs de temps sont les appels à malloc et libre . Recycler des objets-2,6 secondes.

  • en continuant à échantillonner, je trouve toujours les opérations qui ne sont pas strictement nécessaires - 1.1 secondes.

facteur total d'accélération: 43,6

maintenant, il n'y a pas deux programmes identiques, mais dans le logiciel non-toy j'ai toujours vu une progression comme celle-ci. D'abord, vous obtenez la chose facile, puis le plus difficile, jusqu'à ce que vous obtenez un point de rendements décroissants. Alors la perspicacité que vous gagnez peut bien conduire à une nouvelle conception, en commençant une nouvelle ronde de speedups, jusqu'à ce que vous frappez de nouveau décroissant retourner. C'est à ce moment-là qu'il pourrait être logique 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.

P. S. on peut se demander pourquoi je n'ai pas utilisé de profileur. La réponse est que presque tous ces "problèmes"étaient un site d'appel de fonction, qui empilent des échantillons. Les profileurs, même aujourd'hui, sont à peine à venir autour de l'idée que les déclarations et les instructions d'appel sont plus important pour localiser, et plus facile à fixer, que les fonctions entières. J'ai construit un profileur pour faire ça, mais pour une vraie intimité avec ce que fait le code, il n'y a pas de substitut pour mettre les doigts dedans. C'est pas une question que le nombre d'échantillons est faible, car aucun des problèmes trouvés sont si minuscules qu'ils sont faciles à manquer.

ajouté: jerryjvl a demandé quelques exemples. Ici, c'est le premier problème. Il se compose d'un petit nombre de lignes de code séparées, ensemble prenant 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)

ceux-ci utilisaient le cluster de liste ILST (similaire à une classe de liste). Ils sont mis en œuvre de la manière habituelle, avec "masquer l'information", ce qui signifie que les utilisateurs de la classe n'étaient pas censés avoir à se soucier de la façon dont ils ont été mis en œuvre. Quand ces lignes ont été écrites (sur environ 800 lignes de code) pensé n'a pas été donné à l'idée que ceux-ci pourraient être un "goulot d'étranglement" (je déteste ce mot). Ils sont tout simplement la façon recommandée de faire les choses. Il est facile de dire en rétrospective qu'ils auraient dû être évités, mais dans mon expérience tous les problèmes de performance sont comme cela. En général, il est bon d'essayer d'éviter de créer des problèmes de performances. Il est même préférable de trouver et de corriger ceux qui sont créés, même s'ils "auraient dû être évités" (en rétrospective). J'espère que 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 articles à leurs fins. (La solution était de recueillir les éléments de tableaux, et de construire les listes à la fois.) Ce qui est intéressant, c'est que ces relevés n'ont coûté (c'est-à-dire qu'ils étaient sur la pile d'appels) que 3/48 de l'heure d'origine, de sorte qu'ils n'étaient pas en 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 donc ont maintenant un "gros poisson". En général, c'est comment il va.

je pourrais ajouter que ce projet a été distillé à partir d'un vrai projet que j'ai aidé sur. Dans ce projet, les problèmes de performance étaient beaucoup plus dramatiques (tout comme les accélérations), comme appeler une routine d'accès à la base de données dans une boucle interne pour voir si une tâche était terminée.

RÉFÉRENCE AJOUTÉE: Le code source, original et redessiné, se trouve dans www.ddj.com , pour 1993, dans le dossier 9311.zip, fichiers slug.asc et de la limace.zip.

EDIT 2011/11/26: Il y a maintenant un projet sourceforge contenant du code source en C ++ visuel et une description détaillée de la façon dont il a été accordé. Il passe seulement par la première moitié du scénario décrit ci-dessus, et il ne suit pas exactement la même séquence, mais obtient encore un 2-3 ordre de grandeur de l'accélération.

407
répondu Mike Dunlavey 2017-05-23 10:31:30

Suggestions:

  • précalculer plutôt que recalculer : toute boucle ou appel répété qui contient des calculs qui ont une gamme relativement limitée d'entrées, envisager de faire une recherche (ou un dictionnaire tableau) qui contient le résultat de ce calcul pour toutes les valeurs dans la gamme valide d'entrées. Utilisez ensuite une recherche simple à l'intérieur de l'algorithme de la place.

    down-side : si peu de les valeurs pré-calculées sont effectivement utilisées cela peut aggraver les choses, aussi la recherche peut prendre de la mémoire significative.
  • Don't use library methods : 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 paramètres, etc. En ré-implémentant une méthode, vous pouvez être en mesure de retirer beaucoup de logique qui ne s'applique pas dans les circonstances exactes où vous l'utilisez.

    vers le bas : l'écriture de code supplémentaire signifie plus de surface pour les bogues.
  • do use library methods : pour me contredire, les bibliothèques de langues sont écrites par des gens qui sont beaucoup plus intelligents que vous ou moi; il y a des chances qu'ils l'aient fait mieux et plus vite. Ne l'implémentez pas vous-même à moins que vous ne puissiez le rendre plus rapide (i.e.: toujours mesurer!)
  • Tricher : dans certains cas, bien qu'exact le calcul peut exister pour votre problème, vous pouvez ne pas avoir besoin de "exact", parfois une approximation peut être "assez bon" et beaucoup plus rapide dans l'affaire. Demandez-vous, est-il vraiment si la réponse est de 1%? 5%? même à 10%?

    Bas-côtés : Bien... la réponse ne sera pas exacte.
179
répondu jerryjvl 2009-05-29 15:16:46

quand vous ne pouvez plus améliorer la performance - voir si vous pouvez améliorer la performance perçue à la place.

vous pouvez ne pas être en mesure de rendre votre algorithme fooCalc plus rapide, mais souvent, il ya des moyens de faire de votre application semble plus sensible à l'utilisateur.

quelques exemples:

  • anticiper ce que l'utilisateur va pour demander et commencer à travailler sur ce avant
  • afficher les résultats comme ils entrent, au lieu de tous à la fois à la fin
  • précis progress mètre

cela ne rendra pas votre programme plus rapide, mais il pourrait rendre vos utilisateurs plus heureux avec la vitesse que vous avez.

158
répondu kenj0418 2009-09-13 15:37:49

je passe la plus grande partie de ma vie ici. Les grandes lignes sont d'exécuter votre profileur et de le faire enregistrer:

  • Cache . Le cache de données est la source n ° 1 des décrochages dans la plupart des programmes. Améliorer le taux de succès de cache en réorganisant les structures de données incriminées pour avoir une meilleure localisation; pack structures et les types numériques pour éliminer les octets gaspillés (et donc les récupérations de cache gaspillées); les données préfetch dans la mesure du possible pour réduire stalle.
  • Load-hit-magasins . Les hypothèses du compilateur sur l'aliasing du pointeur, et les cas où les données sont déplacées entre des ensembles de registres déconnectés via la mémoire, peuvent causer un certain comportement pathologique qui fait que l'ensemble du pipeline CPU s'efface sur une charge op. Trouvez des endroits où les flotteurs, les vecteurs et les ints sont moulés les uns aux autres et éliminez-les. Utilisez __restrict libéralement pour promettre au compilateur de l'alias.
  • Microcoded opérations . La plupart des processeurs ont des opérations qui ne peuvent pas être pipelinées, mais plutôt exécuter un petit sous-programme stocké dans ROM. Les exemples sur le PowerPC sont les nombres entiers multipliés, divisez et shift-by-variable-amount. Le problème est que tout le pipeline s'arrête complètement pendant que cette opération est en cours. Essayer d'éliminer l'utilisation de ces opérations ou au moins les décomposer en leur composante pipelined ops de sorte que vous pouvez obtenir le bénéfice de l'expédition superscalar sur tout le reste de votre programme.
  • Branche mispredicts . Ces trop vider le pipeline. Trouvez des cas où le CPU passe beaucoup de temps à remplir le tuyau après une branche, et utilisez l'indication de branche si disponible pour le faire prédire correctement plus souvent. Ou encore mieux, remplacez les branches par des mouvements conditionnels dans la mesure du possible, surtout après les opérations à la pointe flottante parce que leur pipe est habituellement plus profonde et lire les indicateurs de condition après fcmp peuvent causer un décrochage.
  • Séquentielle à virgule flottante ops . Faites ces SIMD.

et une chose de plus que j'aime faire:

  • " Réglez votre compilateur sur la sortie des listes d'assemblage et regardez ce qu'il émet pour les fonctions hotspot dans votre code. Toutes ces optimisations astucieuses qu'un "bon compilateur devrait être capable de faire pour vous automatiquement"? Il y a des Chances que votre compilateur ne les fasse pas. J'ai vu GCC émettre un code WTF.
132
répondu Crashworks 2009-05-29 22:37:01

Lancez plus de matériel!

76
répondu sisve 2009-05-29 14:32:26

autres suggestions:

  • évitez l'E / S : toute E/S (disque, réseau, ports, etc.) être toujours beaucoup plus lent que le code qui est effectuer des calculs, donc se débarrasser de tous les e/s que vous faites pas strictement besoin.

  • Move I/O avant de 151960920" : Charger toutes les données que vous allez pour avoir besoin d'un calcul à l'avance, de sorte que vous ne ont répété les I/O attend dans le cœur d'un critique algorithme (et peut-être en conséquence le disque répété cherche, quand charger toutes les données dans un seul clic peut éviter de chercher).

  • Delay I/O : n'écrivez pas vos résultats avant le le calcul est terminé, les stocker dans une structure de données et ensuite, jetez-le en une fois à la fin quand le travail dur est effectué.

  • Fileté I/O : Pour ceux qui osent assez, combinent ' I/ O 'ou' retard I / O' avec le calcul réel par déplacement de la charge dans un filetage parallèle, de sorte que vous chargez plus de données vous pouvez travailler sur un calcul sur les données que vous avez déjà, ou alors que vous calculez la prochaine lot de données, vous pouvez simultanément écrire les résultats à partir de la dernière fournée.

56
répondu Peter Mortensen 2009-09-13 16:06:59

puisque beaucoup de problèmes de performance impliquent des problèmes de base de données, je vais vous donner quelques choses spécifiques à regarder lors de l'ajustement des requêtes et des procédures stockées.

éviter les curseurs dans la plupart des bases de données. Évitez de boucler ainsi. La plupart du temps, l'accès aux données doit être défini, pas d'enregistrement par enregistrement de la transformation. Cela inclut le fait de ne pas réutiliser un seul enregistrement stocké procédure lorsque vous voulez insérer 1.000.000 d'enregistrements à la fois.

N'utilisez jamais select *, seulement renvoyer les champs dont vous avez réellement besoin. Ceci est particulièrement vrai s'il y a des jointures car les champs join seront répétés et causeront ainsi une charge inutile à la fois sur le serveur et sur le réseau.

éviter l'utilisation de sous-séries corrélées. Utilisez les jointures (y compris les jointures vers des tables dérivées lorsque c'est possible) (je sais que C'est vrai pour Microsoft SQL Server, mais testez les conseils quand vous utilisez un backend différent).

Index, index, index. Et mettez ces statistiques à jour si applicable à votre base de données.

Faire la requête sargable . Signification éviter les choses qui rendent impossible l'utilisation de l'index, tels que l'utilisation d'un caractère générique dans le premier caractère d'une clause ou d'une fonction dans la jointure ou de la partie gauche d'une instruction where.

utiliser les types de données corrects. Il est plus rapide de faire des calculs de date sur un champ de date que de devoir essayer de convertir une chaîne de type de données en un type de date, puis faire le calcul.

N'a jamais mis de boucle dans une gâchette!

la plupart des bases de données ont un moyen de vérifier comment l'exécution de la requête sera faite. Dans Microsoft SQL Server, cela s'appelle un plan d'exécution. Vérifiez ceux d'abord pour voir où les zones de problème se situent.

tenir compte de la fréquence d'exécution de la requête ainsi que du temps nécessaire pour exécuter la requête lors de la détermination de ce qui doit être optimisé. Parfois, vous pouvez gagner plus de performance d'un léger tweak à une requête qui exécute des millions de fois par jour que vous ne pouvez en effaçant le temps d'une requête longue_running qui ne tourne qu'une fois par mois.

utilisez une sorte d'outil de profileur pour trouver ce qui est réellement envoyé à et à partir de la base de données. Je peux me rappeler une fois dans le passé où nous ne pouvions pas comprendre pourquoi la page était si lente à charger quand la procédure stockée était rapide et a découvert par le profilage que la page Web demandait la requête plusieurs fois au lieu d'une fois.

le profileur vous aidera aussi à trouver qui bloque qui. Certaines requêtes qui s'exécutent rapidement alors qu'elles sont exécutées seules peuvent devenir vraiment lentes à cause des verrous d'autres requêtes.

46
répondu HLGEM 2012-01-26 17:26:24

le facteur limitant le plus important aujourd'hui est le bande-mémoire 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é entre les noyaux et les fils, aggravant ce problème encore plus. Enfin, la signalisation inter-puce nécessaire pour garder les différentes caches cohérentes augmente également avec un nombre accru de noyaux. 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 réflexion et un remaniement minutieux.

beaucoup de commentaires mentionnent déjà le code de mise en cache. Il y a au moins deux saveurs distinctes de ceci:

  • éviter les latences de récupération de mémoire.
  • réduction de la pression du bus mémoire (bande passante).

le premier le problème concerne spécifiquement le fait de rendre vos modèles d'accès aux données plus réguliers, ce qui permet au préfeteur matériel de fonctionner efficacement. Évitez l'allocation de mémoire dynamique qui répand vos objets de données dans la mémoire. Utilisez des conteneurs linéaires au lieu de listes, de hachures et d'arbres liés.

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 s'adaptent dans le cache disponible, et réutilisez ces données autant que possible il est toujours dans le cache.

empaqueter les données plus serré et en s'assurant que vous utilisez toutes les données dans les lignes de cache dans les boucles chaudes, aidera à éviter ces autres effets, et permettra d'ajuster plus utile données dans le cache.

29
répondu Mats N 2009-06-19 16:51:08
  • Quel matériel utilisez-vous? Pouvez-vous utiliser des optimisations spécifiques à une plate-forme (comme la vectorisation)?
  • Pouvez-vous obtenir un meilleur compilateur? Par exemple: passer de GCC à Intel?
  • pouvez-vous faire tourner 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 plate-forme. Dans le style de, "dans un if/else, la plus commune de la déclaration de la première"
25
répondu Johan Kotlinski 2009-05-31 00:52:09
  • routines en ligne (élimination de l'appel/du retour et de la poussée des paramètres)
  • essayer d'éliminer les tests / commutateurs avec les tables de recherche ups (si elles sont plus rapides)
  • déroulez les boucles (l'appareil de Duff) au point où elles s'inscrivent dans le cache CPU
  • Localisez l'accès à la mémoire pour ne pas détruire votre cache
  • localiser les calculs connexes si l'optimiseur ne le fait pas déjà
  • Éliminer les invariants de boucle si l'optimiseur ne le fait pas déjà
15
répondu plinth 2009-05-29 15:05:04

vous devriez probablement considérer le" point de vue de Google", c'est-à-dire déterminer comment votre application peut devenir largement parallélisée et concurrente, ce qui signifiera inévitablement aussi à un certain point de regarder dans la distribution de votre application à travers différentes machines et réseaux, de sorte qu'il peut idéalement échelle presque linéairement avec le matériel que vous lui lancez.

d'autre part, les gens de Google sont également connus pour jeter beaucoup de main-d'œuvre et de ressources à résoudre certains des problèmes dans les projets, les outils et l'infrastructure qu'ils utilisent, comme par exemple l'optimisation du programme entier pour gcc en ayant une équipe dédiée d'ingénieurs hacking gcc internes afin de le préparer pour des scénarios D'utilisation Google-cas typiques.

de même, profiler une application ne signifie plus simplement profiler le code du programme, mais aussi tous les systèmes et infrastructures environnants (réseaux, commutateurs, serveur, RAID) tableaux) afin d'identifier les redondances et le potentiel d'optimisation du point de vue du système.

15
répondu none 2009-09-13 19:31:54

bien que J'aime la réponse de Mike Dunlavey, en fait il s'agit d'une grande réponse avec un exemple à l'appui, je pense qu'il pourrait être exprimé très simplement ainsi:

trouvez d'abord ce qui prend le plus de temps, et comprenez pourquoi.

C'est le processus d'identification du temps de porcs qui vous aide à comprendre où vous devez affiner votre algorithme. C'est la seule réponse agnostique globale que je puisse donner au langage. trouver un problème qui est déjà censé être pleinement optimisé. Aussi présumant que vous voulez être indépendant d'architecture dans votre quête de vitesse.

ainsi, bien que l'algorithme puisse être optimisé, sa mise en œuvre peut ne pas l'être. L'identification vous permet de savoir quelle partie est laquelle: algorithme ou implémentation. Donc, celui qui passe le plus de temps en revue est votre meilleur candidat. Mais puisque vous dites que vous voulez serrer les derniers %, vous pouvez également examiner les parties mineures, les parties que vous n'avez pas examinées de si près au début.

enfin un peu d'essai et d'erreur avec des chiffres de performance sur différentes façons de mettre en œuvre la même solution, ou potentiellement des algorithmes différents, peut apporter des idées qui aident à identifier les gaspilleurs de temps et les épargnants de temps.

HPH, asoudmove.

15
répondu asoundmove 2011-01-28 03:37:45
  • quand vous arrivez au point que vous utilisez des algorithmes efficaces c'est une question de ce que vous avez besoin de plus vitesse ou mémoire . Utilisez la mise en cache pour" payer " en mémoire pour plus de vitesse ou utilisez des calculs pour réduire l'empreinte mémoire.
  • si possible (et plus rentable) jetez le matériel au problème - CPU plus rapide, plus de mémoire ou HD pourrait résoudre le problème plus rapidement qu'en essayant de le coder.
  • utiliser la parallélisation si possible - exécuter une partie du code sur plusieurs threads.
  • utilisez le bon outil pour le travail . certains langages de programmation créent du code plus efficace, en utilisant du code géré (par exemple Java / .NET) accélèrent le développement, mais les langages de programmation natifs créent du code plus rapide.
  • Micro optimiser . Seulement étaient applicables vous pouvez utiliser l'assemblage optimisé pour vitesse petits morceaux de code, en utilisant des optimisations SSE/vectorielles dans les bons endroits peuvent augmenter considérablement la performance.
12
répondu Dror Helper 2009-05-30 15:39:32

diviser pour mieux régner

si l'ensemble de données en cours de traitement est trop grand, boucle sur des morceaux de celui-ci. Si vous avez fait votre code correctement, la mise en œuvre devrait être facile. Si vous avez un programme monolithique, maintenant, vous connaissez mieux.

12
répondu MPelletier 2010-03-22 03:21:21

tout d'abord, comme mentionné dans plusieurs réponses antérieures, apprendre ce qui mord votre performance - est-ce la mémoire ou le processeur ou le réseau ou la base de données ou autre chose. En fonction de cela...

  • ...si c'est de la mémoire - trouvez un des livres écrits il y a longtemps par Knuth, un de "L'Art De La Programmation informatique" série. Il s'agit très probablement de tri et de recherche - Si ma mémoire est erronée, alors vous devrez trouver dans lequel il parle de la façon de gérer le stockage de données sur bande lente. Transformer mentalement sa paire mémoire / bande en votre paire de mémoire cache / principale (ou en paire de cache L1/L2) respectivement. Étudiez tous les trucs qu'il décrit - si vous ne trouvez pas quelque chose qui résout votre problème, puis embaucher un informaticien Professionnel pour effectuer une recherche professionnelle. Si votre problème de mémoire est par hasard avec FFT (cache manque aux index bit-reversed lors de l'exécution de papillons radix-2) alors ne pas embaucher un scientifique-à la place, optimiser manuellement passe un par un jusqu'à ce que vous êtes soit gagner ou obtenir de l'impasse. Vous avez mentionné jusqu'au dernier pour cent n'est-ce pas? Si c'est peu en effet, vous gagnerez très probablement.

  • ...si c'est un processeur, passez au langage d'assemblage. Spécification du processeur d'étude - what takes ticks , VLIW, SIMD. Les appels de fonction sont très probablement remplaçables. Apprendre boucle les transformations des pipelines, de le dérouler. Les multiples et les divisions peuvent être remplacés / interpolés par des déplacements de bits (les multiples par de petits entiers peuvent être remplacés par des additions). Essayez des trucs avec des données plus courtes - si vous êtes chanceux une instruction avec 64 bits pourrait s'avérer remplaçable avec deux sur 32 ou même 4 sur 16 ou 8 sur 8 bits go figure. Essayez aussi plus long données-par exemple vos calculs de flotteur peuvent se révéler plus lent que les doubles à un processeur particulier. Si vous avez trigonométrique, combattez-le avec des tables pré-calculées; gardez également à l'esprit que sinus de petite valeur pourrait être remplacé par cette valeur si la perte de précision est dans les limites permises.

  • ...si c'est réseau - pensez à compresser les données que vous passez au-dessus. Remplacer le transfert XML par un transfert binaire. Les protocoles d'étude. Essayez UDP au lieu de TCP si vous pouvez gérer la perte de données.

  • ...si c'est la base de données, allez à n'importe quel forum de base de données et demander des conseils. En mémoire de grille de données, l'optimisation de plan de requête etc etc etc.

HTH:)

11
répondu gnat 2011-07-29 03:28:14

je pense que cela a déjà été dit d'une manière différente. Mais quand vous avez affaire à un algorithme intensif de processeur, vous devriez simplifier tout à l'intérieur de la boucle la plus interne au détriment de tout le reste.

cela peut sembler évident pour certains, mais c'est quelque chose sur lequel j'essaie de me concentrer, peu importe la langue avec laquelle je travaille. Si vous avez affaire à des boucles imbriquées, par exemple, et que vous trouvez une occasion de descendre un certain code d'un niveau, vous pouvez les cas accélèrent drastiquement votre code. Comme autre exemple, il y a les petites choses à penser, comme travailler avec des entiers au lieu de variables à virgule flottante chaque fois que vous le pouvez, et utiliser la multiplication au lieu de la division chaque fois que vous le pouvez. Encore une fois, ce sont des choses qui devraient être considérées pour votre boucle la plus intérieure.

parfois, vous pouvez trouver l'avantage d'effectuer vos opérations mathématiques sur un entier à l'intérieur de la boucle interne, puis de le réduire à un flottant variable de point vous pouvez travailler avec par la suite. C'est un exemple de sacrifier la vitesse dans une section d'améliorer la vitesse de l'autre, mais dans certains cas, le remboursement peut être bien utile.

8
répondu Steve Wortham 2009-05-29 22:07:45

"151910920 la mise en Cache"! un moyen bon marché (dans l'effort du programmeur) de faire presque n'importe quoi plus vite est d'ajouter une couche d'abstraction de cache à n'importe quelle zone de mouvement de données de votre programme. Que ce soit I/O ou juste passage/création d'objets ou de structures. Il est souvent facile d'ajouter des caches aux cours d'usine et aux cours de lecture/écriture.

parfois le cache ne vous gagnera pas beaucoup, mais c'est une méthode facile pour juste ajouter la mise en cache partout et ensuite la désactiver où elle n'aide pas. J'ai souvent trouvé cela pour gagner d'énormes performances sans avoir à micro-analyser le code.

8
répondu Killroy 2009-09-13 19:29:36

Très difficile de donner une réponse générique à cette question. Cela dépend vraiment de votre domaine de problèmes et de l'implémentation technique. Une technique générale qui est assez neutre du point de vue du langage: identifier les points chauds de code qui ne peuvent pas être éliminés et optimiser le code assembleur à la main.

7
répondu dschwarz 2009-05-29 14:32:36

depuis quelques % est un très CPU et de la demande dépendante de la chose....

  • les architectures de cache diffèrent, certaines puces ont une mémoire vive vous pouvez mapper directement, de BRAS (parfois) ont un vecteur unité, SH4 est une matrice utile opcode. Y a-t-il un GPU - peut-être qu'un shader est la solution. TMS320 ' s sont très sensible aux branches dans les boucles (donc séparer les boucles et déplacer les conditions à l'extérieur si possible).

la liste continue.... Mais ce genre de choses sont vraiment le dernier recours...

Construire pour les architectures x86, et exécuter Valgrind /Cachegrind à l'encontre du code pour le profilage des performances. Ou Texas Instruments" CCStudio a un profileur doux. Alors tu sauras vraiment où concentrer...

7
répondu Peter Mortensen 2009-09-13 16:03:00

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

pour tous les projets non-offline, Tout en ayant le meilleur logiciel et le meilleur matériel, si votre débit est faible, alors cette ligne mince va presser les données et vous donner des retards, bien que dans les millisecondes... mais si vous parlez à la dernière goutte, c'est un quelques gouttes gagné, 24/7 pour toute packge envoyé ou reçu.

7
répondu Sam 2011-01-29 02:23:07

j'ai passé un certain temps à travailler sur l'optimisation des systèmes d'affaires client/serveur fonctionnant sur des réseaux à faible bande passante et à longue latence (par exemple satellite, distant, offshore), et j'ai été en mesure d'obtenir des améliorations de performance spectaculaires avec un processus assez reproductible.

  • mesure : commencez par comprendre la capacité sous-jacente du réseau et la topologie. S'entretenir avec les personnes concernées par le réseautage dans l'entreprise, et utilisez des outils de base tels que le ping et le traceroute pour établir (au minimum) la latence du réseau à partir de l'emplacement de chaque client, pendant les périodes opérationnelles habituelles. Ensuite, prenez des mesures précises du temps des fonctions de l'utilisateur final qui affichent les symptômes problématiques. Consignez toutes ces mesures, ainsi que leur emplacement, leur date et leur heure. Envisagez d'intégrer la fonctionnalité "network performance testing" de l'utilisateur final dans votre application client, permettant à vos utilisateurs de pouvoir de participer à la processus d'amélioration; les habiliter comme ceci peut avoir un énorme impact psychologique quand vous avez affaire à des utilisateurs frustrés par un système peu performant.

  • analyser : utiliser toutes les méthodes d'enregistrement disponibles pour établir exactement quelles données sont transmises et reçues pendant l'exécution des opérations concernées. Idéalement, votre application peut capturer les données transmises et reçu par le client et le serveur. Si ceux-ci incluent des horodateurs aussi bien, encore mieux. S'il n'y a pas suffisamment de journalisation (par exemple, système fermé ou incapacité de déployer des modifications dans un environnement de production), utilisez un renifleur de réseau et assurez-vous de bien comprendre ce qui se passe au niveau du réseau.

  • Cache : recherchez les cas où des données statiques ou rarement modifiées sont transmises de façon répétitive et considérons approprié stratégie de mise en cache. Les exemples typiques comprennent les valeurs de "liste de sélection "ou d'autres" entités de référence", qui peuvent être étonnamment grandes dans certaines applications d'affaires. Dans de nombreux cas, les utilisateurs peuvent accepter qu'ils doivent redémarrer ou rafraîchir l'application pour mettre à jour des données rarement mises à jour, surtout si elle peut réduire le temps de l'affichage des éléments d'interface utilisateur couramment utilisés. Assurez - vous de comprendre le comportement réel des éléments de cache déjà déployés- beaucoup les méthodes de mise en cache courantes (par exemple HTTP ETag) nécessitent toujours un aller-retour réseau pour assurer la cohérence, et lorsque la latence réseau est coûteuse, vous pouvez être en mesure de l'éviter complètement avec une approche de mise en cache différente.

  • Parallelise : rechercher les transactions séquentielles qui n'ont pas logiquement besoin d'être émises strictement séquentiellement, et retravailler le système pour les émettre en parallèle. J'ai traité d'un cas où une demande de bout en bout avait un délai réseau inhérent de ~2s, ce qui n'était pas un problème pour une seule transaction, mais lorsque 6 allers-retours 2S séquentiels ont été nécessaires avant que l'Utilisateur a repris le contrôle de l'application client, il est devenu une énorme source de frustration. Le fait de découvrir que ces transactions étaient en fait indépendantes a permis de les exécuter en parallèle, réduisant le délai de l'utilisateur final à un niveau très proche du coût d'un seul aller-retour.

  • combinent : lorsque les requêtes séquentielles doivent être exécutées de manière séquentielle, cherchez des occasions de les combiner en une seule requête plus complète. Les exemples typiques incluent la création de nouvelles entités, suivies par des demandes concernent les entités à d'autres entités existantes.

  • compresse : cherchez des occasions de tirer parti de la compression de la charge utile, soit en remplaçant une forme textuelle par une binaire un, ou en utilisant la technologie de compression réelle. De nombreuses piles de technologies modernes (c.-à-d. moins de dix ans) supportent ce système de façon presque transparente, alors assurez-vous qu'il est configuré. J'ai souvent été surpris par l'impact significatif de la compression où il semblait clair que le problème était fondamentalement la latence plutôt que la bande passante, découvrant après le fait qu'elle permettait à la transaction de s'adapter à l'intérieur d'un seul paquet ou d'éviter autrement la perte de paquet et ont donc un impact sur performance.

  • je répète : retournez au début et réévaluez vos opérations (aux mêmes endroits et aux mêmes heures) avec les améliorations en place, Enregistrez et signalez vos résultats. Comme pour toute optimisation, certains problèmes peuvent avoir été résolus en exposant d'autres qui dominent maintenant.

dans les étapes ci-dessus, je me concentre sur le processus d'optimisation lié à l'application, mais bien sûr vous doit s'assurer que le réseau sous-jacent lui-même est configuré de la manière la plus efficace pour soutenir votre application aussi. Engagez les spécialistes du réseautage dans l'entreprise et déterminez s'ils sont en mesure d'appliquer des améliorations de la capacité, de la qualité de service, de la compression du réseau ou d'autres techniques pour régler le problème. Généralement, ils ne comprennent pas les besoins de votre application, il est donc important que vous êtes équipé (après L'étape D'Analyse) pour en discuter avec eux, et aussi de faire l'analyse de rentabilisation pour tous les coûts vous allez leur demander de subir. J'ai rencontré des cas où une configuration réseau erronée a fait que les données des applications étaient transmises par une liaison satellite lente plutôt que par une liaison terrestre, simplement parce qu'elle utilisait un port TCP qui n'était pas "bien connu" par les spécialistes du réseau; évidemment, corriger un problème comme celui-ci peut avoir un impact dramatique sur les performances, sans modification du code logiciel ou de la configuration nécessaire.

7
répondu Pat 2012-04-17 04:03:18

pas aussi approfondi ou complexe que les réponses précédentes, mais voici: (niveau débutant/intermédiaire)

  • évident: sec
  • exécuter des boucles à l'envers de sorte que vous êtes toujours comparer à 0 plutôt que d'une variable
  • utilisez des opérateurs bitwise chaque fois que vous le pouvez
  • fractionner le code répétitif en modules/fonctions
  • objets de cache
  • local les variables ont un léger avantage sur le plan de la performance
  • manipulation de chaîne de limite autant que possible
6
répondu Aaron 2012-08-30 16:01:33

Impossible à dire. Cela dépend à quoi ressemble le code. Si nous pouvons supposer que le code existe déjà, il suffit de regarder et de comprendre, comment l'optimiser.

meilleure localisation de cache, déroulement de boucle, essayer d'éliminer les longues chaînes de dépendances, pour obtenir un meilleur parallélisme d'instruction. Dans la mesure du possible, privilégier les mouvements conditionnels par rapport aux branches. Exploiter les instructions SIMD lorsque c'est possible.

comprendre ce qu'est votre code faire, et comprendre le matériel sur lequel il tourne. Ensuite, il devient assez simple de déterminer ce que vous devez faire pour améliorer les performances de votre code. C'est le seul conseil général auquel je puisse penser.

bien, cela, et "montrer le code sur SO et demander des conseils d'optimisation pour ce morceau de code spécifique".

5
répondu jalf 2009-05-29 15:10:12

si un meilleur matériel est une option alors certainement aller pour cela. Autrement

  • vérifiez que vous utilisez les meilleures options de compilateur et de linker.
  • si la routine hotspot dans une autre bibliothèque pour l'appelant fréquent, envisager de le déplacer ou de le cloner dans le module appelants. Élimine une partie de l'appel et peut améliorer les hits de cache (voir comment AIX relie statiquement strcpy() dans des objets partagés liés séparément). Ceci pourrait diminuer cache frappe également, c'est pourquoi une mesure.
  • voir s'il est possible d'utiliser une version spécialisée de la routine hotspot. L'inconvénient est plus d'une version à maintenir.
  • Regardez l'assembleur. Si vous pensez que cela pourrait être mieux, examiner pourquoi le compilateur n'a pas à comprendre cela, et comment vous pouvez aider le compilateur.
  • Considérer: êtes-vous vraiment à l'aide de l'algorithme le mieux adapté? Est-il le meilleur algorithme pour votre taille?
5
répondu mealnor 2009-09-13 15:52:22

google est une option "Cache.. Dans la mesure du possible, Ne touchez pas le disque "

5
répondu asyncwait 2009-10-09 11:01:10

voici quelques techniques d'optimisation rapides et sales que j'utilise. Je considère qu'il s'agit d'une optimisation de "premier passage".

apprendre où le temps est passé savoir exactement ce qui prend le temps. Est-il fichier IO? C'est L'heure du CPU? Est-ce le réseau? Est-il la Base de données? Il est inutile d'optimiser pour IO si ce n'est pas le goulot d'étranglement.

connaître son environnement savoir où optimiser dépend généralement de l'environnement de développement. Dans VB6, par exemple, passer par référence est plus lent que passer par valeur, mais dans C et c++, par référence est beaucoup plus rapide. Dans C, il est raisonnable d'essayer quelque chose et de faire quelque chose de différent si un code de retour indique un échec, alors que dans Dot Net, les exceptions de capture sont beaucoup plus lentes que la vérification d'une condition valide avant d'essayer.

Indices Construire des indices les plus fréquemment interrogé la base de données Fields. Vous pouvez presque toujours échanger l'espace pour la vitesse.

Evite les recherches à l'intérieur de la boucle à optimiser, j'évite d'avoir à faire des recherches. Trouver l'offset et / ou l'index à l'extérieur de la boucle et réutiliser les données à l'intérieur.

Minimiser IO essayer de concevoir d'une manière qui réduit le nombre de fois que vous avez de lire ou d'écrire surtout sur un réseau de connexion

Réduisez les Abstractions plus le code doit travailler à travers les couches d'abstraction, plus il est lent. À l'intérieur de la boucle critique, réduire les abstractions (par exemple révéler des méthodes de niveau inférieur qui évitent le code supplémentaire)

Spawn Threads pour les projets avec une interface utilisateur, déclenchant un nouveau thread à la préforme plus lente des tâches qui rend l'application sentir plus réactif, bien que ne l'est pas.

Pré-traitement vous pouvez généralement échanger l'espace pour la vitesse. S'il y a des calculs ou d'autres opérations intenses, voyez si vous pouvez précalculer certaines informations avant d'être dans la boucle critique.

5
répondu Andrew Neely 2011-07-20 13:15:33

changer la disposition de vos données peut parfois aider. En C, vous pouvez passer d'un tableau ou les structures d'une structure de tableaux, ou vice versa.

4
répondu Nosredna 2009-05-29 22:20:29

Bidouiller l'OS et le cadre.

cela peut sembler exagéré, mais pensez-y comme ceci: les systèmes D'exploitation et les cadres sont conçus pour faire beaucoup de choses. Votre demande n'choses très spécifiques. Si vous pouviez faire en sorte que L'OS fasse exactement ce dont votre application a besoin et que votre application comprenne comment le cadre (php,.net,java) fonctionne, vous pourriez obtenir beaucoup mieux de votre matériel.

Facebook, par exemple, ils ont changé le niveau du noyau thingys sous Linux, ils ont changé le fonctionnement de memcached (par exemple, ils ont écrit un proxy memcached, et a utilisé udp au lieu de tcp ).

Window2008 en est un autre exemple. Win2K8 a une version où vous pouvez installer juste le système D'exploitation de base nécessaire pour exécuter x applicaions (par exemple Web-Apps, Server Apps). Cela réduit une grande partie des frais généraux que L'OS a sur les processus en cours d'exécution et vous donne une meilleure performance.

bien sûr, vous devriez toujours jeter plus de matériel comme première étape...

4
répondu Nir Levy 2010-01-17 13:42:02

passage par référence et non par valeur

4
répondu l--''''''---------'''''''''''' 2011-03-06 05:21:33

réduire les tailles variables (dans les systèmes embarqués)

si la taille de votre variable est plus grande que la taille du mot sur une architecture spécifique, elle peut avoir un effet significatif à la fois sur la taille du code et la vitesse. Par exemple, si vous avez un système 16 bits, et utilisez une variable long int très souvent, et plus tard se rendre compte qu'il ne peut jamais sortir de la gamme (-32.768 ... 32.767) envisager de la réduire à short int.

de mon expérience personnelle, si un programme est prêt ou presque prêt, mais nous réalisons qu'il prend environ 110% ou 120% de la mémoire du programme du matériel cible, une normalisation rapide des variables résout généralement le problème plus souvent qu'autrement.

à ce moment-là, l'optimisation des algorithmes ou de parties du code lui-même peut devenir désespérément futile:

  • réorganiser l'ensemble de la structure et le programme ne fonctionne plus comme prévu, ou du moins vous introduisez beaucoup d'insectes.
  • faites quelques astuces astucieuses: habituellement vous passez beaucoup de temps à optimiser quelque chose, et découvrez pas ou très petite diminution de la taille du code, comme le compilateur l'aurait optimisé de toute façon.

beaucoup de gens font l'erreur d'avoir des variables qui stockent exactement la valeur numérique d'une unité pour laquelle ils utilisent la variable: par exemple, leur variable time stocke le nombre exact de millisecondes, même si seulement le temps les étapes de dire 50 ms sont pertinentes. Peut-être que si votre variable représentait 50 ms pour chaque incrément d'un, vous seriez en mesure de rentrer dans une variable plus petite ou égale à la taille du mot. Sur un système 8 bits, par exemple, même une simple addition de deux variables 32 bits génère une bonne quantité de code, surtout si vous êtes bas sur les registres, alors que les additions 8 bits sont à la fois petites et rapides.

4
répondu vsz 2011-06-25 16:03:01