8
votes

Comment augmenter les travaux d'alimentation? Vaut-il la peine d'utiliser pow (x, 2)?

est-il plus efficace de faire la multiplication que la hausse à la puissance 2 en C ++?

J'essaie de faire des optimisations détaillées définitives. Le compilateur traitera-t-il x * x la même chose que POW (x, 2)? Si je me souviens bien, la multiplication était Mieux vaut pour une raison quelconque, mais peut-être que cela n'a pas d'importance en C ++ 11.

merci


5 commentaires

Dépend de savoir si l'optimiseur (ou la mise en œuvre de la fonction peut éventuellement) s'en occuper. Regardez l'assemblage produit ou la référence.


J'imagine x << 1 est ce qui se passe réellement.


x ^ 2 est pas augmenter au pouvoir 2!


J'espère certainement que le compilateur ne traitera pas x * x identique que x ^ 2 car ^ est xor pas pow .


ah oui désolé, je voulais dire POW au lieu de ^


8 Réponses :


5
votes

En ce qui concerne la performance, faites toujours des mesures pour sauvegarder vos hypothèses. Ne jamais faire confiance à la théorie, sauf si vous avez une référence qui prouve que la théorie correcte.

aussi, gardez à l'esprit que x ^ 2 est pas céder le carré de 2 en C ++: xxx

exemple en direct . < / p>


0 commentaires


0
votes

C / C ++ n'a pas d'opérateur de "puissance" natif. ^ est le bitwise exclusif ou (xor). Ainsi dit, la fonction pow est probablement ce que vous recherchez.

En réalité, pour le squalage d'un numéro entier, x * x est le moyen le plus immédiat, et un compilateur peut l'optimiser à la machine fonctionnant si disponible.


0 commentaires

6
votes

I Général, vous ne devriez pas vous inquiéter des optimisations de Pico-optimisations, à moins que vous n'ayez preuves qu'il existe un point chaud (sauf si vous avez profilé votre code dans des scénarios réalistes et que vous avez identifié un Chunk de code particulier. Gardez également à l'esprit que vos astuces intelligentes peuvent réellement causer des régressions de performance dans de nouveaux processeurs où vos hypothèses ne seront plus contestées.

Les changements algorithmiques sont là où vous obtiendrez le plus de bang pour votre calcul informatique. Concentrez-vous sur cela.

Tinkering avec multiplications et faire un piratage bit intelligent ... eh pas tellement de bangs là-bas * parce que la génération actuelle d'optimisation des compilateurs est vraiment assez excellente à leur travail. Cela ne veut pas dire qu'ils ne peuvent pas être battus. Ils peuvent, mais pas facilement et probablement seulement par quelques personnes comme Agner Fog.

* Il y a bien sûr des exceptions.


8 commentaires

+1 pour "Ils peuvent, mais pas facilement et probablement seulement par quelques personnes comme Agner FOG." N'est pas cette vérité!


Fait intéressant, la différence entre POW () et la multiplication est une différence algorithmique. ;) Considérons si l'OP a posé la même chose avec un exemple différent et un libellé différent: "POW (X, 8) exprime mon intention plus clairement que x x x x x x x < i> x x x. La multiplication est plus rapide? " Vous pouvez appeler cela une optimisation pico, mais aller de POW () à la multiplication est techniquement une amélioration algorithmique (+ constante). (... et comparé à la multiplication séquentielle naïf, "x2 = x x; x4 = x2 * x2; réponse = x4 * x4;" est également une expansion à code fixée d'une fonction d'alimentation entière améliorée d'algorithmie.)


@Mikes La différence peut être algorithmique conceptuellement, mais je suis sûr que le compilateur comprend ce que pow est et quelle sémantique il a et peut (généralement) l'optimiser assez bien. Donc, à moins qu'il y ait dure preuve que c'est un point chaud, ce type de chose doit être considéré comme une micro-optimisation.


Il reste encore deux problèmes avec ceux-ci: 1.) Prendre cette attitude sur votre codeBase signifie que vous pourriez avoir un grand nombre de POW () qui devraient être remplacés par des multiplications (plus une centaine de problèmes de perversions similaires). Les problèmes de performance sont souvent isolés à un "peu de points chauds", mais uniquement lorsque le programme a été codé de manière habituelle de manière efficace pour commencer. Ecrire tout à partir d'un point de vue "Performance dernier" signifie qu'au lieu de quelques points chauds, votre programme pourrait être toujours lent de partout (plus courant que les gens abusent de plus en plus / mal appliquer la "optimisation prématurée" maximale).


2.) Des questions telles que celles-ci sont importantes, car elles ne sont pas isolées pour suranaliser un bloc de code hautement spécifique. Au lieu de cela, ce sont des questions idiomatiques qui permettent aux gens de développer les compétences nécessaires pour déterminer habituellement les choses efficacement de manière cohérente. Développer de bonnes habitudes sur les noix et les boulons Le niveau peut sauver une tonne de temps de profilage, car la quantité d'efforts qu'il prend pour isoler les points chauds (sans parler de la masse perversive correcte) dépasse de loin la quantité d'efforts qu'il faut pour écrire un «bloc de construction» de base code efficacement la première fois (une fois que vous avez appris comment).


Je ne suis pas en désaccord avec vous; En fait, je pense que nous sommes d'accord avec conceptuellement, mais abordez la question des angles légèrement différents. Je pense certainement qu'on devrait essayer d'écrire un code efficace pour commencer, et cela signifie apprendre ce que fait et ne fait pas une différence dans ce contexte . Mais c'est très différent de la micro-optimisation comme vous allez.


Méfiez-vous également de la foi aveugle dans les compilateurs: il y a même dix ans, la plupart des gens ont supposé que les compilateurs ont effectué des optimisations sur une échelle qu'ils ne le font toujours pas aujourd'hui. (Au niveau de la micro-optimisation, la planification des instructions bénéficie toujours de lignes de code source de réorganisation!) POW () est rarement remplacée par des intrinsions entier: généralement le total intégral> float-> pow-> int est effectué et POW ne peut pas être optimisé parce que FP Math n'est pas associatif. Test rapide: avec GCC 4.8, POW (I, 2) est aussi rapide que je i, mais pow (i, 4) est 115 fois plus lent que je i i i i . Avec Clang 3.3, il est 337 fois plus lent, car il optimise mieux les multiplies.


Vient de voir votre commentaire: la micro-optimisation de votre optimisation est une solution brutalement lente / improductive de coder, surtout si vous vous laissez emporter et écrire un code de twiddling illisible (qui ne vous aidera probablement pas), ou si vous dépensez beaucoup de instructions de réorganisation temporelle pour aider le planificateur, etc. Ce n'est jamais une bonne idée d'écrire des "optimisations" illisibles avant le profilage, mais si vous décidez entre deux versions tout aussi lisibles de code idiomatique réparties sur le codebase, investissez tôt dans "l'apprentissage habituel Processus "est probablement sage ... même si cela signifie une micro-optimisation slog la première fois.



1
votes

pow est une fonction de bibliothèque, pas un opérateur. À moins que le compilateur ne puisse optimiser l'appel (qu'il fasse légitimement en profitant de sa connaissance du comportement des fonctions de la bibliothèque standard), appeler pow () imposera la surcharge d'un appel de fonctions et de toutes les choses supplémentaires, la fonction pow () doit faire.

le deuxième argument à pow () ne doit pas nécessairement être un entier; Par exemple, pow (x, 1.0 / 3.0) vous donnera une approximation de la racine de cube de x . Cela va nécessiter des calculs assez sophistiqués. Il peut revenir à la multiplication répétée si le deuxième argument est une faible valeur intégrale, mais il doit ensuite vérifier cela au moment de l'exécution.

Si le numéro que vous voulez carré est un entier, pow impliquera la convertir en double , puis convertir le résultat à un type d'entier, ce qui est relativement cher. et pourrait causer des erreurs d'arrondi subtiles.

Utiliser x * x est très probablement plus rapide et plus fiable que pow (x, 2) , et c'est plus simple. (Dans la plupart des contextes, la simplicité et la fiabilité sont des considérations plus importantes que la vitesse.)


0 commentaires

2
votes

La mise en œuvre de POW () implique généralement des logarithmes, la multiplication et l'expononentiaton, de sorte qu'il prendra certainement plus de temps qu'une simple multiplication. La plupart des processeurs haut de gamme modernes peuvent effectuer une multiplication dans quelques clockcycles pour des valeurs entières et une douzaine de cycles de virage flottant se multiplient. L'exponentiation est soit faite sous forme d'instructions complexes (microcoded) qui prend quelques dizaines de cycles ou plus, ou en série de multiplications et d'ajouts (généralement avec des nombres positifs et négatifs alternatifs, mais pas certainement). L'exponentiation est un processus similaire.

sur les processeurs inférieurs (par exemple les processeurs de bras ou plus x86), les résultats sont encore pires. Des centaines de cycles dans une opération de point flottant, ou dans certains processeurs, même des calculs de points flottants sont un certain nombre d'opérations entières qui effectuent les mêmes étapes que les instructions de flotteur sur des processeurs plus avancés, de sorte que le temps pris pour pow () < / code> pourrait être des milliers de cycles, comparés à une douzaine d'une multiplication.

Le choix est utilisé, tout le calcul sera significativement plus long qu'une simple multiplication.

La fonction pow () est utile lorsque l'exposant est grand ou non un entier. Même pour des exposants relativement volumineux, vous pouvez effectuer le calcul en marquant ou en cube plusieurs fois, et il sera plus rapide que pow () .

Bien sûr, le compilateur peut parfois être capable de comprendre ce que vous voulez faire et de le faire en tant que séquence de multiplications comme optimisation. Mais je ne voudrais pas compter sur ça.

Enfin, comme toujours, pour les questions de performance: s'il est vraiment important pour votre code, mesurez-le, votre compilateur peut être plus intelligent que vous minces. Si la performance n'est pas importante, effectuez le calcul du code le plus lisible.


0 commentaires

0
votes

Vous devriez lire le lien suivant Pourquoi GCC peut-il optimiser A * A * A * A * A * A à (A * A * A) * (A * A * A)?

pow (x, 2) sera très probablement converti en X x. Cependant, des pouvoirs plus élevés tels que le POW (X, 4) ne peuvent pas être effectués aussi de manière optimale que possible. Par exemple, POW (x, 4) pourrait être effectué en 3 multiplications x x x x ou en deux (x x) (x * x) en fonction de Dans quelle mesure vous souhaitez que la définition de point flottant soit (par défaut, je pense qu'il utilisera 3 multiplications.

Il serait intéressant de voir quoi, par exemple, POW (x * x, 2) produit avec et sans--ffast-maths.


0 commentaires

0
votes

Vous devriez examiner le modèle de fonction POP.Math's POW. Il prend l'exposant comme paramètre de modèle et calculez automatiquement, par exemple, POW <4> (x) sous (x * x) * (x * x).

http://www.boost.org/doc/libs/1_53_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/powers/ct_pow.html


0 commentaires