Quelles techniques simples utilisez-vous pour améliorer les performances?


21

Je parle de la façon dont nous écrivons des routines simples afin d'améliorer les performances sans rendre votre code plus difficile à lire ... par exemple, c'est le type que nous avons appris:

for(int i = 0; i < collection.length(); i++ ){
   // stuff here
}

Mais, je le fais généralement quand un foreachn'est pas applicable:

for(int i = 0, j = collection.length(); i < j; i++ ){
   // stuff here
}

Je pense que c'est une meilleure approche car elle n'appellera la lengthméthode qu'une seule fois ... ma petite amie dit que c'est cryptique cependant. Y a-t-il une autre astuce simple que vous utilisez sur vos propres développements?


34
+1 juste pour avoir une petite amie qui vous dira quand votre code n'est pas clair.
Kristo

76
Vous postez juste ceci pour nous dire que vous avez une petite amie.
Josh K

11
@Christian: N'oubliez pas qu'il existe des optimisations du compilateur qui pourraient le faire pour vous, de sorte que vous n'affectez que la lisibilité et n'affectez pas du tout les performances; L'optimisation prématurée est la racine de tout mal ... Essayez d'éviter plus d'une déclaration ou d'une affectation sur la même ligne, ne faites pas lire les gens deux fois ... Vous devriez utiliser la manière normale (votre premier exemple) ou mettre le deuxième déclaration en dehors de la boucle for (bien que cela diminue également la lisibilité car vous auriez besoin de relire pour voir ce que signifie le j).
Tamara Wijsman

5
@TomWij: La citation correcte (et complète): "Nous devons oublier les petites efficacités, disons environ 97% du temps: l'optimisation prématurée est la racine de tous les maux. Pourtant, nous ne devons pas laisser passer nos opportunités dans ces 3% critiques." "
Robert Harvey

3
@tomwij: Si vous dépensez les trois pour cent, alors par définition, vous devriez le faire dans un code à temps critique, et ne pas perdre votre temps avec les 97% restants.
Robert Harvey

Réponses:


28

insérer une discussion prématurée est la racine de tous les maux

Cela dit, voici quelques habitudes que j'ai adoptées pour éviter une efficacité inutile et, dans certains cas, rendre mon code plus simple et plus correct également.

Ce n'est pas une discussion des principes généraux, mais de certaines choses à prendre en compte pour éviter d'introduire des inefficacités inutiles dans le code.

Connaissez votre big-O

Cela devrait probablement être fusionné dans la longue discussion ci-dessus. C'est à peu près le bon sens qu'une boucle à l'intérieur d'une boucle, où la boucle intérieure répète un calcul, va être plus lente. Par exemple:

for (i = 0; i < strlen(str); i++) {
    ...
}

Cela prendra un temps horrible si la chaîne est vraiment longue, car la longueur est recalculée à chaque itération de la boucle. Notez que GCC optimise réellement ce cas car il strlen()est marqué comme une fonction pure.

Lors du tri d'un million d'entiers 32 bits, le tri à bulles serait la mauvaise façon de procéder . En général, le tri peut être effectué en temps O (n * log n) (ou mieux, dans le cas du tri radix), donc à moins que vous ne sachiez que vos données seront petites, recherchez un algorithme qui est au moins O (n * log n).

De même, lorsque vous traitez avec des bases de données, faites attention aux index. Si vous SELECT * FROM people WHERE age = 20, et que vous n'avez pas d'index sur les personnes (âge), cela nécessitera un scan séquentiel O (n) plutôt qu'un scan d'index O (log n) beaucoup plus rapide.

Hiérarchie arithmétique entière

Lors de la programmation en C, gardez à l'esprit que certaines opérations arithmétiques sont plus chères que d'autres. Pour les entiers, la hiérarchie se présente comme suit (la moins chère en premier):

  • + - ~ & | ^
  • << >>
  • *
  • /

Certes, le compilateur généralement des choses comme Optimize n / 2à n >> 1automatiquement si vous ciblez un ordinateur grand public, mais si vous ciblez un appareil embarqué, vous pourriez ne pas obtenir ce luxe.

Aussi, % 2et & 1ont une sémantique différente. La division et le module arrondissent généralement vers zéro, mais leur implémentation est définie. Bon vieux >>et &toujours arrondi vers l'infini négatif, ce qui (à mon avis) a beaucoup plus de sens. Par exemple, sur mon ordinateur:

printf("%d\n", -1 % 2); // -1 (maybe)
printf("%d\n", -1 & 1); // 1

Par conséquent, utilisez ce qui a du sens. Ne pensez pas que vous êtes un bon garçon en utilisant % 2quand vous alliez à l'origine écrire & 1.

Opérations en virgule flottante coûteuses

Évitez les opérations lourdes en virgule flottante comme pow()et log()dans le code qui n'en a pas vraiment besoin, en particulier lorsqu'il s'agit d'entiers. Prenons, par exemple, la lecture d'un nombre:

int parseInt(const char *str)
{
    const char *p;
    int         digits;
    int         number;
    int         position;

    // Count the number of digits
    for (p = str; isdigit(*p); p++)
        {}
    digits = p - str;

    // Sum the digits, multiplying them by their respective power of 10.
    number = 0;
    position = digits - 1;
    for (p = str; isdigit(*p); p++, position--)
        number += (*p - '0') * pow(10, position);

    return number;
}

Non seulement cette utilisation de pow()(et les conversions int<-> doublenécessaires pour l'utiliser) est assez chère, mais elle crée une opportunité de perte de précision (incidemment, le code ci-dessus n'a pas de problèmes de précision). C'est pourquoi je grimace quand je vois ce type de fonction utilisé dans un contexte non mathématique.

Notez également que l'algorithme "intelligent" ci-dessous, qui se multiplie par 10 à chaque itération, est en fait plus concis que le code ci-dessus:

int parseInt(const char *str)
{
    const char *p;
    int         number;

    number = 0;
    for (p = str; isdigit(*p); p++) {
        number *= 10;
        number += *p - '0';
    }

    return number;
}

Réponse très complète.
Paddyslacker

1
Notez que la discussion sur l'optimisation prématurée ne s'applique pas au code garbage. Vous devez toujours utiliser une implémentation qui fonctionne bien en premier lieu.

Notez que GCC optimise réellement ce cas car strlen () est marqué comme une fonction pure. Je pense que vous voulez dire que c'est une fonction const, pas pure.
Andy Lester

@Andy Lester: En fait, je voulais dire pure. Dans la documentation de GCC , il indique que const est légèrement plus strict que pure en ce sens qu'une fonction const ne peut pas lire la mémoire globale. strlen()examine la chaîne pointée par son argument pointeur, ce qui signifie qu'elle ne peut pas être const. Aussi, strlen()est en effet marqué comme pur dans la glibcstring.h
Joey Adams

Vous avez raison, mon erreur, et j'aurais dû revérifier. J'ai travaillé sur les fonctions d'annotation du projet Parrot comme ou pureou constet je l'ai même documenté dans le fichier d'en-tête en raison de la différence subtile entre les deux. docs.parrot.org/parrot/1.3.0/html/docs/dev/c_functions.pod.html
Andy Lester

13

D'après votre question et le fil de commentaires, il semble que vous "pensez" que ce changement de code améliore les performances, mais vous ne savez pas vraiment si c'est le cas ou non.

Je suis fan de la philosophie de Kent Beck :

"Faites-le fonctionner, faites-le bien, faites-le vite."

Ma technique pour améliorer les performances du code, consiste d'abord à faire passer le code aux tests unitaires et à bien le factoriser, puis (en particulier pour les opérations de bouclage) à écrire un test unitaire qui vérifie les performances, puis à refactoriser le code ou à penser à un algorithme différent si celui que je '' ve choisi ne fonctionne pas comme prévu.

Par exemple, pour tester la vitesse avec du code .NET, j'utilise l'attribut Timeout de NUnit pour écrire des affirmations qu'un appel à une méthode particulière s'exécutera dans un certain laps de temps.

En utilisant quelque chose comme l'attribut timeout de NUnit avec l'exemple de code que vous avez donné (et un grand nombre d'itérations pour la boucle), vous pouvez réellement prouver si votre "amélioration" du code a vraiment aidé à la performance de cette boucle.

Un avertissement: Bien que cela soit efficace au niveau "micro", ce n'est certainement pas le seul moyen de tester les performances et ne prend pas en compte les problèmes qui pourraient survenir au niveau "macro" - mais c'est un bon début.


2
Même si je suis un grand partisan du profilage, je pense également qu'il est judicieux de garder à l'esprit les conseils que Cristian recherche. Je choisirai toujours la plus rapide de deux méthodes également lisibles. Être forcé dans l'optimisation post-mature n'est pas amusant.
AShelly

Il n'y a pas nécessairement besoin de tests unitaires, mais cela vaut toujours la peine de passer ces 20 minutes pour vérifier si certains mythes sur les performances sont vrais ou non, surtout parce que la réponse dépend souvent du compilateur et de l'état des indicateurs -O et -g (ou Debug / Libération en cas de VS).
mbq

+1 Cette réponse complète mon commentaire connexe à la question elle-même.
Tamara Wijsman

1
@AShelly: si nous parlons de reformulations simples de la syntaxe de boucle, la changer après coup est très facile à faire. En outre, ce que vous trouvez également lisible pourrait ne pas l'être pour d'autres programmeurs. Il est préférable d'utiliser autant que possible la syntaxe "standard" et de la modifier uniquement lorsque cela s'avère nécessaire.
Joeri Sebrechts

@AShelly sûrement si vous pouvez penser à deux méthodes tout aussi lisibles et que vous choisissez celle moins efficace que vous ne faites pas votre travail? Quelqu'un le ferait-il réellement?
glenatron

11

Gardez à l'esprit que votre compilateur peut très bien tourner:

for(int i = 0; i < collection.length(); i++ ){
   // stuff here
}

dans:

int j = collection.length();
for(int i = 0; i < j; i++ ){
   // stuff here
}

ou quelque chose de similaire, si collectionest inchangé sur la boucle.

Si ce code se trouve dans une section critique de votre application, il serait utile de savoir si c'est le cas ou non - ou bien si vous pouvez modifier les options du compilateur pour ce faire.

Cela maintiendra la lisibilité du code (car le premier est ce que la plupart des gens s'attendront à voir), tout en vous gagnant ces quelques cycles de machine supplémentaires. Vous êtes alors libre de vous concentrer sur les autres domaines où le compilateur ne peut pas vous aider.

Sur une note latérale: si vous changez collectionà l'intérieur de la boucle en ajoutant ou en supprimant des éléments (oui, je sais que c'est une mauvaise idée, mais cela arrive), votre deuxième exemple ne bouclera pas sur tous les éléments ou essaiera d'accéder au passé la fin du tableau.


1
Pourquoi ne pas le faire explicitement?

3
Dans certaines langues qui vérifient les limites, vous ralentirez votre code si vous le faites explicitement. Avec une boucle vers collection.length, le compilateur le déplace pour vous et omet la vérification des limites. Avec une boucle vers une constante ailleurs dans votre application, vous aurez une vérification des limites à chaque itération. C'est pourquoi il est important de mesurer - l'intuition sur les performances n'est presque jamais juste.
Kate Gregory

1
C'est pourquoi j'ai dit "ça vaudrait la peine de le découvrir".
ChrisF

Comment le compilateur C # peut-il savoir que collection.length () ne modifie pas la collection, comme le fait stack.pop ()? Je pense qu'il serait préférable de vérifier l'IL plutôt que de supposer que le compilateur optimise cela. En C ++, vous pouvez marquer une méthode comme const ('ne change pas l'objet'), afin que le compilateur puisse effectuer cette optimisation en toute sécurité.
JBRWilkinson

1
@JBRW Les optimiseurs qui font cela sont également conscients de la méthode ok-appelons-la-constness-même-si-ce-n'est-pas-C ++ des méthodes des collections. Après tout, vous ne pouvez vérifier les limites que si vous pouvez remarquer que quelque chose est une collection et savoir comment obtenir sa longueur.
Kate Gregory

9

Ce type d'optimisation n'est généralement pas recommandé. Ce morceau d'optimisation peut facilement être fait par le compilateur, vous travaillez avec un langage de programmation de niveau supérieur au lieu de l'assemblage, alors pensez au même niveau.


1
Donnez-lui un livre sur la programmation;)
Joeri Sebrechts

1
+1, car la plupart de nos copines sont probablement plus intéressées par Lady Gaga que par la clarté du code.
haploïde

Pourriez-vous expliquer pourquoi ce n'est pas recommandé?
Macneil

@macneil bien ... cette astuce fait que les codes ne sont pas si communs et ne fonctionnent pas du tout, ce morceau d'optimisation est censé être fait par le compilateur.
tactoth

@macneil si vous travaillez dans une langue de niveau supérieur, pensez au même niveau.
tactoth

3

Cela peut ne pas s'appliquer autant au codage à usage général, mais je fais principalement du développement intégré de nos jours. Nous avons un processeur cible spécifique (qui n'allera pas plus vite - il semblera étrangement obsolète au moment où ils retireront le système dans plus de 20 ans), et des délais très restrictifs pour une grande partie du code. Le processeur, comme tous les processeurs, a certaines bizarreries concernant les opérations rapides ou lentes.

Nous avons une technique utilisée pour nous assurer de générer le code le plus efficace, tout en maintenant la lisibilité pour toute l'équipe. Dans les endroits où la construction du langage le plus naturel ne génère pas le code le plus efficace, nous avons créé des macros qui garantissent que le code optimal est utilisé. Si nous faisons un projet de suivi pour un processeur différent, nous pouvons mettre à jour les macros pour la méthode optimale sur ce processeur.

À titre d'exemple spécifique, pour notre processeur actuel, les branches vident le pipeline, bloquant le processeur pendant 8 cycles. Le compilateur prend ce code:

 bool isReady = (value > TriggerLevel);

et le transforme en l'équivalent d'assemblage de

isReady = 0
if (value > TriggerLevel)
{
  isReady = 1;
}

Cela prendra soit 3 cycles, soit 10 s'il saute isReady=1;. Mais le processeur a une maxinstruction à cycle unique , il est donc préférable d'écrire du code pour générer cette séquence qui est garantie de toujours prendre 3 cycles:

diff = value-TriggerLevel;
diff = max(diff, 0);
isReady = min(1,diff);

De toute évidence, l'intention ici est moins claire que l'original. Nous avons donc créé une macro, que nous utilisons chaque fois que nous voulons une comparaison booléenne supérieure à:

#define BOOL_GT(a,b) min(max((a)-(b),0),1)

//isReady = value > TriggerLevel;
isReady = BOOL_GT(value, TriggerLevel);

Nous pouvons faire des choses similaires pour d'autres comparaisons. Pour un étranger, le code est un peu moins lisible que si nous n'utilisions que la construction naturelle. Cependant, cela devient rapidement clair après avoir passé un peu de temps à travailler avec le code, et c'est bien mieux que de laisser chaque programmeur expérimenter ses propres techniques d'optimisation.


3

Eh bien, le premier conseil serait d'éviter de telles optimisations prématurées jusqu'à ce que vous sachiez exactement ce qui se passe avec le code, de sorte que vous soyez sûr de le rendre plus rapide et non plus lent.

En C # par exemple, le compilateur optimisera le code si vous bouclez la longueur d'un tableau, car il sait qu'il n'a pas à vérifier la plage de l'index lorsque vous accédez au tableau. Si vous essayez de l'optimiser en plaçant la longueur du tableau dans une variable, vous romprez la connexion entre la boucle et le tableau et ralentirez en fait le code beaucoup plus.

Si vous allez faire une micro-optimisation, vous devez vous limiter aux choses qui sont connues pour utiliser beaucoup de ressources. S'il n'y a qu'un léger gain de performances, vous devriez plutôt opter pour le code le plus lisible et le plus facile à gérer. Comment le travail informatique change avec le temps, donc quelque chose que vous découvrez est un peu plus rapide maintenant, peut ne pas rester comme ça.


3

J'ai une technique très simple.

  1. Je fais fonctionner mon code.
  2. Je le teste pour la vitesse.
  3. Si c'est rapide, je reviens à l'étape 1 pour une autre fonctionnalité. Si c'est lent, je le profile pour trouver le goulot d'étranglement.
  4. Je corrige le goulot d'étranglement. Revenez à l'étape 1.

Il y a de nombreuses fois où cela permet d'économiser du temps pour contourner ce processus, mais en général, vous saurez si c'est le cas. En cas de doute, je m'y tiens par défaut.


2

Profitez du court-circuit:

if(someVar || SomeMethod())

prend autant de temps à coder et est tout aussi lisible que:

if(someMethod() || someVar)

mais cela va être évalué plus rapidement au fil du temps.


1

Attendez six mois, demandez à votre patron d'acheter de nouveaux ordinateurs à tout le monde. Sérieusement. Le temps du programmeur est beaucoup plus cher que le matériel à long terme. Les ordinateurs hautes performances permettent aux codeurs d'écrire du code de manière simple sans se soucier autant de la vitesse.


6
Euh ... Qu'en est-il des performances de vos clients? Êtes-vous assez riche pour leur acheter de nouveaux ordinateurs?
Robert Harvey

2
Et nous avons presque atteint le mur de la performance; le calcul multicœur est la seule issue, mais l'attente ne fera pas que vos programmes l'utiliseront.
mbq

+1 Cette réponse complète mon commentaire connexe à la question elle-même.
Tamara Wijsman

3
Aucun temps de programmation n'est plus cher que le matériel lorsque vous avez des milliers ou des millions d'utilisateurs. Le temps du programmeur N'EST PAS plus important que le temps de l'utilisateur, faites-le dans votre tête dès que possible.
HLGEM

1
Prenez de bonnes habitudes, cela ne prend pas de temps de programmeur car c'est ce que vous faites tout le temps.
Dominique McDonnell

1

Essayez de ne pas trop optimiser à l'avance, puis lorsque vous optimisez, vous vous inquiétez un peu moins de la lisibilité.

Là, je déteste plus que la complexité inutile, mais lorsque vous rencontrez une situation complexe, une solution complexe est souvent requise.

Si vous écrivez le code de la manière la plus évidente, faites un commentaire expliquant pourquoi il a été modifié lorsque vous effectuez le changement complexe.

Cependant, en particulier pour votre sens, je trouve que la plupart du temps, faire l'opposé booléen de l'approche par défaut aide parfois:

for(int i = 0, j = collection.length(); i < j; i++ ){
// stuff here
}

peut devenir

for(int i = collection.length(); i > 0; i-=1 ){
// stuff here
}

Dans de nombreuses langues, tant que vous apportez les ajustements appropriés à la partie "stuff" et qu'elle est toujours lisible. Il n'aborde tout simplement pas le problème de la façon dont la plupart des gens penseraient à le faire en premier car il compte à rebours.

en c # par exemple:

        string[] collection = {"a","b"};

        string result = "";

        for (int i = 0, j = collection.Count() - 1; i < j; i++)
        {
            result += collection[i] + "~";
        }

pourrait également s'écrire:

        for (int i = collection.Count() - 1; i > 0; i -= 1)
        {
            result = collection[i] + "~" + result;
        }

(et oui, vous devriez le faire avec une jointure ou un stringbuilder, mais j'essaie de faire un exemple simple)

Il existe de nombreuses autres astuces que l'on peut utiliser qui ne sont pas difficiles à suivre, mais beaucoup d'entre elles ne s'appliquent pas à toutes les langues, comme l'utilisation du milieu sur le côté gauche d'une affectation dans l'ancien vb pour éviter la pénalité de réaffectation de chaîne ou la lecture de fichiers texte en mode binaire en .net pour dépasser la pénalité de mise en mémoire tampon lorsque le fichier est trop volumineux pour une lecture.

Le seul autre cas vraiment générique auquel je puisse penser qui s'appliquerait partout serait d'appliquer une algèbre booléenne à des conditions complexes pour essayer de transformer l'équation en quelque chose qui a plus de chances de tirer parti d'un court-circuit conditionnel ou de transformer un complexe ensemble d'instructions if-then ou case imbriquées dans une équation entièrement. Ni l'un ni l'autre de ces travaux dans tous les cas, mais ils peuvent être des gains de temps importants.


c'est une solution, mais le compilateur
émettra

Mais en inversant l'indice, l'itération elle-même pourrait devenir plus complexe.
Tamara Wijsman

@stijn Je pensais à c # quand je l'ai écrit, mais cette suggestion tombe peut-être aussi dans la catégorie spécifique à la langue pour cette raison - voir modifier ... @ToWij certainement, je ne pense pas qu'il y ait beaucoup de suggestions de cette nature qui ne courent pas le risque de cela. Si votre // substance était une sorte de manipulation de la pile, il ne serait peut-être même pas possible d'inverser la logique correctement, mais dans de nombreux cas, cela n'est pas trop déroutant si cela est fait avec soin dans la plupart de ces cas à mon humble avis.
Projet de loi du

tu as raison; en C ++, je préfère toujours la boucle "normale" mais avec l'appel de la longueur () retiré de l'itération (comme dans const size_t len ​​= collection.length (); for (size_t i = 0; i <len; ++ i) {}) pour deux raisons: je trouve que la boucle de comptage vers l'avant `` normale '' est plus lisible / compréhensible (mais c'est probablement juste parce qu'elle est plus courante), et elle prend l'appel de longueur invariant de la boucle () hors de la boucle.
stijn

1
  1. Profil. Avons-nous même un problème? Où?
  2. Dans 90% des cas où cela est en quelque sorte lié aux E / S, appliquez la mise en cache (et peut-être plus de mémoire)
  3. Si c'est lié au CPU, appliquez la mise en cache
  4. Si la performance est toujours un problème, nous avons quitté le domaine des techniques simples - faites le calcul.

1

Utilisez les meilleurs outils que vous pouvez trouver - bon compilateur, bon profileur, bonnes bibliothèques. Obtenez les bons algorithmes, ou mieux encore - utilisez la bonne bibliothèque pour le faire pour vous. Les optimisations de boucle triviales sont de petites pommes de terre, et vous n'êtes pas aussi intelligent que le compilateur d'optimisation.


1

Le plus simple pour moi est d'utiliser la pile lorsque cela est possible chaque fois qu'un modèle d'utilisation de cas commun correspond à une plage de, par exemple, [0, 64) mais a de rares cas qui n'ont pas de petite limite supérieure.

Exemple simple C (avant):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

Et après:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {0}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

J'ai généralisé cela comme ça depuis que ces types de points chauds surviennent beaucoup dans le profilage:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_calloc(&values_mem, n, sizeof(int));

    // do stuff with values
    ...

    mf_free(&values_mem);
}

Ce qui précède utilise la pile lorsque les données allouées sont suffisamment petites dans ces cas à 99,9%, et utilise le tas dans le cas contraire.

En C ++, j'ai généralisé cela avec une petite séquence conforme aux normes (similaire aux SmallVectorimplémentations disponibles) qui tourne autour du même concept.

Ce n'est pas une optimisation épique (j'ai obtenu des réductions de, disons, de 3 secondes pour qu'une opération se termine à 1,8 seconde), mais cela nécessite un effort si trivial pour s'appliquer. Lorsque vous pouvez obtenir quelque chose de 3 à 1,8 secondes en introduisant simplement une ligne de code et en en modifiant deux, c'est un très bon coup pour un si petit argent.


0

Eh bien, il y a beaucoup de changements de performances que vous pouvez effectuer lors de l'accès aux données qui auront un impact énorme sur votre application. Si vous écrivez des requêtes ou utilisez un ORM pour accéder à une base de données, vous devez lire certains livres d'optimisation des performances pour le backend de base de données que vous utilisez. Il y a de fortes chances que vous utilisiez des techniques connues peu performantes. Il n'y a aucune raison de le faire, sauf l'ignorance. Ce n'est pas une optimisation prématurée (je maudis le gars qui a dit cela parce qu'elle a été si largement interprétée que ne jamais se soucier des performances), c'est une bonne conception.

Juste un échantillon rapide des améliorateurs de performances pour SQL Server: utilisez des index appropriés, évitez les curseurs - utilisez une logique basée sur un ensemble, utilisez des clauses sargable where, n'empilez pas les vues au-dessus des vues, ne renvoyez pas plus de données que nécessaire ou plus colonnes que vous avez besoin, n'utilisez pas de sous-requêtes corrélées.


0

S'il s'agit de C ++, vous devriez prendre l'habitude de ++iplutôt que de i++. ++ine sera jamais pire, cela signifie exactement la même chose qu'une déclaration autonome, et dans certains cas, cela pourrait être une amélioration des performances.

Cela ne vaut pas la peine de changer le code existant au cas où cela aiderait, mais c'est une bonne habitude à prendre.


0

J'ai une vision un peu différente. Le simple fait de suivre les conseils que vous obtenez ici ne fera pas beaucoup de différence, car il y a des erreurs que vous devez faire, que vous devez ensuite corriger, dont vous devez ensuite apprendre.

L'erreur que vous devez faire est de concevoir votre structure de données de la façon dont tout le monde le fait. Autrement dit, avec des données redondantes et de nombreuses couches d'abstraction, avec des propriétés et des notifications qui se propagent à travers la structure en essayant de la garder cohérente.

Ensuite, vous devez effectuer un réglage des performances (profilage) et le faire vous montrer comment, à bien des égards, ce qui vous coûte des tonnes de cycles, ce sont les nombreuses couches d'abstraction, avec des propriétés et des notifications qui se propagent dans toute la structure en essayant de la garder cohérente.

Vous pourrez peut-être résoudre ces problèmes quelque peu sans modifications majeures du code.

Ensuite, si vous avez de la chance, vous pouvez apprendre que moins de structure de données est meilleure et qu'il vaut mieux tolérer une incohérence temporaire que d'essayer de garder bien des choses en accord avec les vagues de messages.

Comment vous écrivez des boucles n'a vraiment rien à voir avec cela.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.