Dois-je utiliser la multiplication ou la division?

Voici une question amusante:

disons que nous devons effectuer une opération simple où nous avons besoin de la moitié de la valeur d'une variable. Il y a typiquement deux façons de faire ceci:

y = x / 2.0;
// or...
y = x * 0.5;

en supposant que nous utilisions les opérateurs standard fournis avec le langage, lequel est le plus performant?

je devine que la multiplication est généralement mieux, donc j'essaie de m'y tenir quand je code, mais je tiens à le confirmer.

bien que personnellement je suis intéressé par la réponse pour Python 2.4-2.5, n'hésitez pas à poster également une réponse pour d'autres langues! Et si vous le souhaitez, n'hésitez pas à poster d'autres moyens plus fantaisistes (comme l'utilisation d'opérateurs de changement bitwise).

106
demandé sur Edmundito 2008-10-22 20:01:23

25 réponses

Python:

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 / 2.0'
real    0m26.676s
user    0m25.154s
sys     0m0.076s

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 * 0.5'
real    0m17.932s
user    0m16.481s
sys     0m0.048s

la multiplication est 33% plus rapide

Lua:

time lua -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m7.956s
user    0m7.332s
sys     0m0.032s

time lua -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m7.997s
user    0m7.516s
sys     0m0.036s

= > pas de différence réelle

LuaJIT:

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m1.921s
user    0m1.668s
sys     0m0.004s

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m1.843s
user    0m1.676s
sys     0m0.000s

=>c'est seulement 5% plus rapide

conclusions: en Python, il est plus rapide de se multiplier que de se diviser, mais au fur et à mesure que vous vous rapprochez du CPU en utilisant des VMs ou des JITs plus avancés, l'avantage disparaît. C'est tout à fait possible qu'un future VM de Python la rendrait hors de propos

72
répondu Javier 2008-10-22 17:03:37

Toujours utiliser ce qui est le plus clair. Autre chose que vous faites est d'essayer de déjouer le compilateur. Si le compilateur est du tout intelligent, il fera de son mieux pour optimiser le résultat, mais rien ne peut empêcher le prochain gars de vous détester pour votre solution de bitshifting merdique (j'adore la manipulation de bits d'ailleurs, c'est amusant. Mais le plaisir != lisible)

l'optimisation prématurée est la racine de tout le mal. Rappelez-vous toujours les trois règles de l'optimisation!

  1. n'optimise pas.
  2. si vous êtes un expert, voir la règle # 1
  3. Si vous êtes un expert et peut justifier le besoin, utilisez la procédure suivante:

    • Code il unoptimized
    • déterminez à quelle vitesse est "assez rapide" -- notez quelle exigence de l'utilisateur / histoire exige cette métrique.
    • Ecrire un essai de vitesse
    • tester le code existant--si c'est rapide assez, vous avez terminé.
    • Recode il optimisé
    • Test Code optimisé. Si elle ne correspond pas à la métrique, jetez-la ET CONSERVEZ l'original.
    • si elle répond au critère, conserver le code d'origine sous la forme de commentaires

aussi, faire des choses comme enlever les boucles internes quand elles ne sont pas nécessaires ou choisir une liste liée sur un tableau pour un tri d'insertion ne sont pas des optimisations, juste programmation.

61
répondu Bill K 2008-10-22 16:37:56

je pense que cela devient tellement nitpicky que vous seriez mieux de faire ce qui rend le code plus lisible. Si vous n'opérez pas des milliers, voire des millions de fois, je doute que quelqu'un remarque la différence.

si vous devez vraiment faire un choix, l'analyse comparative est la seule solution. Trouvez quelle(S) fonction (s) vous donne des problèmes, puis trouvez où dans la fonction les problèmes se produisent, et corrigez ces sections. Cependant, j'ai encore des doutes qu'une seule opération mathématique (même répétée plusieurs fois) serait une cause de goulot d'étranglement.

47
répondu Thomas Owens 2008-10-22 16:09:46
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:

y = x / 3.0;
y = x * 0.333333;  // how many 3's should there be, and how will the compiler round?

même si vous laissez le compilateur comprendre la constante inversée pour une précision parfaite, la réponse peut être différente.

x = 100.0;
x / 3.0 == x * (1.0/3.0)  // is false in the test I just performed

la question de la vitesse n'est susceptible de se poser que dans les langages C/C++ ou JIT, et même alors seulement si l'opération est en boucle à un goulot d'étranglement.

31
répondu Mark Ransom 2017-08-27 20:46:32

si vous voulez optimiser votre code mais être clair, essayez ceci:

y = x * (1.0 / 2.0);

le compilateur devrait être capable de faire la division au moment de la compilation, donc vous obtenez une multiplication au moment de l'exécution. J'attendrais la précision pour être le même que dans le y = x / 2.0 .

où cela peut être important beaucoup est dans les processeurs intégrés où l'émulation en virgule flottante est nécessaire pour calculer l'arithmétique en virgule flottante.

24
répondu Jason S 2009-03-18 00:19:54

va Juste ajouter quelque chose pour les "autres langues".

C: puisque c'est juste un exercice académique que vraiment ne fait aucune différence, j'ai pensé que je contribuerais quelque chose de différent.

j'ai compilé à l'assemblage sans optimisations et ai regardé le résultat.

Le code:

int main() {

    volatile int a;
    volatile int b;

    asm("## 5/2\n");
    a = 5;
    a = a / 2;

    asm("## 5*0.5");
    b = 5;
    b = b * 0.5;

    asm("## done");

    return a + b;

}

compilé avec gcc tdiv.c -O1 -o tdiv.s -S

la division par 2:

movl    , -4(%ebp)
movl    -4(%ebp), %eax
movl    %eax, %edx
shrl    , %edx
addl    %edx, %eax
sarl    %eax
movl    %eax, -4(%ebp)

et la multiplication par 0.5:

movl    , -8(%ebp)
movl    -8(%ebp), %eax
pushl   %eax
fildl   (%esp)
leal    4(%esp), %esp
fmuls   LC0
fnstcw  -10(%ebp)
movzwl  -10(%ebp), %eax
orw 72, %ax
movw    %ax, -12(%ebp)
fldcw   -12(%ebp)
fistpl  -16(%ebp)
fldcw   -10(%ebp)
movl    -16(%ebp), %eax
movl    %eax, -8(%ebp)

cependant, quand j'ai changé ces int s en double s (ce qui est ce que python ferait probablement), j'ai eu ceci:

de la division:

flds    LC0
fstl    -8(%ebp)
fldl    -8(%ebp)
flds    LC1
fmul    %st, %st(1)
fxch    %st(1)
fstpl   -8(%ebp)
fxch    %st(1)

multiplication:

fstpl   -16(%ebp)
fldl    -16(%ebp)
fmulp   %st, %st(1)
fstpl   -16(%ebp)

Je n'ai comparé aucun de ces codes, mais juste en examinant le code, vous pouvez voir que l'utilisation la division par 2 est plus courte que la multiplication par 2. En utilisant des doubles, la multiplication est plus courte parce que le compilateur utilise les opcodes à virgule flottante du processeur, qui courent probablement plus vite (mais en fait je ne sais pas) que de ne pas les utiliser pour la même opération. En fin de compte, cette réponse a montré que la performance de multiplaction par 0,5 vs. division par 2 dépend de la mise en œuvre du langage et de la plate-forme sur laquelle il fonctionne. En fin de compte, la différence est négligeable et est quelque chose vous ne devriez pratiquement jamais vous inquiéter, sauf en termes de lisibilité.

comme note, vous pouvez voir que dans mon programme main() retourne a + b . Lorsque j'enlève le mot-clé volatile, vous ne devinerez jamais à quoi ressemble l'assemblage (à l'exclusion de la configuration du programme):

## 5/2

## 5*0.5
## done

movl    , %eax
leave
ret

il a fait à la fois la division, la multiplication, et l'addition dans une seule instruction! Clairement, vous n'avez pas à vous soucier de cela si l'optimiseur est n'importe quelle sorte de respectable.

désolé pour la réponse trop longue.

18
répondu Carson Myers 2009-12-27 08:57:06

écrivez ce qui exprime le plus clairement votre intention.

après que votre programme fonctionne, trouvez ce qui est lent, et faites-le plus rapide.

ne faites pas l'inverse.

8
répondu Jay Bazuzi 2008-10-22 16:31:35

faites ce que vous voulez. Pensez à votre lecteur d'abord, ne vous inquiétez pas de la performance jusqu'à ce que vous êtes sûr que vous avez un problème de performance.

laissez le compilateur faire la performance pour vous.

7
répondu buti-oxa 2008-10-22 16:12:01

tout d'abord, à moins que vous ne travailliez en C ou en assemblée, vous êtes probablement dans un langage de niveau supérieur où les décrochages de mémoire et les frais généraux d'appel vont tout à fait éclipser la différence entre multiplier et diviser au point de ne plus être pertinent. Donc, choisissez ce qui se lit mieux dans ce cas.

Si vous parlez d'un niveau très élevé, il ne sera pas sensiblement plus lent pour tout ce que vous êtes susceptible de l'utiliser pour. Vous verrez dans d'autres réponses, les gens ont besoin de faire un million multiplier / diviser juste pour mesurer une différence de sous-millisecondes entre les deux.

si vous êtes toujours curieux, d'un point de vue d'optimisation de bas niveau:

Divide tend à avoir un pipeline beaucoup plus long que multiplier. Cela signifie qu'il faut plus de temps pour obtenir le résultat, mais si vous pouvez garder le processeur occupé avec les tâches dépendantes, alors il ne fait pas de vous coûter plus qu'une multiplication.

Quelle est la longueur du pipeline la différence est dépend entièrement du matériel. Le dernier matériel que j'ai utilisé était quelque chose comme 9 cycles pour un FPU multiplier et 50 cycles pour un FPU diviser. Ça semble beaucoup, mais ensuite vous perdriez 1000 cycles pour un manque de mémoire, donc ça peut mettre les choses en perspective.

une analogie est de mettre une tarte dans un micro-ondes pendant que vous regardez une émission de télévision. Le temps total que cela vous a pris loin de l'émission de télévision est combien de temps il a été de le mettre dans le micro-ondes, et de le sortir du micro-ondes. Le reste de votre temps vous avez encore regardé l'émission de TÉLÉVISION. Donc, si la tarte a pris 10 minutes à cuisiner au lieu d'une minute, elle n'a pas réellement utilisé plus de votre temps de télévision.

en pratique, si vous vous souciez de la différence entre multiplier et diviser, vous devez comprendre les pipelines, la mémoire cache, les décrochages de branches, les prédictions hors de l'ordre et les dépendances des pipelines. Si cela ne ressemble pas à où vous aviez l'intention d'aller avec cette question, alors le correct la réponse est d'ignorer la différence entre les deux.

il y a de nombreuses années, il était absolument essentiel d'éviter les divisions et de toujours utiliser les multiples, mais à l'époque les coups de mémoire étaient moins pertinents, et les divisions étaient bien pires. Ces jours-ci, je note lisibilité plus élevé, mais s'il n'y a pas de différence de lisibilité, je pense que c'est une bonne habitude d'opter pour les multiples.

7
répondu James Podesta 2016-10-12 08:20:08

si vous travaillez avec des types de points entiers ou non flottants n'oubliez pas vos opérateurs de bitshifting: < < > >

    int y = 10;
    y = y >> 1;
    Console.WriteLine("value halved: " + y);
    y = y << 1;
    Console.WriteLine("now value doubled: " + y);
4
répondu sbeskur 2008-10-22 16:08:44

la Multiplication est généralement plus rapide - certainement jamais plus lent. Cependant, s'il n'est pas critique de vitesse, écrivez celui qui est le plus clair.

4
répondu Dan Hewett 2008-10-22 16:08:56

en fait, il y a une bonne raison qu'en règle générale la multiplication par pouce sera plus rapide que la division. La division en virgule flottante dans le matériel est faite soit avec des algorithmes de déplacement et de soustraction conditionnelle ("division longue "avec des nombres binaires) ou - plus probablement ces jours - ci-avec des itérations comme Goldschmidt algorithme. 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, comme le sont les shift-and-add of multiplication), et les algorithmes itératifs font au moins une multiplication par itération . Dans les deux cas, il est fort probable que la division mettra plus de cycles. Bien sûr, cela ne tient pas compte des bizarreries dans les compilateurs, le mouvement des données, ou 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 ou 1.0/2.0 * x plutôt que x / 2.0 est une chose raisonnable à faire. Le pédantisme de "code ce qui est le plus clair" est absolument vrai, mais tous les trois sont si proches dans la lisibilité que le pédantisme est dans ce cas juste pédant.

4
répondu Gene 2012-06-30 04:20:49

la division en virgule flottante est (généralement) particulièrement lente, alors que la multiplication en virgule flottante est aussi relativement lente, elle est probablement plus rapide que la division en virgule flottante.

mais je suis plus enclin à répondre "il n'a pas vraiment d'importance", à moins que le profilage a montré que la division est un peu goulot vs. multiplication. Je devine, cependant, que le choix de la multiplication par opposition à la division ne va pas avoir un grand impact de performance dans votre application.

3
répondu mipadi 2008-10-22 16:12:02

cela devient plus d'une question quand vous programmez en assemblée ou peut-être C. je pense qu'avec la plupart des langages modernes que l'optimisation comme ceci est fait pour moi.

2
répondu Seamus 2008-10-22 16:44:30

méfiez-vous de "deviner la multiplication est généralement mieux, alors j'essaie de m'y tenir quand je code, "

dans le contexte de cette question spécifique,"mieux" signifie ici "plus rapide". 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 une analyse d'erreur . Voir Questions de Base en Arithmétique à virgule Flottante et une Analyse d'Erreur .

bien que certaines valeurs à virgule flottante soient exactes, la plupart des valeurs à virgule flottante sont une approximation; il s'agit d'une valeur idéale plus une erreur. Chaque opération s'applique à la valeur idéale et la valeur de l'erreur.

les plus gros problèmes viennent d'essayer de manipuler deux nombres presque égaux. Les bits les plus à droite (les bits d'erreur) viennent dominer les résultats.

>>> for i in range(7):
...     a=1/(10.0**i)
...     b=(1/10.0)**i
...     print i, a, b, a-b
... 
0 1.0 1.0 0.0
1 0.1 0.1 0.0
2 0.01 0.01 -1.73472347598e-18
3 0.001 0.001 -2.16840434497e-19
4 0.0001 0.0001 -1.35525271561e-20
5 1e-05 1e-05 -1.69406589451e-21
6 1e-06 1e-06 -4.23516473627e-22

dans cet exemple, vous pouvez voir que lorsque les valeurs deviennent plus petites, la différence entre des nombres presque égaux crée des résultats non-nuls où la bonne réponse est zéro.

2
répondu S.Lott 2008-10-23 00:53:04

j'ai toujours appris que la multiplication est plus efficace.

1
répondu Toon Krijthe 2008-10-22 16:07:12

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 qu'il ne devienne une question bâton avec ce qui est plus maintenable/lisible - je déteste quand les gens me disent ceci mais son si vrai.

1
répondu Christopher Lightfoot 2008-10-22 16:09:57

je suggère la multiplication en général, parce que vous n'avez pas à passer les cycles EN s'assurant que votre diviseur n'est pas 0. Cela ne s'applique pas, bien sûr, si le diviseur est une constante.

1
répondu Steve 2008-10-22 16:21:18

Java android, profilé sur Samsung GT-S5830

public void Mutiplication()
{
    float a = 1.0f;

    for(int i=0; i<1000000; i++)
    {
        a *= 0.5f;
    }
}
public void Division()
{
    float a = 1.0f;

    for(int i=0; i<1000000; i++)
    {
        a /= 2.0f;
    }
}

résultats?

Multiplications():   time/call: 1524.375 ms
Division():          time/call: 1220.003 ms
La Division

est environ 20% plus rapide que la multiplication (!)

1
répondu PiotrK 2011-10-11 17:53:18

comme avec les billets # 24 (la multiplication est plus rapide) et #30 - mais parfois ils sont tous les deux tout aussi faciles à comprendre:

1*1e-6F;

1/1e6F;

~ je les trouve aussi faciles à lire, et je dois les répéter des milliards de fois. Donc, il est utile de savoir que la multiplication est généralement plus rapide.

1
répondu Chris 2011-12-27 16:42:09

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 (64 bit floating point). Cependant, en relançant les tests sur vs2010, j'ai détecté une énorme différence, jusqu'à un facteur 4 plus rapide pour les multiplications. Il semble que vs2003 et vs2010 génèrent des codes fpu différents.

sur un Pentium 4, 2,8 GHz, vs2003:

  • la Multiplication: 8.09
  • Division: 7,97

Sur un Xeon W3530 vs2003:

  • Multiplication: 4.68
  • Division: 4.64

Sur un Xeon W3530, vs2010:

  • Multiplication: 5.33
  • Division: 21.05

Il semble que sur vs2003 une division dans une boucle (donc le diviseur a été utilisé plusieurs fois) a été traduit en une multiplication avec l'inverse. Sur vs2010 cette optimisation n'est plus appliquée (je suppose parce qu'il y a un résultat légèrement différent entre les deux méthodes). Notez également que le cpu effectue des divisions plus rapidement dès que votre numérateur est 0.0. Je ne connais pas l'algorithme précis relié à la puce, mais il dépend peut-être du nombre.

Edit 18-03-2013: l'observation pour vs2010

1
répondu gast128 2013-07-11 14:42:15

Eh bien, si nous supposons qu'un additionnez/soustrayez les coûts d'exploitation 1, alors multipliez les coûts 5, et divisez les coûts environ 20.

0
répondu matma 2008-10-22 16:10:39

après une si longue et intéressante discussion 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 @Javier 's tests). Si la vitesse n'est pas critique, si votre application n'a pas besoin de traiter en temps réel énorme quantité de données, vous pouvez opter pour la clarté en utilisant une division alors que si le traitement la vitesse ou la charge du processeur sont un problème, la multiplication pourrait être le plus sûr. Enfin, à moins que vous ne sachiez exactement sur quelle plate-forme votre application sera déployée, benchmark n'a pas de sens. Et pour la clarté du code, un seul commentaire ferait l'affaire!

0
répondu Jean-François 2017-05-23 11:54:31

Voici un drôle de plaisir réponse:

x / 2.0 est pas est équivalent à x * 0.5

disons que vous avez écrit cette méthode le 22 Octobre 2008.

double half(double x) => x / 2.0;

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 tout au long de votre application. Donc vous le changez, et l'expérience d'un remarquable 5% d'amélioration des performances.

double half(double x) => x * 0.5;

c'Était la bonne décision de changer le code? En mathématiques, les deux expressions sont en fait équivalentes. En informatique, qui n'est pas toujours vrai. S'il vous plaît lire minimiser l'effet des problèmes de précision pour plus de détails. Si vos valeurs calculées sont - à un certain point-comparées à d'autres valeurs, vous changerez le résultat des cas de bord. Par exemple:

double quantize(double x)
{
    if (half(x) > threshold))
        return 1;
    else
        return -1;
}

la ligne de fond est; une fois que vous vous contentez de l'un des deux, puis coller à elle!

0
répondu l33t 2018-05-29 10:34:26

techniquement, il n'y a pas de division, il y a juste multiplication par éléments inverses. Par exemple vous ne divisez jamais par 2, vous multipliez en fait par 0.5.

'Division' - nous nous rendons compte qu'il existe pour une seconde - est toujours plus difficile que la multiplication parce que pour' diviser ' x par y il faut d'abord calculer la valeur y^{-1} tel que y*y^{-1} = 1 et puis faire la multiplication x*y^{-1} . Si vous savez déjà y^{-1} alors ne pas le calculer de y doit être une optimisation.

-2
répondu briantyler 2012-01-20 15:34:53