Général C++ Amélioration De La Performance Des Conseils
Quelqu'un pourrait-il m'indiquer un article, ou d'écrire quelques conseils ici au sujet de certains de programmation C++, des habitudes qui sont généralement valide (pas de réels inconvénients) et améliore les performances? Je ne veux pas dire des modèles de programmation et de la complexité de l'algorithme - je besoin de petites choses comme la façon dont vous définissez vos fonctions, les choses à faire/à éviter dans les boucles, ce qui à allouer sur la pile, ce qui sur le tas, et ainsi de suite.
Il n'est pas question de faire un logiciel particulier plus rapide, aussi il n'est pas sur la façon de créer un logiciel propre conception, mais plutôt les habitudes de programmation que - si vous avez toujours les appliquer, vous permet de rendre votre code plutôt un peu plus rapide qu'un peu plus lent.
- J'ai quelques habitudes de programmation de l'optimisation construit au fil des années (à partir de l'assemblage de la langue & 8-bits micros). Le problème est que souvent, un simple changement de design de l' {micro} optimisations nulle et non avenue. Ma meilleure optimisation est de réduire {s'abstenir de} la codification de l'inutile, des méthodes et des objets. Il sera toujours temps d'ajouter plus de fonctionnalités, si nécessaire.
- Avec la plupart des langues, surtout l'un d'aussi compliqué que C++ si vous ne faites pas quelque chose de manifestement incompétents (comme la copie d'énormes blocs de données à chaque fois grâce à une boucle), il est presque impossible de déterminer si des effets "au moment de la conception d'optimisation", a - y compris ces petites habitudes. Mon avis est que de [Steve McConnell] (stevemcconnell.com): il n'est tout simplement pas la peine de s'inquiéter au sujet de l'optimisation lors du codage. Prenez l'habitude d'écrire lisible code au lieu de optimisé code.
- mais si vous êtes de l'écriture en temps réel de l'application, le temps linéaire des questions trop, et sur le développement d'une application, le goulot d'étranglement peut passer à un autre endroit et si vous avez codé il y soigneusement avant, les choses pourraient fonctionner plus rapidement
- Quelle est la vraie question ici? Le mieux que je puisse déchiffrer le texte est extrêmement large et auto-contradictoire (conseils généraux sur le rendement, mais pas la complexité algorithmique et puis certains enchaînements en général les habitudes de programmation qui n'ont "un peu" un impact sur les performances?).
- exactement 🙂 je suis à la recherche d'habitudes qui à coup sûr ne fera pas de mal, mais POURRAIT rendre le code un peu mieux
- Je ne suis pas d'accord avec vous. Vous pouvez écrire optimisé et lisible à la fois. Vous ne devriez pas être en train d'essayer d'optimiser tout, mais au moins optimisé ce que vous savez déjà que pourrait l'être.
- Vous pourriez vous intéresser à ma réponse ici: stackoverflow.com/questions/653980/c-optimization-techniques/...
Vous devez vous connecter pour publier un commentaire.
Un certain nombre de conseils dans Effective C++, Plus Efficace C++, Efficace STL et C++ Normes De Codage sont le long de cette ligne.
Un exemple simple d'une telle astuce: utiliser la pré-incrémentation (++i) plutôt que de post-incrémentation (++i) lorsque cela est possible. Ceci est particulièrement important avec les itérateurs, en tant que post-incrémentation implique la copie de l'itérateur. Vous optimiseur de peut être en mesure d'annuler cette, mais il n'est pas de tout travail supplémentaire pour écrire pré-incrémentation au lieu de cela, alors pourquoi prendre le risque?
i++
être tout aussi efficace que++i
pour les primitives, mais pas pour les itérateurs, et la consistance est bonne.++i
de cette façon: "incrémenter i", puis de le comparer aui++
: "je incrément". Lequel semble le plus adéquat? Espérons maintenant que vous détestez que la forme de l'incrément de moins en moins. 🙂inc eax
, qui est aussi une "déclaration" pas une expression 😉eggs->and.bacon(are).yummy[3]->food++
est beaucoup plus évident pour moi. -- Laurence: ma compréhension est une séquence de points sont en voie de disparition dans 0x en faveur de "est-séquencé-avant" de la sémantique, de jeter une autre clé dans les travaux.++(eggs ... food)
, qui, je pense, rend évident. Si vous ne vous souvenez pas de ce que vous faites à partir de l'une des extrémités d'une ligne de code à l'autre, les lignes de code sont trop longues, et votre exemple est volontairement à la limite, même sans un incrément. @Partielle: par ces motifs, il doit être appelé (C+1). Il ne modifie pas la C, chaque fois que vous utilisez C++ 😉Si je vous comprends bien, vous êtes demandant d'éviter de Prématuré Pessimization, un bon complément pour éviter l'Optimisation Prématurée. Le n ° 1 chose à éviter, basé sur mon expérience, est de ne pas copier de gros objets chaque fois que possible. Cela comprend:
Ce dernier point nécessite quelques explications. Je ne peux pas vous dire combien de fois j'ai vu cela:
Le droit chemin est:
Ces lignes directrices s'appliquent à quelque chose de plus grand qu'un pointeur intelligent ou un type. Aussi, je vous recommande fortement d'investir du temps à apprendre à le profil de votre code. Un bon outil de profilage pour aider à capturer le gaspillage des opérations.
const&
partout.const &
lorsque pratique est un bon point de départ pour la plupart des gens. La moyenne programmeur C++ ne sais pas à propos de copie d'élision et de la RVO, et à mon humble avis il est plus facile d'obtenir ces faux que c'est d'oublier d'utiliser des références.const
. C'est juste qu'ils sont en général, c'est ainsi que j'ai choisi d'écrire le code d'exemple. Puisque vous avez mentionné en temps réel de code, je mettrait plus l'accent sur profilage que de s'inquiéter de la langue lawering se passe dans les commentaires ici.Quelques une de mes bêtes noires:
if (a && b)
, si b est plus susceptible d'être faux, il place d'abord pour enregistrer l'évaluation d'un.Il y a beaucoup d'autres "mauvaises habitudes", dont je ne parlerai pas, parce que dans la pratique, les compilateurs modernes/optimiseurs permettra d'éliminer les effets néfastes (par exemple, une valeur de retour d'optimisation vs passé par référence, boucle de déroulement, etc.).
L'un des bons points de départ est la Sutter le Gourou de la semaine de la série, et le Exceptionnel C++ livres qui ont grandi hors de cela.
La "L'optimisation de Logiciels en C++" par Agner Brouillard est généralement l'une des meilleures références pour les techniques d'optimisation à la fois simple, mais certainement aussi pour les plus avancés. Un autre grand avantage est qu'il est libre de lire sur son site internet. (Voir le lien en son nom pour son site, et le lien sur le titre de l'article en pdf).
Edit: rappelez-vous Aussi que 90% (ou plus) du temps est passé à 10% (ou moins) du code. Donc, en général, de l'optimisation de code est vraiment localiser votre goulets d'étranglement. De plus, il est important et utile de savoir que les compilateurs modernes fera optimzation beaucoup mieux que la plupart des codeurs, notamment des micro-optimisations telles que retarder l'initialisation des variables, etc. Les compilateurs sont souvent de très bon à l'optimisation, donc passer du temps à écrire stable, fiable et simple code.
Je dirais que ça vaut la peine de se concentrer davantage sur le choix de l'algorithme que sur des micro-optimisations, au moins pour la plupart.
Utiliser les foncteurs (classes avec
operator()
mis en œuvre) à la place des pointeurs de fonction. Le compilateur, il est plus facile d'emploi inline l'ancien. C'est pourquoi C++std::sort
tend à faire mieux (quand on lui donne un foncteur) que C estqsort
.std::sort
dans une large mesure, également, fait mieux, car il permet également d'éviter le besoin supplémentaire d'indirection (en passant adresses de deux objets comparés commevoid*
, plutôt que les objets eux-mêmes, même si une simple copie est moins cher).Il semble de votre question que vous savez déjà au sujet de la "optimisation prématurée est le mal", la philosophie, donc je ne vais pas prêcher à ce sujet. 🙂
Les compilateurs modernes sont déjà assez intelligents à la micro-optimisation de choses pour vous. Si vous essayez trop dur, vous pouvez souvent faire des choses plus lent que l'original straight-forward code.
Pour les petits "optimisations" vous pouvez le faire en toute sécurité sans réfléchir, et qui n'affecte pas beaucoup la lisibilité/maintability du code, découvrez le "Prématuré Pessimization" section du livre C++ Normes de Codage par Sutter & Alexandrescu.
Pour les plus techniques d'optimisation, découvrez Efficace C++ par Bulka & Mayhew. Utilisez uniquement lorsque cela est justifié par le profilage!
Pour une bonne programmation en C++ pratiques, découvrez:
Sur le dessus de ma tête, une bonne performance générale de la pratique est de passer à des poids lourds objets par référence plutôt que par copie. Par exemple:
Pour un petit objet, comme un nombre complexe ou 2 dimensions (x, y) le point, la fonction sera probablement courir plus vite avec l'objet passé par copie.
Quand il s'agit de taille fixe, de poids moyen d'objets, il n'est pas clair si la fonction s'exécutera plus rapidement avec une copie ou une référence à l'objet. Seulement profilage dire. J'ai l'habitude de simplement passer par const de référence (si la fonction n'a pas besoin d'une copie locale) et seulement s'inquiéter si le profilage me dit.
Certains diront que vous pouvez inline petites méthodes de la classe sans le penser. Cela peut vous donner une exécution boost de performance, mais il peut également prolonger votre moment de la compilation, si il ya un gros montant de l'in-lining. Si une méthode de la classe est partie d'une bibliothèque d'API, il peut être préférable de ne pas l'inclure, peu importe la façon dont il est petit. C'est parce que la mise en œuvre de fonctions inline doit être visible pour les autres modules/classes. Si vous modifiez quelque chose dans cette fonction inline/méthode, puis d'autres modules de référence-il besoin d'être re-compilé.
Quand j'ai commencé à programmer, je voudrais essayer de micro-optimiser tout ce qui était ingénieur électrique en moi). Quelle perte de temps!
Si vous êtes dans les systèmes embarqués, les choses changent et vous ne pouvez pas prendre de mémoire pour acquis. Mais c'est une toute autre boîte de pandore.
Voici un joli article sur le sujet: Comment Allez-Y Doucement
Utiliser le conteneur de droite
Conteneurs de séquence
vector
pour les données de taille inconnue, si vous allez continuer à ajouter des données. Si vous allez l'appeler à plusieurs reprisespush_back()
, soit utiliserreserve()
ou utiliser undeque
à la place.list
est probablement le bon choix.deque
est probablement le bon choix.list
est probablement le mauvais choix.list
, mais vous ne vous déplacer vers l'arrière dans la liste, vous pouvez trouverforward_list
plus à votre goût. Il ne sera pas plus rapide, mais cela prendra moins de place.Cet avis devient de plus en plus applicable le plus grand le récipient. Pour les petits conteneurs,
vector
peut toujours être le bon choix, simplement en raison de la baisse des facteurs constants. En cas de doute, l'indice de référence.Conteneurs associatifs
unordered_foo
/hash_foo
, il n'y a pas une tonne de choix. Utilisation selon la des quatre conteneurs appropriés à vos besoins.unordered_foo
, de l'utiliser au lieu de la version commandée si vous ne vous souciez pas de l'ordre des éléments et vous avez une bonne fonction de hachage pour le type.Utiliser les exceptions judicieusement
Amour modèles de
Éviter
dynamic_cast
dynamic_cast
est parfois le seul choix pour faire quelque chose, mais souvent l'utilisation dedynamic_cast
peut être éliminé par l'amélioration de la conception.dynamic_cast
avec untypeid
suivie par unstatic_cast
soit.vector
croît de façon exponentielle, de sorte que le coût de ne pas appelerreserve
avant plusieurs appels àpush_back
est marginal. (C'est toujours mieux d'appelerreserve
si vous savez la nouvelle taille, mais ne pas les éviter à l'aide devector
juste parce que vous n'avez pas). Aussi,deque
ne serait pas mieux dans ce cas.vector
insert est probablement plus rapide qu'undeque
insérer si vous n'avez pas réaffecter, le coût de la réaffectation d'un grandvector
est immense. Pire, il est concentré en un temps, donc un très grandvector
peut créer un décalage perceptible en fonction de l'application.J'aime cette question parce que c'est demandé pour certains "bonnes habitudes".
J'ai trouvé que certaines choses qui sont souhaitables dans la programmation initialement une corvée, mais deviennent acceptables et même facile une fois qu'ils deviennent des habitudes.
Un exemple en est toujours à l'aide de pointeurs intelligents au lieu de raw pointeurs de contrôle de segment de mémoire à vie. Une autre, liée bien sûr, est de développer l'habitude de toujours utiliser RAII pour l'acquisition de ressources et de la libération. Un autre est toujours l'utilisation d'exceptions pour la gestion des erreurs.
Ces trois ont tendance à simplifier le code, le rendant de ce fait plus petit et donc plus rapide, ainsi que plus facile à comprendre.
Vous pouvez aussi faire des getters et setters implicitement inline; toujours faire plein usage de l'initialiseur de listes de constructeurs et de toujours utiliser la recherche et d'autres fonctions connexes qui sont fournies dans la bibliothèque std, au lieu de l'élaboration de vos propres boucles.
Pas spécifiquement le C++, mais il est souvent utile pour éviter la copie des données. Dans les programmes de longue date avec beaucoup de l'allocation de mémoire, il peut être intéressant de considérer l'allocation de la mémoire comme une partie importante de la conception, de sorte que la mémoire que vous utilisez provient de piscines qui sont réutilisés, même si ce n'est pas nécessairement une commune assez de chose pour être considéré comme digne de la formation d'une habitude.
Une chose - ne pas copier le code d'un endroit à un autre si vous avez besoin de la fonctionnalité - l'utilisation d'une fonction. Cela permet de maintenir le code de petite taille et le rend plus facile à optimiser tous les endroits qui utilisent cette fonctionnalité.
Modèles! À l'aide de modèles peuvent réduire la quantité de code, car vous pouvez avoir une classe ou d'une fonction/méthode qui peut être réutilisable avec de nombreux types de données.
De considérer les éléments suivants:
Basic_string pourrait être composé d'un char, wchar_t, unsigned char ou unsigned wchar_t.
Modèles peut également être utilisé pour une multitude de choses différentes, telles que les traits, de spécialisation de classe ou même utilisé pour passer un int valeur à une classe!
Sauf si vous êtes vraiment sûr qu'un autre type de conteneur est mieux, utiliser std::vector. Même si std::deque, std::list, std::map etc semble plus conveniant choix, un vecteur outperformes d'eux à la fois l'utilisation de la mémoire et de l'accès à l'élément\itération fois.
Aussi, préférez l'utilisation d'un contenants membre de l'algorithme (c'est à dire la carte.equal_range (...)), au lieu de leurs homologues à l'échelle mondiale (std::equal_range(begin(), end()...))
Éviter d'itérer sur le même ensemble de données à de multiples reprises comme beaucoup que possible.
Voici une liste que j'ai appelé dans le passé - http://www.devx.com/cplus/Article/16328/0/page/1. Au-delà, Googler c++ astuces pour améliorer les performances des rendements tout à fait un peu.
Je suggère de lire le chapitre II ("Performance") de "La Programmation Des Perles" de Jon Bentley. C'est pas du C++ spécifiques, mais ces techniques peuvent être appliquées en C ou C++. Le site contient uniquement des parties du livre, je vous recommande de lire le livre.
J'ai pris l'habitude de préférer l'écriture
++i
plutôt quei++
pas qu'il n'apporte aucun gain de performance lors de lai
est unint
mais les choses sont différentes lorsquei
est uniterator
qui pourrait avoir une mise en œuvre complexe.Alors disons que vous venez de le langage de programmation C, de perdre votre habitude de déclarer toutes tes variables au début de la fonction: déclarer vos variables lorsqu'elles sont nécessaires dans la fonction de flux puisque la fonction peut contenir début
return
déclarations avant de certaines variables qui ont été initialisées au début sont utilisées de manière efficace.En dehors de cela, une autre ressource est C++ Normes de Codage: 101 Règles, des lignes Directrices et des Pratiques Exemplaires par Herb Sutter (encore lui) et Alexei Alexandrescu.
Il y a aussi une édition plus récente de Scott Meyers Effective C++: Effective C++: 55 moyens spécifiques pour améliorer vos programmes et des dessins et modèles.
Enfin, je voudrais mentionner Tony Albrecht est Les pièges de la Programmation Orientée Objet présentation: non pas qu'il contient des règles de base que vous pouvez suivre aveuglément, mais c'est une lecture très intéressante.
new
(envisager l'opérateur surchargé etc), donc si un constructeur de la classe appellenew
à tout moment, il généralement ne pas être en retard-initialisé.Cette page résumer tout ce que vous devez savoir à propos de l'optimisation en C++ (que ce soit pendant ou après l'écriture de logiciels). C'est vraiment de bons conseils et est très lear -- et peut être utilisé comme un rappel utile dans l'optimisation de la phase du projet.
C'est un peu vieux, donc vous devez savoir qui des optimisations sont déjà effectué par votre compilateur (comme NRVO).
Autre que cela, la lecture Effective C++, Plus Efficace, C++, Efficace STL C++ et les Normes de Codage qui ont déjà été cités est aussi importante, car elle explique beaucoup de choses sur ce qui se produit dans la langue et dans la STL, vous permettant de mieux optimiser votre cas particulier, en utilisant une meilleure compréhension de ce qui se passe exactement.
Beaucoup de bonnes suggestions ici déjà.
L'une des meilleures façons d'obtenir de bonnes habitudes est de les forcer sur vous-même. Pour cela, j'ai l'amour de PC-Lint. PC-Lint ferons respecter Scott Meyer Effective C++ & Plus Efficace C++ règles. Aussi obéissant à de la Charpie règles tend à aboutir à une plus facile à entretenir, moins de risques d'erreurs, et plus propre code. Il suffit de ne pas aller trop fou quand vous vous rendez compte de la charpie souvent générer plus de sorties que vous avez le code source; une fois, j'ai travaillé sur un projet avec 150 MO de code source et de 1,8 GO de Peluches messages.
5a. Définir ces moyens, qui permettent pour les trois premiers (c'est à dire faire des nœuds dans un bloc)
La stimulation du rendement, vous pouvez obtenir de cette façon sont étonnantes. Pour moi de 1500 fois pour un calcul lourd app. Pas plus brute avant, mais sur les mêmes structures de données écrites dans un grand paquet de logiciel.
Je n'avais pas l'embêter avec coincés comme des pré-incrémentation post. Qui ne donne que des économies dans certains (sans importance) des cas et la plupart de ce qui est mentionné, c'est que les trucs similaires qui pourraient gratter 1% supplémentaire ici et là de temps en temps, mais généralement ne vaut pas la peine.
Celui-ci aura de très bonnes Techniques pour général, c++ Optimisation:
http://www.tantalon.com/pete/cppopt/main.htm
Utiliser le profiler pour voir quelle partie de l'application qui s'exécute lentement et ensuite utiliser les techniques d'optimisation.
J'ai utilisé le valgrind avec callgrind outil de profilage de but et il va vous donner des lignes de coût combien.
valgrind --tool=callgrind
Préfèrent l'utilisation de pré-incrémentation.
Avec int/pointeur etc il ne fait aucune différence.
Mais avec les types de classes de la manière standard de la mise en œuvre nécessite la création d'un nouvel objet.
Donc préfèrent pré-incrémentation. Juste au cas où à un moment plus tard, les types sont modifiées.
Ensuite, vous n'aurez pas besoin de modifier le code pour faire face.
++i
fait une différence si seulement une sémantique de l'un quandi
est uniterator
i++
et++i
est la position d'une seule instruction. Un bon compilateur ne doit pas générer de surcharge supplémentaire.La meilleure façon d'améliorer ces compétences est la lecture de livres et d'articles, mais je peux vous aider avec quelques conseils:
this
pointeur doit parfois être ajusté à point pour les différentes classes de base et les fonctions membres sont parfois appelés par les wrappers. Mais c'est certainement pas assez pour justifier influencer la façon dont vous concevez votre hiérarchie de classe.Pourquoi personne n'a mentionné jusqu'à maintenant? Pourquoi tout le monde est dans la pauvre petite
++i
?L'une des meilleures peu de choses que vous pouvez facilement le faire, à pas pessimize votre code:
Effective C++ par Scott Meyers, Point 20:
Préfèrent passer par référence à const passer par valeur
Exemple:
Dans le cas d'un court
std::string
vous pourriez ne pas gagner beaucoup, mais le passage grand objets comme ça, peut vous faire économiser beaucoup de puissance de calcul que vous éviter de répéter la copie. Et peut aussi vous sauver de l'apparition d'un bug ou deux si vous avez oublié de mettre en œuvre votre constructeur de copie.