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, d'après mon expérience. J'ai essayé d'expliquer cela dans un article du Dr Dobbs en novembre 1993, en partant d'un programme non trivial conventionnellement bien conçu sans gaspillage évident et en passant par une série d'optimisations jusqu'à ce que son temps d'horloge murale soit réduit de 48 secondes. à 1,1 seconde, et la taille du code source a été réduite d'un facteur 4. Mon outil de diagnostic était le suivant . La séquence de changements était la suivante:
Le premier problème rencontré a été l'utilisation de clusters 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, ce qui a réduit le temps à 20 secondes.
Maintenant, le plus grand preneur de temps est plus de construction de listes. En pourcentage, il n'était pas si important auparavant, 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 plus petits sur lesquels je peux faire quelque chose, et le temps passe à 13 secondes.
Maintenant, il me semble que j'ai 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. Ensuite, je réfléchis à la conception de base du programme, à sa structure axée sur les transactions, et je demande si toutes les recherches de liste qu'il fait sont réellement imposées par les exigences du problème.
Ensuite, je suis tombé sur une refonte, 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 trouve pas constamment des choses que le programmeur sait être assez prévisibles. En d'autres termes, n'interprétez pas la séquence des choses à faire, ne la "compilez" pas.
- Cette refonte est effectuée, réduisant le code source d'un facteur 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 d'origine.
Plus de diagnostic révèle qu'il passe du temps à gérer les files d'attente. L'intégration de ces éléments réduit le temps à 7 secondes.
Maintenant, un grand preneur de temps est l'impression de diagnostic que je faisais. Rincer - 4 secondes.
Maintenant, les plus grands preneurs de temps sont les appels à malloc et gratuits . Recycler les objets - 2,6 secondes.
En continuant à échantillonner, je trouve toujours des opérations qui ne sont pas strictement 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 choses faciles, puis les plus difficiles, jusqu'à ce que vous arriviez à un point de rendements décroissants. Ensuite, les informations que vous obtiendrez pourraient bien conduire à une refonte, en commençant 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(;;)
ouwhile(1)
sont plus rapides: le genre de questions que je vois si souvent sur Stack Overflow.
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, qui empile les échantillons avec précision. Les profileurs, même aujourd'hui, viennent à peine à l'idée que les instructions et les instructions d'appel sont plus importantes à localiser et plus faciles à corriger que des fonctions entières.
J'ai en fait construit un profileur pour le faire, mais pour une réelle intimité avec ce que fait le code, il n'y a pas de substitut pour mettre les doigts dedans. Ce n'est pas un problème que le nombre d'échantillons soit petit, car aucun des problèmes détectés n'est si minuscule qu'ils sont facilement ignorés.
AJOUT: jerryjvl a demandé quelques exemples. Voici le premier problème. Il se compose d'un petit nombre de lignes de code distinctes, prenant ensemble 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)
Celles-ci utilisaient le cluster de listes ILST (similaire à une classe de liste). Ils sont mis en œuvre de la manière habituelle, la «dissimulation d'informations» signifiant que les utilisateurs de la classe n'étaient pas censés avoir à se soucier de la façon 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 qu'elles pouvaient être un "goulot d'étranglement" (je déteste ce mot). Ils sont simplement la façon recommandée de faire les choses. Il est facile de dire avec le recul que cela aurait dû être évité, mais d'après mon expérience, tous les problèmes de performances sont comme ça. En général, il est bon d'essayer d'éviter de créer des problèmes de performances. Il est encore mieux de trouver et de corriger ceux qui sont créés, même s'ils "auraient dû être évités" (avec le recul).
Voici le deuxième problème, sur deux lignes distinctes:
/* ADD TASK TO TASK LIST */
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)
Il s'agit de créer des listes en ajoutant des éléments à leurs extrémités. (Le correctif consistait à collecter les éléments dans des tableaux et à créer les listes en une seule fois.) La chose intéressante est que ces instructions ne coûtaient (c.-à-d. Étaient sur la pile des appels) 3/48 du temps d'origine, donc elles n'étaient pas dans fait un gros problème au début . Cependant, après avoir éliminé le premier problème, ils coûtaient 3/20 du temps et étaient donc maintenant un "plus gros poisson". En général, c'est comme ça que ça se passe.
Je pourrais ajouter que ce projet a été distillé d'un vrai projet sur lequel j'ai aidé. Dans ce projet, les problèmes de performances étaient beaucoup plus dramatiques (tout comme les accélérations), comme l'appel d'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, à la fois original et remanié, peut être trouvé sur 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 passe que 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 toujours une accélération de 2 à 3 ordres de grandeur.
Suggestions:
Inconvénients : si peu des valeurs précalculées sont réellement utilisées, cela peut aggraver les choses, la recherche peut également prendre beaucoup de mémoire.
Inconvénients : l'écriture de code supplémentaire signifie plus de surface pour les bogues.
Inconvénients : Eh bien ... la réponse ne sera pas exacte.
la source
Lorsque vous ne pouvez plus améliorer les performances, voyez si vous pouvez améliorer les performances perçues à la place.
Vous ne pourrez peut-être pas rendre votre algorithme fooCalc plus rapide, mais il existe souvent des moyens de rendre votre application plus sensible à l'utilisateur.
Quelques exemples:
Cela ne rendra pas votre programme plus rapide, mais cela pourrait rendre vos utilisateurs plus heureux de la vitesse que vous avez.
la source
Je passe la majeure partie de ma vie à cet endroit. Les grandes lignes consistent à exécuter votre profileur et à le faire enregistrer:
__restrict
généreusement pour promettre au compilateur d'alias.Et encore une chose que j'aime faire:
la source
Examples on the PowerPC ...
<- Autrement dit, certaines implémentations de PowerPC. PowerPC est un ISA, pas un CPU.lea
.Jetez plus de matériel dessus!
la source
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, alors débarrassez-vous de toutes les E / S dont vous n'avez pas strictement besoin.
Déplacer les E / S à l'avant : chargez toutes les données dont vous aurez besoin pour un calcul à l'avance, de sorte que vous n'ayez pas d'attentes d'E / S répétées dans le cœur d'un algorithme critique (et peut-être par conséquent répétées recherche de disque, lorsque le chargement de toutes les données en une seule fois peut éviter de chercher).
Retardez les E / S : n'écrivez pas vos résultats avant la fin du calcul, stockez-les dans une structure de données, puis enregistrez-les en une seule fois à la fin lorsque le travail est terminé.
E / S filetées : pour ceux qui osent assez, combinez les `` E / S à l'avance '' ou les `` E / S différées '' avec le calcul réel en déplaçant le chargement dans un thread parallèle, de sorte que pendant que vous chargez plus de données, vous pouvez travailler sur un calcul sur les données que vous avez déjà, ou pendant que vous calculez le prochain lot de données, vous pouvez simultanément écrire les résultats du dernier lot.
la source
mmap()
pour l'entrée, faire desmadvise()
appels appropriés et utiliseraio_write()
pour écrire de gros morceaux de sortie (= quelques Mio).Étant donné que de nombreux problèmes de performances impliquent des problèmes de base de données, je vais vous donner quelques éléments spécifiques à examiner 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 également les boucles. La plupart du temps, l'accès aux données doit être basé sur un ensemble, et non pas enregistrement par enregistrement. Cela inclut de ne pas réutiliser une seule procédure stockée d'enregistrements lorsque vous souhaitez insérer 1 000 000 d'enregistrements à la fois.
N'utilisez jamais select *, renvoyez uniquement 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 entraîneront ainsi une charge inutile sur le serveur et le réseau.
Évitez l'utilisation de sous-requêtes corrélées. Utilisez les jointures (y compris les jointures aux tables dérivées lorsque cela est possible) (je sais que cela est vrai pour Microsoft SQL Server, mais testez les conseils lors de l'utilisation d'un backend différent).
Index, index, index. Et obtenez ces statistiques mises à jour si elles s'appliquent à votre base de données.
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 similaire ou d'une fonction dans la jointure ou comme partie gauche d'une instruction where.
Utilisez des types de données corrects. Il est plus rapide de faire 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 de faire 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. Vérifiez-les d'abord pour voir où se trouvent les problèmes.
Tenez compte de la fréquence d'exécution de la requête ainsi que du temps nécessaire à son exécution pour déterminer ce qui doit être optimisé. Parfois, vous pouvez obtenir plus de performances d'un léger ajustement à une requête qui s'exécute des millions de fois par jour que d'essuyer une heure de longue durée qui ne s'exécute qu'une fois par mois.
Utilisez une sorte d'outil de profilage pour savoir ce qui est réellement envoyé vers et depuis la base de données. Je me souviens d'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 avait découvert grâce au 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 tout en s'exécutant seules peuvent devenir très lentes en raison des verrous d'autres requêtes.
la source
Le facteur limitant le plus important aujourd'hui est la bande passante à mémoire limitée . Les multicœurs ne font qu'aggraver la situation, 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, ce qui aggrave encore ce problème. Enfin, la signalisation inter-puce nécessaire pour maintenir la cohérence des différents caches augmente également avec un nombre accru de cœurs. Cela ajoute également une pénalité.
Ce sont les effets que vous devez gérer. Parfois par la micro-gestion de votre code, mais parfois par une réflexion et une refactorisation soigneuses.
De nombreux commentaires mentionnent déjà un code compatible avec le cache. Il existe au moins deux saveurs distinctes:
Le premier problème concerne spécifiquement la régularisation de vos modèles d'accès aux données, permettant au préfetcher matériel de fonctionner efficacement. Évitez l'allocation dynamique de mémoire qui répartit vos objets de données dans la mémoire. Utilisez des conteneurs linéaires au lieu de listes, de hachages 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 tiennent dans le cache disponible et réutilisez ces données autant que possible pendant qu'elles sont encore dans le cache.
Le fait de regrouper les données plus étroitement et de vous assurer que vous utilisez toutes les données dans les lignes de cache dans les boucles actives, vous aidera à éviter ces autres effets et permettra d'ajuster des données plus utiles dans le cache.
la source
la source
Bien que j'aime la réponse de Mike Dunlavey, en fait c'est une excellente réponse avec un exemple à l'appui, je pense qu'elle pourrait être exprimée très simplement ainsi:
Découvrez d'abord ce qui prend le plus de temps et comprenez pourquoi.
C'est le processus d'identification des porcs du temps qui vous aide à comprendre où vous devez affiner votre algorithme. C'est la seule réponse agnostique de langage globale que je puisse trouver à un problème qui est déjà censé être entièrement optimisé. En supposant également que vous souhaitiez être indépendant de l'architecture dans votre quête de vitesse.
Ainsi, alors que l'algorithme peut être optimisé, sa mise en œuvre peut ne pas l'être. L'identification vous permet de savoir de quelle pièce il s'agit: algorithme ou implémentation. Donc, selon le nombre de porcs le plus important, il est votre principal candidat à l'examen. Mais puisque vous dites que vous voulez extraire les derniers%, vous voudrez peut-être également examiner les parties moindres, les parties que vous n'avez pas examinées de si près au début.
Enfin, un peu d'essais et d'erreurs avec des chiffres de performances sur différentes façons de mettre en œuvre la même solution, ou des algorithmes potentiellement différents, peuvent apporter des informations qui aident à identifier les pertes de temps et les gains de temps.
HPH, asoudmove.
la source
Vous devriez probablement considérer la "perspective Google", c'est-à-dire déterminer comment votre application peut devenir largement parallélisée et concurrente, ce qui impliquera inévitablement à un moment donné d'envisager de distribuer votre application sur différentes machines et réseaux, afin qu'elle puisse idéalement évoluer presque linéairement avec le matériel que vous lui lancez.
D'autre part, les gens de Google sont également connus pour avoir déployé beaucoup de main-d'œuvre et de ressources pour résoudre certains des problèmes dans les projets, les outils et l'infrastructure qu'ils utilisent, comme par exemple l'optimisation de l'ensemble du programme pour gcc en ayant une équipe d'ingénieurs dédiée pirater les internes de gcc afin de le préparer aux scénarios d'utilisation typiques de Google.
De même, le profilage d'une application ne signifie plus simplement profiler le code du programme, mais aussi tous ses systèmes et infrastructures environnants (pensez réseaux, commutateurs, serveur, matrices RAID) afin d'identifier les redondances et le potentiel d'optimisation du point de vue d'un système.
la source
la source
la source
Diviser et conquérir
Si l'ensemble de données en cours de traitement est trop volumineux, passez en boucle sur des morceaux de celui-ci. Si vous avez bien fait votre code, l'implémentation devrait être facile. Si vous avez un programme monolithique, vous savez maintenant mieux.
la source
Tout d'abord, comme mentionné dans plusieurs réponses précédentes, découvrez ce qui mord vos performances - 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 l'un des livres écrits il y a longtemps par Knuth, l'un de la série "The Art of Computer Programming". Il s'agit très probablement d'une question de tri et de recherche - si ma mémoire est mauvaise, vous devrez découvrir dans lequel il explique comment gérer le stockage lent des données sur bande. Transformer mentalement sa mémoire / bande paire en votre paire de cache / mémoire principale (ou en 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, alors embauchez un informaticien professionnel pour mener une recherche professionnelle. Si votre problème de mémoire est par hasard avec la FFT (le cache manque aux index à bits inversés lorsque vous faites des papillons radix-2), alors n'embauchez pas de scientifique - à la place, optimisez manuellement les passes une par une jusqu'à ce que vous '' évincer jusqu'aux derniers pour cent vrai? Si c'est peu, vous gagnerez probablement.
... s'il s'agit d'un processeur - passez au langage d'assemblage. Étudiez les spécifications du processeur - données - par exemple, vos calculs de flottement peuvent s'avérer plus lents que les doubles pour un processeur particulier. Si vous avez des trucs trigonométriques, combattez-les avec des tables pré-calculées; gardez également à l'esprit 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. ce qui prend des tiques , VLIW, SIMD. Les appels de fonction sont très probablement des mangeurs de tiques remplaçables. Apprenez les transformations de boucle - pipeline, dérouler. Les multiplications et les divisions peuvent être remplaçables / interpolées avec des décalages de bits (les multiplications par de 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 par deux sur 32 ou même 4 sur 16 ou 8 sur 8 bits. Essayez aussi plus longtemps
... s'il s'agit d'un réseau - pensez à compresser les données que vous passez dessus. Remplacez le transfert XML par binaire. Protocoles d'étude. Essayez UDP au lieu de TCP si vous pouvez en quelque sorte gérer la perte de données.
... s'il s'agit d'une base de données, allez sur n'importe quel forum de base de données et demandez conseil. Grille de données en mémoire, optimisation du plan de requête, etc., etc., etc.
HTH :)
la source
Caching!Un moyen peu coûteux (dans l'effort du programmeur) d'accélérer presque n'importe quoi est d'ajouter 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'objets ou de structures. Il est souvent facile d'ajouter 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 simplement ajouter le cache partout, puis le désactiver là où cela n'aide pas. J'ai souvent trouvé cela pour gagner des performances énormes sans avoir à micro-analyser le code.
la source
Je pense que cela a déjà été dit d'une manière différente. Mais lorsque vous avez affaire à un algorithme gourmand en processeur, vous devez tout simplifier dans 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 indépendamment de la langue avec laquelle je travaille. Si vous avez affaire à des boucles imbriquées, par exemple, et que vous trouvez la possibilité de réduire le code d'un niveau, vous pouvez dans certains cas accélérer considérablement 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 prises en compte pour votre boucle la plus intérieure.
Parfois, il peut être avantageux d'effectuer vos opérations mathématiques sur un 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.
la source
J'ai passé un peu de temps à optimiser les systèmes d'entreprise client / serveur fonctionnant sur des réseaux à faible bande passante et à longue latence (par exemple satellite, distant, offshore), et j'ai pu obtenir des améliorations de performances spectaculaires avec un processus assez répétable.
Mesurer : Commencez par comprendre la capacité et la topologie sous-jacentes du réseau. Parler aux personnes en réseau concernées dans l'entreprise et utiliser des outils de base tels que ping et traceroute pour établir (au minimum) la latence du réseau à partir de chaque emplacement client, pendant les périodes opérationnelles typiques. Ensuite, prenez des mesures précises du temps de fonctions spécifiques de l'utilisateur final qui affichent les symptômes problématiques. Enregistrez toutes ces mesures, ainsi que leurs emplacements, dates et heures. Envisagez d'intégrer la fonctionnalité de «test des performances réseau» de l'utilisateur final dans votre application client, permettant à vos utilisateurs expérimentés de participer au processus d'amélioration; leur donner de telles capacités peut avoir un impact psychologique énorme lorsque vous avez affaire à des utilisateurs frustrés par un système peu performant.
Analyser : en utilisant toutes les méthodes de journalisation disponibles pour établir exactement quelles données sont transmises et reçues pendant l'exécution des opérations affectées. Idéalement, votre application peut capturer des données transmises et reçues à la fois par le client et le serveur. Si ceux-ci incluent également des horodatages, c'est encore mieux. Si une journalisation suffisante n'est pas disponible (par exemple, système fermé ou incapacité à 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 manière répétitive et envisagez une stratégie de mise en cache appropriée. Les exemples typiques incluent les valeurs de «liste de choix» ou d'autres «entités de référence», qui peuvent être étonnamment grandes dans certaines applications commerciales. Dans de nombreux cas, les utilisateurs peuvent accepter qu'ils doivent redémarrer ou actualiser l'application pour mettre à jour des données rarement mises à jour, surtout si cela peut réduire considérablement le temps d'affichage des éléments d'interface utilisateur couramment utilisés. Assurez-vous de comprendre le comportement réel des éléments de mise en cache déjà déployés - de nombreuses méthodes de mise en cache courantes (par exemple HTTP ETag) nécessitent toujours un aller-retour réseau pour garantir la cohérence, et lorsque la latence du réseau est coûteuse, vous pourrez peut-être l'éviter complètement avec une approche de mise en cache différente.
Paralléliser : recherchez les transactions séquentielles qui n'ont logiquement pas besoin d'être émises strictement séquentiellement et retravaillez le système pour les émettre en parallèle. J'ai traité un cas où une demande de bout en bout avait un retard de réseau inhérent de ~ 2 s, ce qui n'était pas un problème pour une seule transaction, mais lorsque 6 allers-retours séquentiels de 2 s étaient nécessaires avant que l'utilisateur ne reprenne le contrôle de l'application cliente , 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, ce qui a réduit le délai de l'utilisateur final à très près du coût d'un aller-retour unique.
Combiner : où les demandes séquentielles doivent être exécutées séquentiellement, recherchez les opportunités de les combiner en une seule demande plus complète. Les exemples typiques incluent la création de nouvelles entités, suivies des demandes de mise en relation de ces entités avec d'autres entités existantes.
Compresser : recherchez des opportunités pour tirer parti de la compression de la charge utile, soit en remplaçant un formulaire textuel par un formulaire binaire, soit en utilisant une technologie de compression réelle. De nombreuses piles technologiques modernes (c'est-à-dire dans une décennie) prennent en charge cela de manière 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'il permettait à la transaction de tenir dans un seul paquet ou d'éviter autrement la perte de paquets et donc d'avoir une taille excessive impact sur les performances.
Répétition : retournez au début et mesurez à nouveau vos opérations (aux mêmes endroits et aux mêmes moments) avec les améliorations en place, enregistrez et rapportez vos résultats. Comme pour toute optimisation, certains problèmes peuvent avoir été résolus, ce qui en expose d'autres qui dominent désormais.
Dans les étapes ci-dessus, je me concentre sur le processus d'optimisation lié à l'application, mais bien sûr, vous devez vous assurer que le réseau sous-jacent lui-même est configuré de la manière la plus efficace pour prendre en charge votre application également. Engagez les spécialistes de la mise en réseau dans l'entreprise et déterminez s'ils sont en mesure d'appliquer des améliorations de capacité, la qualité de service, la compression du réseau ou d'autres techniques pour résoudre le problème. Habituellement, ils ne comprendront pas les besoins de votre application, il est donc important que vous soyez équipé (après l'étape Analyser) pour en discuter avec eux, et également pour faire l'analyse de rentabilisation de tous les coûts que vous allez leur demander d'engager. . J'ai rencontré des cas où une configuration réseau erronée provoquait la transmission des données des applications sur une liaison satellite lente plutôt que sur une liaison terrestre, tout simplement parce qu'il utilisait un port TCP qui n'était pas "bien connu" des spécialistes des réseaux; de toute évidence, la correction d'un problème comme celui-ci peut avoir un impact dramatique sur les performances, sans aucun code logiciel ou changement de configuration nécessaire.
la source
Très difficile de donner une réponse générique à cette question. Cela dépend vraiment de votre domaine de problème et de votre implémentation technique. Une technique générale qui est assez neutre en termes de langage: identifiez les points chauds de code qui ne peuvent pas être éliminés et optimisez manuellement le code assembleur.
la source
Les derniers% sont très dépendants du CPU et des applications ....
La liste continue ... Mais ce genre de choses n'est vraiment que le dernier recours ...
Générez pour x86 et exécutez Valgrind / Cachegrind par rapport au code pour un profilage des performances approprié. Ou CCStudio de Texas Instruments a un profileur doux. Ensuite, vous saurez vraiment où vous concentrer ...
la source
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 hors ligne, tout en ayant le meilleur logiciel et le meilleur matériel, si votre débit est faible, cette ligne mince va compresser les données et vous donner des retards, bien qu'en millisecondes ... mais si vous parlez des dernières gouttes , c'est quelques gouttes gagnées, 24/7 pour tout paquet envoyé ou reçu.
la source
Pas aussi en profondeur ou complexe que les réponses précédentes, mais voici: (ce sont plus de niveau débutant / intermédiaire)
la source
Impossible à dire. Cela dépend de l'apparence du code. Si nous pouvons supposer que le code existe déjà, alors nous pouvons simplement le regarder et comprendre à partir de cela, comment l'optimiser.
Meilleure localité de cache, déroulement de boucle, essayez d'éliminer les longues chaînes de dépendance, pour obtenir un meilleur parallélisme au niveau de l'instruction. Préférez les déplacements conditionnels aux branches lorsque cela est possible. Exploitez les instructions SIMD lorsque cela est possible.
Comprenez ce que fait votre code et comprenez le matériel sur lequel il fonctionne. Ensuite, il devient assez simple de déterminer ce que vous devez faire pour améliorer les performances de votre code. C'est vraiment le seul conseil vraiment général auquel je puisse penser.
Eh bien, cela et "Afficher le code sur SO et demander des conseils d'optimisation pour ce morceau de code spécifique".
la source
Si un meilleur matériel est une option, optez pour cela. Autrement
la source
La manière google est une option "Cachez-le .. Dans la mesure du possible, ne touchez pas le disque"
la source
Voici quelques techniques d'optimisation rapides et sales que j'utilise. Je considère qu'il s'agit d'une optimisation de «premier passage».
Apprenez où le temps est passé Découvrez exactement ce qui prend le temps. S'agit-il d'un fichier IO? Est-ce le temps CPU? Est-ce le réseau? Est-ce la base de données? Il est inutile d'optimiser pour IO si ce n'est pas le goulot d'étranglement.
Connaissez votre environnement Savoir où optimiser dépend généralement de l'environnement de développement. Dans VB6, par exemple, le passage par référence est plus lent que le passage par valeur, mais en C et C ++, par référence est beaucoup plus rapide. En C, il est raisonnable d'essayer quelque chose et de faire quelque chose de différent si un code retour indique un échec, tandis que dans Dot Net, intercepter des exceptions est beaucoup plus lent que de vérifier une condition valide avant de tenter.
Index Créez des index sur les champs de base de données fréquemment interrogés. Vous pouvez presque toujours échanger de l'espace contre de la vitesse.
Évitez les recherches À l'intérieur de la boucle pour être optimisé, j'évite d'avoir à faire des recherches. Trouvez le décalage et / ou l'index à l'extérieur de la boucle et réutilisez les données à l'intérieur.
Réduisez au minimum les tentatives d' E / S de manière à réduire le nombre de fois où vous devez lire ou écrire, en particulier via une connexion réseau
Réduire les abstractions Plus le code doit traiter de couches d'abstraction, plus il est lent. À l'intérieur de la boucle critique, réduisez les abstractions (par exemple, révélez des méthodes de niveau inférieur qui évitent le code supplémentaire)
Threads spawn pour les projets avec une interface utilisateur, fraye un nouveau thread pour préformer des tâches plus lentes rend l'application sensation plus sensible, bien que n'est pas.
Pré-processus Vous pouvez généralement échanger de l'espace contre de la vitesse. S'il y a des calculs ou d'autres opérations intenses, voyez si vous pouvez précalculer certaines des informations avant d'être dans la boucle critique.
la source
Si vous avez beaucoup de mathématiques en virgule flottante hautement parallèles, en particulier en simple précision, essayez de les décharger sur un processeur graphique (le cas échéant) en utilisant OpenCL ou (pour les puces NVidia) CUDA. Les GPU ont une immense puissance de calcul en virgule flottante dans leurs shaders, qui est beaucoup plus grande que celle d'un CPU.
la source
Ajout de cette réponse car je ne l'ai pas vue incluse dans toutes les autres.
Minimisez la conversion implicite entre les types et le signe:
Cela s'applique au moins à C / C ++, même si vous pensez déjà vous êtes libre de conversions - parfois il est bon de tester l'ajout d'avertissements du compilateur autour de fonctions qui nécessitent des performances, en particulier attention aux conversions dans les boucles.
GCC spesific: vous pouvez tester cela en ajoutant des pragmas verbeux autour de votre code,
J'ai vu des cas où vous pouvez obtenir une accélération de quelques pour cent en réduisant les conversions générées par des avertissements comme celui-ci.
Dans certains cas, j'ai un en-tête avec des avertissements stricts que je garde inclus pour éviter les conversions accidentelles, mais c'est un compromis car vous pouvez finir par ajouter beaucoup de transtypages à des conversions intentionnelles silencieuses qui peuvent simplement rendre le code plus encombré pour un minimum gains.
la source
Parfois, changer la disposition de vos données peut vous aider. En C, vous pouvez passer d'un tableau ou de structures à une structure de tableaux, ou vice versa.
la source
Ajustez le système d'exploitation 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 application ne fait que des choses très spécifiques. Si vous pouviez faire en sorte que le système d'exploitation réponde exactement aux besoins de votre application et que votre application comprenne comment fonctionne le framework (php, .net, java), vous pourriez tirer un meilleur parti de votre matériel.
Facebook, par exemple, a changé certains trucs au niveau du noyau sous Linux, changé le fonctionnement de memcached (par exemple, ils ont écrit un proxy memcached et utilisé udp au lieu de tcp ).
Window2008 en est un autre exemple. Win2K8 a une version où vous pouvez installer uniquement le système d'exploitation de base nécessaire pour exécuter les applications X (par exemple, Web-Apps, Server Apps). Cela réduit une grande partie des frais généraux que le système d'exploitation a sur les processus en cours d'exécution et vous donne de meilleures performances.
Bien sûr, vous devez toujours ajouter plus de matériel comme première étape ...
la source