Voici une question amusante idiote:
Disons que nous devons effectuer une opération simple où nous avons besoin de la moitié de la valeur d'une variable. Il existe généralement deux façons de procéder:
y = x / 2.0;
// or...
y = x * 0.5;
En supposant que nous utilisons les opérateurs standard fournis avec le langage, lequel offre les meilleures performances?
Je suppose que la multiplication est généralement meilleure, alors j'essaie de m'en tenir à cela lorsque je code, mais j'aimerais le confirmer.
Bien que personnellement je sois intéressé par la réponse pour Python 2.4-2.5, n'hésitez pas à publier également une réponse pour d'autres langues! Et si vous le souhaitez, n'hésitez pas à publier d'autres méthodes plus sophistiquées (comme l'utilisation d'opérateurs de décalage au niveau du bit).
la source
Réponses:
Python:
la multiplication est 33% plus rapide
Lua:
=> pas de réelle différence
LuaJIT:
=> c'est seulement 5% plus rapide
conclusions: en Python, il est plus rapide de se multiplier que de diviser, mais à mesure que vous vous rapprochez du processeur en utilisant des VM ou des JIT plus avancés, l'avantage disparaît. Il est fort possible qu'une future machine virtuelle Python la rende inutile
la source
Utilisez toujours ce qui est le plus clair. Tout ce que vous faites est d'essayer de déjouer le compilateur. Si le compilateur est un peu intelligent, il fera de son mieux pour optimiser le résultat, mais rien ne peut faire en sorte que le prochain type ne vous déteste pas pour votre solution de changement de bits merdique (j'aime peu la manipulation au fait, c'est amusant. Mais amusant! = Lisible )
L'optimisation prématurée est la racine de tout Mal. Souvenez-vous toujours des trois règles d'optimisation!
Si vous êtes un expert et pouvez justifier le besoin, utilisez la procédure suivante:
De plus, faire des choses comme supprimer les boucles internes quand elles ne sont pas nécessaires ou choisir une liste chaînée sur un tableau pour un tri par insertion ne sont pas des optimisations, juste de la programmation.
la source
Je pense que cela devient tellement délicat que vous feriez mieux de faire tout ce qui rend le code plus lisible. À moins que vous n'effectuiez les opérations des milliers, voire des millions de fois, je doute que quiconque remarquera jamais la différence.
Si vous devez vraiment faire un choix, l'analyse comparative est la seule façon de procéder. Trouvez quelle (s) fonction (s) vous posent des problèmes, puis recherchez où dans la fonction les problèmes se produisent et corrigez ces sections. Cependant, je doute encore qu'une seule opération mathématique (même une opération répétée plusieurs, plusieurs fois) soit une cause de goulot d'étranglement.
la source
La multiplication est plus rapide, la division est plus précise. Vous perdrez de la précision si votre nombre n'est pas une puissance de 2:
Même si vous laissez le compilateur déterminer la constante inversée avec une précision parfaite, la réponse peut toujours être différente.
Le problème de vitesse n'a probablement d'importance que dans les langages C / C ++ ou JIT, et même dans ce cas uniquement si l'opération est dans une boucle à un goulot d'étranglement.
la source
y = x * (1.0/3.0);
et le compilateur calculera généralement 1/3 au moment de la compilation. Oui, 1/3 n'est pas parfaitement représentable dans IEEE-754, mais lorsque vous effectuez de l'arithmétique à virgule flottante, vous perdez de toute façon de la précision , que vous effectuiez une multiplication ou une division, car les bits de poids faible sont arrondis. Si vous savez que votre calcul est aussi sensible à l'erreur d'arrondi, vous devez également savoir comment traiter au mieux le problème.(1.0/3.0)
avec la division par3.0
. Je suis arrivé à 1.0000036666774155, et dans cet espace, 7,3% des résultats étaient différents. Je suppose qu'ils n'étaient différents que de 1 bit, mais comme l'arithmétique IEEE est garantie d'arrondir au résultat correct le plus proche, je maintiens ma déclaration selon laquelle la division est plus précise. Que la différence soit significative dépend de vous.Si vous souhaitez optimiser votre code tout en restant clair, essayez ceci:
Le compilateur devrait être capable de faire la division au moment de la compilation, vous obtenez donc une multiplication au moment de l'exécution. Je m'attendrais à ce que la précision soit la même que dans le
y = x / 2.0
cas.Là où cela peut avoir de l'importance, un LOT est dans les processeurs embarqués où une émulation en virgule flottante est nécessaire pour calculer l'arithmétique en virgule flottante.
la source
y = x / 2.0;
mais dans le monde réel, vous devrez peut-être cajoler le compilateur pour qu'il effectue une multiplication moins coûteuse. Il est peut-être moins clair pourquoiy = x * (1.0 / 2.0);
c'est mieux, et il serait plus clair de le dire à lay = x * 0.5;
place. Mais changez le2.0
en a7.0
et je préfère de beaucoup voiry = x * (1.0 / 7.0);
quey = x * 0.142857142857;
.Je vais juste ajouter quelque chose pour l'option "autres langues".
C: Comme il ne s'agit que d'un exercice académique qui ne fait vraiment aucune différence, j'ai pensé que j'apporterais quelque chose de différent.
J'ai compilé en assemblage sans optimisations et j'ai regardé le résultat.
Le code:
compilé avec
gcc tdiv.c -O1 -o tdiv.s -S
la division par 2:
et la multiplication par 0,5:
Cependant, lorsque j'ai changé ces
int
s endouble
s (ce que ferait probablement python), j'ai obtenu ceci:division:
multiplication:
Je n'ai évalué aucun de ce code, mais juste en examinant le code, vous pouvez voir qu'en utilisant des entiers, la division par 2 est plus courte que la multiplication par 2. En utilisant des doubles, la multiplication est plus courte car le compilateur utilise les opcodes en virgule flottante du processeur, qui probablement courir plus vite (mais en fait je ne sais pas) que de ne pas les utiliser pour la même opération. Donc en fin de compte, cette réponse a montré que la performance de la multiplication par 0,5 vs la division par 2 dépend de l'implémentation du langage et de la plateforme sur laquelle il fonctionne. Au final, la différence est négligeable et vous ne devriez pratiquement jamais vous inquiéter, sauf en termes de lisibilité.
En remarque, vous pouvez voir que dans mon programme
main()
renvoiea + b
. Lorsque j'enlève le mot-clé volatile, vous ne devinerez jamais à quoi ressemble l'assemblage (à l'exclusion de la configuration du programme):il a fait à la fois la division, la multiplication ET l'addition en une seule instruction! De toute évidence, vous n'avez pas à vous en soucier si l'optimiseur est respectable.
Désolé pour la réponse trop longue.
la source
movl $5, %eax
le nom de l'optimisation n'est pas important ni même pertinent. Vous vouliez juste être condescendant sur une réponse vieille de quatre ans.Premièrement, à moins que vous ne travailliez en C ou en ASSEMBLAGE, vous êtes probablement dans un langage de niveau supérieur où les blocages de mémoire et les frais généraux d'appels éclipseront absolument la différence entre multiplier et diviser au point de ne plus être pertinent. Alors, choisissez simplement ce qui se lit mieux dans ce cas.
Si vous parlez à un niveau très élevé, ce ne sera pas beaucoup plus lent pour tout ce pour quoi vous êtes susceptible de l'utiliser. Vous verrez dans d'autres réponses, les gens doivent faire un million de multiplications / divisions juste pour mesurer une différence inférieure à la milliseconde entre les deux.
Si vous êtes toujours curieux, d'un point de vue d'optimisation de bas niveau:
Divide a tendance à avoir un pipeline beaucoup plus long que de se multiplier. Cela signifie qu'il faut plus de temps pour obtenir le résultat, mais si vous pouvez occuper le processeur avec des tâches non dépendantes, cela ne vous coûtera pas plus qu'une multiplication.
La durée de la différence de pipeline dépend entièrement du matériel. Le dernier matériel que j'ai utilisé était quelque chose comme 9 cycles pour une multiplication FPU et 50 cycles pour une division FPU. Cela semble beaucoup, mais vous perdriez alors 1000 cycles pour un manque de mémoire, ce qui peut mettre les choses en perspective.
Une analogie consiste à mettre une tarte dans un micro-ondes pendant que vous regardez une émission de télévision. Le temps total qu'il vous a éloigné de l'émission télévisée correspond au temps qu'il a fallu pour le mettre au micro-ondes et le sortir du micro-ondes. Le reste de votre temps, vous avez toujours regardé l'émission télévisée. Donc, si la tarte a mis 10 minutes à cuire au lieu d'une minute, elle n'a en fait plus utilisé votre temps à regarder la télévision.
En pratique, si vous voulez arriver au niveau de vous soucier de la différence entre Multiply et Divide, vous devez comprendre les pipelines, le cache, les blocages de branche, la prédiction dans le désordre et les dépendances de pipeline. Si cela ne correspond pas à votre intention d'aller avec cette question, la bonne réponse est d'ignorer la différence entre les deux.
Il y a de nombreuses (nombreuses) années, il était absolument essentiel d'éviter les divisions et d'utiliser toujours les multiplications, mais à l'époque, les coups de mémoire étaient moins pertinents et les fractures étaient bien pires. Ces jours-ci, j'évalue la lisibilité plus haut, mais s'il n'y a pas de différence de lisibilité, je pense que c'est une bonne habitude d'opter pour des multiplications.
la source
Écrivez celui qui indique le plus clairement votre intention.
Une fois que votre programme fonctionne, déterminez ce qui est lent et accélérez.
Ne faites pas l'inverse.
la source
Faites tout ce dont vous avez besoin. Pensez d'abord à votre lecteur, ne vous inquiétez pas des performances tant que vous n'êtes pas sûr d'avoir un problème de performances.
Laissez le compilateur faire les performances pour vous.
la source
Si vous travaillez avec des entiers ou des types non flottants, n'oubliez pas vos opérateurs de décalage de bits: << >>
la source
En fait, il y a une bonne raison qu'en règle générale, la multiplication sera plus rapide que la division. La division en virgule flottante dans le matériel se fait soit avec des algorithmes de décalage et de soustraction conditionnelle ("division longue" avec des nombres binaires) ou - plus probablement de nos jours - avec des itérations comme l' algorithme de Goldschmidt . Le décalage et la soustraction nécessitent au moins un cycle par bit de précision (les itérations sont presque impossibles à paralléliser, tout comme le décalage et l'addition de multiplication), et les algorithmes itératifs effectuent au moins une multiplication par itération. Dans les deux cas, il est fort probable que la division prendra plus de cycles. Bien sûr, cela ne tient pas compte des bizarreries des compilateurs, du mouvement des données ou de la précision. En gros, cependant, si vous codez une boucle interne dans une partie sensible au temps d'un programme, écrire
0.5 * x
ou1.0/2.0 * x
plutôt quex / 2.0
c'est une chose raisonnable à faire. Le pédantisme du "code ce qui est le plus clair" est absolument vrai, mais tous les trois sont si proches en lisibilité que le pédantisme est dans ce cas juste pédant.la source
J'ai toujours appris que la multiplication est plus efficace.
la source
La multiplication est généralement plus rapide - certainement jamais plus lente. Cependant, s'il n'est pas critique pour la vitesse, écrivez ce qui est le plus clair.
la source
La division en virgule flottante est (généralement) particulièrement lente, donc si la multiplication en virgule flottante est également relativement lente, elle est probablement plus rapide que la division en virgule flottante.
Mais je suis plus enclin à répondre "ça n'a pas vraiment d'importance", à moins que le profilage n'ait montré que la division est un peu un goulot d'étranglement par rapport à la multiplication. J'imagine, cependant, que le choix de la multiplication par rapport à la division n'aura pas un impact important sur les performances de votre application.
la source
Cela devient plus une question lorsque vous programmez en assemblage ou peut-être en C. Je suppose qu'avec la plupart des langages modernes, cette optimisation est faite pour moi.
la source
Méfiez-vous de "deviner que la multiplication est généralement préférable, alors j'essaie de m'en tenir à cela lorsque je code",
Dans le contexte de cette question spécifique, mieux signifie ici «plus vite». Ce qui n'est pas très utile.
Penser à la vitesse peut être une grave erreur. Il y a de profondes implications d'erreur dans la forme algébrique spécifique du calcul.
Voir Arithmétique à virgule flottante avec analyse d'erreur . Voir Problèmes de base dans l'arithmétique à virgule flottante et l'analyse des erreurs .
Alors que certaines valeurs à virgule flottante sont exactes, la plupart des valeurs à virgule flottante sont une approximation; ce sont une valeur idéale plus une erreur. Chaque opération s'applique à la valeur idéale et à la valeur d'erreur.
Les plus gros problèmes viennent d'essayer de manipuler deux nombres presque égaux. Les bits les plus à droite (les bits d'erreur) en viennent à dominer les résultats.
Dans cet exemple, vous pouvez voir que lorsque les valeurs diminuent, la différence entre des nombres presque égaux crée des résultats différents de zéro où la réponse correcte est zéro.
la source
J'ai lu quelque part que la multiplication est plus efficace en C / C ++; Aucune idée concernant les langues interprétées - la différence est probablement négligeable en raison de tous les autres frais généraux.
À moins que cela ne devienne un problème, restez fidèle à ce qui est plus maintenable / lisible - je déteste quand les gens me disent cela, mais c'est tellement vrai.
la source
Je suggérerais la multiplication en général, car vous n'avez pas à passer les cycles à vous assurer que votre diviseur n'est pas 0. Cela ne s'applique pas, bien sûr, si votre diviseur est une constante.
la source
Java Android, profilé sur Samsung GT-S5830
Résultats?
La division est environ 20% plus rapide que la multiplication (!)
la source
a = i*0.5
, nona *= 0.5
. C'est ainsi que la plupart des programmeurs utiliseront les opérations.Comme pour les articles # 24 (la multiplication est plus rapide) et # 30 - mais parfois ils sont tous les deux tout aussi faciles à comprendre:
~ Je les trouve tous les deux tout aussi faciles à lire et je dois les répéter des milliards de fois. Il est donc utile de savoir que la multiplication est généralement plus rapide.
la source
Il y a une différence, mais elle dépend du compilateur. Au début, sur vs2003 (c ++), je n'ai eu aucune différence significative pour les types doubles (virgule flottante 64 bits). Cependant, en exécutant à nouveau les tests sur vs2010, j'ai détecté une énorme différence, jusqu'à un facteur 4 plus rapide pour les multiplications. En suivant cela, il semble que vs2003 et vs2010 génèrent un code fpu différent.
Sur un Pentium 4, 2,8 GHz, par rapport à 2003:
Sur un Xeon W3530, par rapport à 2003:
Sur un Xeon W3530, vs2010:
Il semble que sur vs2003 une division dans une boucle (donc le diviseur a été utilisé plusieurs fois) a été traduite en une multiplication avec l'inverse. Sur vs2010, cette optimisation n'est plus appliquée (je suppose car il y a un résultat légèrement différent entre les deux méthodes). Notez également que le processeur effectue des divisions plus rapidement dès que votre numérateur est 0,0. Je ne connais pas l'algorithme précis câblé dans la puce, mais peut-être que cela dépend du nombre.
Edit 18-03-2013: l'observation pour vs2010
la source
n/10.0
par une expression du formulaire(n * c1 + n * c2)
? Je m'attendrais à ce que sur la plupart des processeurs, une division prenne plus de deux multiplications et une division, et je crois que la division par n'importe quelle constante peut donner un résultat correctement arrondi dans tous les cas en utilisant la formulation indiquée.Voici une réponse idiote et amusante:
x / 2.0 n'est pas équivalent à x * 0.5
Disons que vous avez écrit cette méthode le 22 octobre 2008.
Maintenant, 10 ans plus tard, vous apprenez que vous pouvez optimiser ce morceau de code. La méthode est référencée dans des centaines de formules à travers votre application. Vous le changez donc et bénéficiez d'une amélioration remarquable des performances de 5%.
Était-ce la bonne décision de changer le code? En maths, les deux expressions sont en effet équivalentes. En informatique, cela n'est pas toujours vrai. Veuillez lire Minimiser l'effet des problèmes de précision pour plus de détails. Si vos valeurs calculées sont - à un moment donné - comparées à d'autres valeurs, vous modifierez le résultat des cas extrêmes. Par exemple:
L'essentiel est; une fois que vous vous êtes contenté de l'un des deux, respectez-le!
la source
2
et les0.5
nombres peuvent être représentés exactement dans IEEE 754, sans aucune perte de précision (contrairement par exemple0.4
ou0.1
, ils ne peuvent pas).Eh bien, si nous supposons qu'une opération d'ajout / sous-piste coûte 1, multipliez les coûts par 5 et divisez les coûts par 20.
la source
Après une discussion aussi longue et intéressante, voici mon point de vue sur ceci: Il n'y a pas de réponse définitive à cette question. Comme certains l'ont souligné, cela dépend à la fois du matériel (cf. piotrk et gast128 ) et du compilateur (cf. les tests de @Javier ). Si la vitesse n'est pas critique, si votre application n'a pas besoin de traiter en temps réel une énorme quantité de données, vous pouvez opter pour la clarté en utilisant une division, alors que si la vitesse de traitement ou la charge du processeur sont un problème, la multiplication peut être la plus sûre. Enfin, à moins que vous ne sachiez exactement sur quelle plateforme votre application sera déployée, le benchmark n'a pas de sens. Et pour la clarté du code, un seul commentaire ferait l'affaire!
la source
Techniquement, la division n'existe pas, il y a juste une multiplication par éléments inverses. Par exemple, vous ne divisez jamais par 2, vous multipliez en fait par 0,5.
La «division» - disons-nous qu'elle existe pendant une seconde - est toujours plus difficile que la multiplication parce que pour «diviser»
x
pary
un il faut d'abord calculer la valeury^{-1}
telle quey*y^{-1} = 1
puis faire la multiplicationx*y^{-1}
. Si vous le savez déjà, ney^{-1}
pas le calculer à partir dey
doit être une optimisation.la source