6
votes

Améliorer les performances du code C

Quel est le moyen le plus peu orthodoxe d'améliorer la performance du code C? Ceci est non-Holds-barred! Tout va, y compris la modification des structures de boucle en boucle à des gotos, de tout le codage et de tout, en utilisant des déclarations de cas à bizarres, etc. Ne vous inquiétez pas du tout sur la maintenabilité, la lisibilité, etc.

P.s. Ceci est pratique ... et je suis bien conscient de la manière d'améliorer les performances du code de manière raisonnable (améliorer les algorithmes, le profil avant d'optimiser, etc.)


3 commentaires

Il n'y a pas de preuve qui allant contre la langue et quels compilateurs sont "optimisés" pour vous donner un coup de pouce de performance.


Depuis lorsqu'il améliore les algorithmes, le profilage avant d'optimiser, etc. raisonnable? Si c'était vrai, nous n'aurions pas à travailler si fort pour convaincre les gens de faire ces choses.


J'ai voté pour re-ouvert. J'aurais aimé ajouter une réponse, à savoir ce lien: Stackoverflow.com/Questtions/926266/...


15 Réponses :


22
votes

Dans mon expérience, la manière la plus peu orthodoxe d'optimisation du code C est de profiler l'application, d'identifier lentement des structures et des hits de DB, puis de concevoir des solutions raisonnables autour d'eux à l'aide d'une analyse Big O.


4 commentaires

Cela ne mérite pas vraiment +6, puisque ce n'est pas du tout peu orthodoxe et va à l'encontre de la question ....... mais c'est raisonnable pour que je ne puisse pas vous permettre non plus: \ \


@Mark: C'est une blague, disant que le moyen parfaitement logique d'optimiser est "peu orthodoxe" car peu de gens le font réellement de cette façon.


De plus, les gens ont tendance à vous regarder comme vous et un nombre irrégulier de têtes si vous le suggérez.


Solution directe - simplement statistiquement étrange



6
votes

Le périphérique de Duff est l'exemple canonique. C'est tellement étrange que Tom Duff admis, "Ce code forme une sorte d'argument dans [le débat sur la chute à travers des déclarations de cas], mais je ne suis pas sûr que ce soit pour ou contre".


0 commentaires

4
votes

Profil de votre code, trouvez les points lents et utilisez l'assemblage en ligne pour les optimiser.


3 commentaires

Quand j'ai travaillé lors d'une entreprise de match, nous l'avons fait, mais éventuellement, vous avez frappé des retours décroissants et vous devez regarder la grande image. Nous avons souvent trouvé que réorganiser la mise en page des structures de données affectait une large mesure à une large mesure.


Vous avez oublié l'étape 4: profil à nouveau pour vous assurer que votre assemblage en ligne n'a pas réellement ralenti le code. J'ai vu cela arriver.


@Nosredna - Je suis vraiment intéressé par votre commentaire ... Je suis sur le point de poser une question à ce sujet



1
votes

3 commentaires

Et Carmack n'est pas Carmack.


Droite, mais c'est connu comme tel.


C'est aussi typiquement une performance pénalité sur le matériel le plus moderne (puisque la plupart des architectures disposent d'une instruction racine carrée réciproque matérielle qui reste dans le domaine FP).



3
votes

Vous recherchez une solution non orthodoxe, sans réserve, mais à l'intention générale d'optimiser c?

Réécrivez-le en langage de montage.


0 commentaires

4
votes

1) boucle déroulante. Vous économisez un saut, une comparaison et incrémentez chaque itération si vous ne boucle pas réellement.
2) Évitez la double indirection. Il est généralement plus rapide de réaliser des arithmétiques de la récupération, de sorte qu'une [Y * Hauteur + x] est généralement plus rapide que celle d'un [y] [x]. Plus un tableau unidimensionnel de taille MXN sauve des mots m (ou n) de mots de pointeurs par rapport à une matrice rectangulaire de dimensions mxn.
3) Utilisez des optimisations de montage ridicules dans la mesure du possible. Par exemple, sur l'architecture X86, vous pouvez utiliser l'instruction BSWAP pour échanger les octets en une seule opération au lieu de la température normale TEMP = A; a = b; B = TEMP; motif.

Et bien sûr, n'oubliez pas: 4) Ne faites pas la vérification des limites ou la manipulation des erreurs.

qui ayant été dit, j'éviterais tout cela sauf (2) dans la pratique.


5 commentaires

Sauf que la majeure partie de cela est inutile car le compilateur le fera.


Dans la plupart des cas, "optimisations peu orthodoxes" sont inutiles - soulignant que les réponses à une question inutile sont elles-mêmes inutiles est un peu ... inutile. (c:


Les compilateurs ne peuvent pas automatiquement faire 1 et 2? Et ne devrait-il pas y avoir une bibliothèque pleine de hacks de montage pour ces types de choses?


Zut. Pensais que j'étais intelligent - vous avez dit "inutile". Doh!


Je n'ai pas regardé la sortie du code de la machine d'un compilateur C récemment, mais depuis 1 et 3 étaient couverts dans mon cours de compilateurs de premier cycle, je suppose qu'ils sont assez largement mis en œuvre.



5
votes

abuser de la constante 0x5f3759DF pour calculer la place inverse Les racines doivent rapidement classer assez haut ...


0 commentaires

2
votes

Votre compilateur est presque certainement mieux à optimiser que vos tentatives laides vous donneraient. La plupart des petits astuces historiques sont maintenant inutiles. Les personnes ignorant la lisibilité et la maintenabilité ont tendance à écrire du code qui finit moins efficace, car les optimisations réelles sont rendues plus difficiles.

Lorsque le code a été optimisé de toutes les manières possibles et toujours besoin d'un gain de performance, la réécriture des portions critiques dans l'ASM est le meilleur espoir d'avoir un effet.


0 commentaires

5
votes

Utilisez l'assemblage en ligne?

Sérieusement, si, si, en changeant simplement le code C, vous pouvez améliorer les performances, il est probable que vous puissiez le faire proprement.

Quelques exceptions près:

1) Si vous comptez sur une sémantique d'alignement des pointeurs de différents types, vous pouvez effectuer des opérations de bloc sur des pointeurs qui exposent techniquement votre application à une condition de dépassement des limites, mais dans la pratique ne contient pas de caractéristiques d'alignement de votre système. Donc, une copie de mémoire peut être effectuée en alignant des caractères initiales, puis le bloc intérieur peut être effectué à l'aide d'un long pointeur *.

2) Il peut être possible de copier des cadres de pile de manière intelligente si vous connaissez l'ordre de mémoire dans lequel votre compilateur attribue des variables locales. Cela peut vous permettre de mettre en œuvre des co-routines que la langue ne supporte pas autrement. Les coroutines sont souvent un moyen plus simple et plus rapide de mettre en œuvre des types de contrôle de boucle.

3) Les syndicats sont toujours un peu "hacky", mais vous les utilisez. C'est un moyen de mettre en œuvre du polymorphisme avec une vérification de type assez lâche.

4) L'utilisation du préprocesseur C en tant que code de génération automatique est généralement très difficile à déboguer et à lire. Comme ces personnes ont tendance à éviter cela.


0 commentaires

1
votes

Dans les applications DSP, il vaut toujours la peine d'aller à la langue d'assemblage afin d'obtenir la meilleure performance des instructions de SIMD que c compilateurs ne réussissent pas très bien. Mais ce n'est pas vraiment une solution "C".

Quelque chose que je fais assez souvent est d'utiliser un logiciel d'ajustement de courbe pour remplacer les fonctions avec des approximations plus rapides pour calculer. Parfois, les luts sont encore plus rapides que de faire un tas de calculs, mais pas aussi souvent qu'ils étaient.


0 commentaires

1
votes

Voir ce chapitre, C'est une vie merveilleuse par Abrash (il s'agit d'environ 5 pages: cliquez sur "Suivant" au bas de chaque écran).

Résumé (certaines citations de l'article):

  • Magie de la table (table immense de la recherche et machine d'état incroyable)
  • Une approche de la programmation de la performance qui fonctionne à un niveau plus efficace et étroitement intégré que vous ne reviendrez jamais
  • Économie étonnante d'effort

0 commentaires

2
votes

Il n'y a rien de peu orthodoxe à faire pour les performances du code C. Toutes les techniques efficaces ont été "orthodoxées".

Le meilleur que j'ai trouvé est d'utiliser un profileur avec accès aux compteurs de performance du processeur et d'accorder une attention particulière au cache et aux raques ratés. Ajouter des préfets de cache Où que vous puissiez et supprimez des branches imprévisibles où que vous puissiez.

Ne vous inquiétez pas avec la boucle déroulante. Si la branche est prévisible, elle est presque gratuite. Laissez le compilateur à s'inquiéter à ce sujet.

Sur certaines architectures très parallèles telles que l'IA64, il peut être plus rapide de dérouler une boucle jusqu'au bout. Un exemple de ceci évite les fonctions de chaîne C. Utilisez MEMSET pour zéro une matrice à chaîne, MemCY pour régler la chaîne et le MEMCMP pour comparer l'ensemble de la matrice contre un autre tableau similaire. Cela peut utiliser des charges de 64 bits, ne doit jamais vérifier le terminateur zéro et peut être optimisé pour ne pas boucler ou brancher du tout si l'utilisation d'une "petite" taille de matrice de 64 ou 128. Les fonctions MEMXXXX () sont généralement construites. Ins et très optimisé.


0 commentaires

2
votes

J'entends beaucoup de réponses de la forme "Essayez de faire x, Y ou Z", mais c'est comme dire "entendre, avoir un poisson et bien manger pendant une journée".

Je préférerais vous apprendre à pêcher - pour les problèmes de performance. Les gens qui disent "profil d'abord" sont sur la bonne voie mais (IMHO) sont beaucoup trop timides.

Voici un exemple d'accord de performance agressif. < / p>

Voici une courte explication de la raison pour laquelle cela fonctionne.

Voici une longue explication de pourquoi ça marche.

Cela vous apprendra à pêcher en vous montrant comment savoir où sont les poissons et à quel point ils sont gros. Une fois que vous les trouvez, vous pouvez les cuisiner (fixez-les) de nombreuses façons merveilleuses. La grande chose est, une fois que vous trouverez et jetez un poisson (problème de performance), les autres deviennent plus gros et plus facile à attraper.


0 commentaires

3
votes

Pour le point 3 ci-dessus dans la réponse de Dathan, une autre façon d'échanger, vous pouvez échanger des variables de manière non conventionnelle à l'aide de XOR.

int = 3, y = 4;
x = x ^ y; 
y = y ^ x; 
x = x ^ y; 


1 commentaires

Je devrais également mentionner que lorsque vous traitez avec des tableaux, il est plus rapide de le faire en utilisant * (quelque_array + n) lorsque vous avez déclaré que vous avez déclaré ... Mais cela pourrait être non pertinent maintenant en ce qui concerne la technologie de compilateur aujourd'hui ... ;)



1
votes

0 commentaires