it-swarm.com.de

Soll ich Multiplikation oder Division verwenden?

Hier ist eine dumme lustige Frage:

Nehmen wir an, wir müssen eine einfache Operation ausführen, bei der wir die Hälfte des Wertes einer Variablen benötigen. Es gibt typischerweise zwei Möglichkeiten, dies zu tun:

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

Angenommen, wir verwenden die Standardoperatoren, die mit der Sprache geliefert werden, welcher hat eine bessere Leistung?

Vermutlich ist Multiplikation in der Regel besser, also versuche ich mich beim Codieren daran zu halten, aber ich möchte das gerne bestätigen. 

Obwohl ich persönlich an der Antwort für Python 2.4-2.5 interessiert bin, können Sie auch eine Antwort für andere Sprachen posten! Und wenn Sie möchten, können Sie auch gerne andere ausgefallene Möglichkeiten posten (z. B. bitweise Verschiebungsoperatoren verwenden).

108
Edmundito

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

multiplikation ist 33% schneller

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

=> kein echter Unterschied

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

=> es ist nur 5% schneller

schlussfolgerungen: In Python ist es schneller zu multiplizieren als zu teilen, aber wenn Sie sich mit fortgeschritteneren VMs oder JITs der CPU nähern, verschwindet der Vorteil. Es ist durchaus möglich, dass ein zukünftiger Python VM dies irrelevant macht

73
Javier

Verwenden Sie immer das, was am klarsten ist. Alles, was Sie sonst noch tun, ist den Compiler zu überlisten. Wenn der Compiler überhaupt intelligent ist, wird er das Beste tun, um das Ergebnis zu optimieren, aber nichts kann den nächsten Kerl dazu bringen, Sie für Ihre beschissene Bitverschiebungslösung zu hassen (ich liebe übrigens die Bit-Manipulation, es macht Spaß. Aber Spaß! = Lesbar )

Vorzeitige Optimierung ist die Wurzel allen Übels. Denken Sie immer an die drei Regeln der Optimierung!

  1. Nicht optimieren.
  2. Wenn Sie ein Experte sind, lesen Sie Regel 1
  3. Wenn Sie ein Experte sind und die Notwendigkeit begründen können, gehen Sie wie folgt vor:

    • Code nicht optimiert
    • bestimmen Sie, wie schnell "schnell genug" ist. Beachten Sie, für welche Benutzeranforderung/Story diese Metrik erforderlich ist.
    • Schreibe einen Geschwindigkeitstest
    • Bestehenden Code testen - Wenn es schnell genug ist, sind Sie fertig.
    • Recode es optimiert
    • Testen Sie optimierten Code. WENN sie die Metrik nicht erfüllt, werfen Sie sie weg und behalten Sie das Original.
    • Wenn es den Test erfüllt, behalten Sie den ursprünglichen Code als Kommentar bei

Auch das Entfernen von inneren Schleifen, wenn sie nicht erforderlich sind, oder das Auswählen einer verknüpften Liste über ein Array für eine Einfügesortierung sind keine Optimierungen, sondern nur Programmieren.

62
Bill K

Ich denke, das wird so pingelig, dass man besser tun könnte, was den Code lesbarer macht. Wenn Sie die Operationen nicht zu Tausenden, wenn nicht zu Millionen, durchführen, bezweifle ich, dass jemand den Unterschied jemals bemerken wird.

Wenn Sie wirklich die Wahl treffen müssen, ist Benchmarking der einzige Weg. Finden Sie heraus, welche Funktion (en) Ihnen Probleme bereiten, und finden Sie heraus, wo in der Funktion die Probleme auftreten, und beheben Sie diese Abschnitte. Ich bezweifle jedoch immer noch, dass eine einzige mathematische Operation (selbst eine, die sich viele Male wiederholt hat) eine Ursache für einen Engpass sein würde.

47
Thomas Owens

Multiplikation ist schneller, Division ist genauer. Sie verlieren etwas Präzision, wenn Ihre Zahl nicht 2 ist:

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

Selbst wenn Sie den Compiler die invertierte Konstante mit perfekter Genauigkeit ermitteln lassen, kann die Antwort dennoch unterschiedlich sein.

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

Das Geschwindigkeitsproblem ist nur in C/C++ - oder JIT-Sprachen von Bedeutung und auch dann nur, wenn sich der Vorgang in einer Schleife an einem Engpass befindet.

33
Mark Ransom

Wenn Sie Ihren Code optimieren möchten, aber dennoch klar sind, versuchen Sie Folgendes:

y = x * (1.0 / 2.0);

Der Compiler sollte in der Lage sein, die Division zur Kompilierzeit durchzuführen, damit Sie zur Laufzeit eine Multiplikation erhalten. Ich würde erwarten, dass die Genauigkeit dieselbe ist wie im y = x / 2.0-Fall.

Wo dies von Belang sein kann, ist eine LOT in eingebetteten Prozessoren, bei denen eine Gleitkommaemulation erforderlich ist, um eine Gleitkomma-Arithmetik zu berechnen.

24
Jason S

Fügen Sie einfach etwas für die Option "Andere Sprachen" hinzu.
C: Da dies nur eine akademische Übung ist, die wirklich keinen Unterschied macht, dachte ich, ich würde etwas anderes beitragen.

Ich habe die Assembly ohne Optimierungen kompiliert und das Ergebnis betrachtet.
Der 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;

}

mit gcc tdiv.c -O1 -o tdiv.s -S kompiliert

die Division durch 2:

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

und die Multiplikation mit 0,5:

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

Als ich jedoch ints in doubles änderte (was wahrscheinlich Python tun würde), bekam ich Folgendes:

einteilung:

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

multiplikation:

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

Ich habe keinen Code für diesen Code getestet. Wenn Sie jedoch nur den Code untersuchen, können Sie feststellen, dass die Division um 2 kürzer als die Multiplikation mit 2 ist. Bei der Verwendung von Doubles ist die Multiplikation kürzer, da der Compiler die Gleitkomma-Opcodes des Prozessors verwendet laufen wahrscheinlich schneller (aber ich weiß es eigentlich nicht), als sie nicht für dieselbe Operation zu verwenden. Letztendlich hat diese Antwort gezeigt, dass die Leistung von Multiplaction um 0,5 gegenüber Division durch 2 von der Implementierung der Sprache und der Plattform abhängt, auf der sie ausgeführt wird. Letztendlich ist der Unterschied vernachlässigbar und sollte sich fast nie sorgen, außer in Bezug auf die Lesbarkeit.

Als Randbemerkung können Sie sehen, dass in meinem Programm main()a + b zurückgegeben wird. Wenn ich das volatile Keyword wegnehme, erraten Sie nie, wie die Assembly aussieht (mit Ausnahme des Programm-Setups):

## 5/2

## 5*0.5
## done

movl    $5, %eax
leave
ret

es hat sowohl die Division als auch die Multiplikation UND die Addition in einer einzigen Anweisung ausgeführt! Natürlich müssen Sie sich keine Sorgen machen, wenn das Optimierungsprogramm respektabel ist.

Entschuldigung für die zu lange Antwort.

19
Carson Myers

Erstens, wenn Sie nicht in C oder Assembly arbeiten, befinden Sie sich wahrscheinlich in einer höheren Programmiersprache, in der Gedächtnisstillstände und generelle Anrufaufrufe den Unterschied zwischen Multiplizieren und Teilen auf den Punkt der Irrelevanz zwingen. Wählen Sie einfach aus, was in diesem Fall besser ist.

Wenn Sie von einem sehr hohen Niveau sprechen, ist es für alles, für das Sie es wahrscheinlich verwenden werden, nicht messbar langsamer. Sie werden in anderen Antworten sehen, die Leute müssen eine Million Multiplikationen/Trennungen durchführen, nur um einen Unterschied zwischen den Millisekunden zu messen.

Wenn Sie immer noch neugierig sind, aus Sicht der Optimierung auf niedriger Ebene:

Divide tendiert dazu, eine wesentlich längere Pipeline zu haben als sich zu multiplizieren. Dies bedeutet, dass es länger dauert, um das Ergebnis zu erhalten. Wenn Sie den Prozessor jedoch mit nicht abhängigen Aufgaben beschäftigen können, kostet Sie das nicht mehr als ein Vielfaches.

Wie lang der Pipeline-Unterschied ist, hängt vollständig von der Hardware ab. Die letzte Hardware, die ich verwendete, war etwa 9 Zyklen für eine FPU-Multiplikation und 50 Zyklen für eine FPU-Division. Klingt viel, aber dann verlieren Sie 1000 Zyklen für einen Speicherfehler, sodass die Dinge in die richtige Perspektive gerückt werden können.

Eine Analogie gibt einen Kuchen in die Mikrowelle, während Sie eine Fernsehsendung ansehen. Die Gesamtzeit, die Sie von der Fernsehsendung brauchten, ist, wie lange es dauerte, sie in die Mikrowelle zu legen und aus der Mikrowelle zu nehmen. Den Rest deiner Zeit hast du immer noch die Fernsehsendung gesehen. Wenn also der Kuchen 10 Minuten brauchte, um zu kochen, anstatt 1 Minute, brauchte er keine Fernsehzeit mehr.

Wenn Sie sich in der Praxis um den Unterschied zwischen Multiply und Divide kümmern, müssen Sie sich mit Pipelines, Cache, Verzweigungen, Verzerrungen, Out-of-Order-Prognosen und Pipeline-Abhängigkeiten auskennen. Wenn dies nicht so klingt, als wollten Sie diese Frage beantworten, ist die richtige Antwort, den Unterschied zwischen den beiden zu ignorieren. 

Vor vielen (vielen) Jahren war es absolut wichtig, Trennungen zu vermeiden und immer Multiplikationen zu verwenden, aber damals waren Memory-Treffer weniger relevant und Trennungen waren viel schlimmer. Heutzutage stelle ich die Lesbarkeit höher ein, aber wenn es keinen Unterschied in der Lesbarkeit gibt, denke ich, ist es eine gute Angewohnheit, sich für Multiplikationen zu entscheiden.

9
James Podesta

Schreiben Sie, was klarer Ihre Absicht ist.

Wenn Ihr Programm funktioniert, finden Sie heraus, was langsam ist, und machen Sie das schneller.

Mach es nicht anders herum.

8
Jay Bazuzi

Tu was immer du brauchst. Denken Sie zuerst an Ihren Leser. Machen Sie sich keine Gedanken über die Leistung, bis Sie sicher sind, dass Sie ein Leistungsproblem haben.

Lassen Sie den Compiler die Leistung für Sie übernehmen.

7
buti-oxa

Wenn Sie mit Ganzzahlen oder Nicht-Fließkommatypen arbeiten, vergessen Sie nicht Ihre Bitverschiebungsoperatoren: << >> 

    int y = 10;
    y = y >> 1;
    Console.WriteLine("value halved: " + y);
    y = y << 1;
    Console.WriteLine("now value doubled: " + y);
4
sbeskur

Die Multiplikation ist in der Regel schneller - sicherlich niemals langsamer. Wenn es jedoch nicht auf die Geschwindigkeit ankommt, schreiben Sie das, was am klarsten ist.

4
Dan Hewett

Eigentlich gibt es einen guten Grund dafür, dass die Daumenvervielfachung im Allgemeinen schneller ist als die Division. Die Fließkommadivision in Hardware erfolgt entweder mit Shift- und Conditional-Subtract-Algorithmen ("lange Division" mit Binärzahlen) oder - wahrscheinlicher - mit Iterationen wie Goldschmidt - Algorithmus. Verschiebung und Subtraktion erfordern mindestens einen Zyklus pro Bit Genauigkeit (die Iterationen sind nahezu unmöglich zu parallelisieren, ebenso wie das Shift-and-Add der Multiplikation), und iterative Algorithmen führen mindestens eine Multiplikation pro Iteration durch. In beiden Fällen ist es sehr wahrscheinlich, dass die Division mehr Zyklen benötigt. Natürlich berücksichtigt dies keine Macken in Compilern, Datenverschiebung oder Genauigkeit. Im großen und ganzen ist es jedoch sinnvoll, 0.5 * x oder 1.0/2.0 * x anstelle von x / 2.0 zu schreiben, wenn Sie eine innere Schleife in einem zeitempfindlichen Teil eines Programms codieren. Die Pedanterie von "Code, was am klarsten ist" ist absolut wahr, aber alle drei sind so gut lesbar, dass die Pedanterie in diesem Fall nur Pedanterie ist.

4
Gene

Die Gleitkommadivision ist (im Allgemeinen) besonders langsam. Während die Gleitkommamultiplikation auch relativ langsam ist, ist sie wahrscheinlich schneller als die Gleitkommadivision.

Aber ich bin eher geneigt zu antworten "es ist nicht wirklich wichtig", es sei denn, das Profiling hat gezeigt, dass die Division ein bisschen Engpass vs. Multiplikation ist. Ich vermute jedoch, dass die Wahl der Multiplikation vs. Division keinen großen Einfluss auf die Leistung Ihrer Anwendung hat.

3
mipadi

Dies wird mehr zu einer Frage, wenn Sie in Assembly oder vielleicht C programmieren. Ich denke, dass bei den meisten modernen Sprachen diese Optimierung für mich gemacht wird.

2
Seamus

Ich habe immer gelernt, dass die Multiplikation effizienter ist.

2
Toon Krijthe

Seien Sie vorsichtig "die Multiplikation zu erraten ist normalerweise besser, also versuche ich mich daran zu halten, wenn ich codiere"

Im Zusammenhang mit dieser spezifischen Frage bedeutet besser "schneller". Welches ist nicht sehr nützlich.

Über Geschwindigkeit nachzudenken kann ein schwerwiegender Fehler sein. In der spezifischen algebraischen Form der Berechnung gibt es tiefgreifende Fehlerauswirkungen. 

Siehe Gleitkomma-Arithmetik mit Fehleranalyse . Siehe Grundlegende Probleme der Gleitkomma-Arithmetik und Fehleranalyse

Während einige Gleitkommawerte genau sind, sind die meisten Gleitkommawerte eine Näherung. Sie sind ein idealer Wert und einige Fehler. Jede Operation gilt für den idealen Wert und den Fehlerwert.

Die größten Probleme ergeben sich aus dem Versuch, zwei nahezu gleiche Zahlen zu manipulieren. Die Bits ganz rechts (die Fehlerbits) dominieren die Ergebnisse.

>>> 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

In diesem Beispiel können Sie sehen, dass bei kleineren Werten die Differenz zwischen nahezu gleichen Zahlen Ergebnisse ungleich Null ergibt, wenn die richtige Antwort Null ist.

2
S.Lott

Es gibt einen Unterschied, aber es ist abhängig vom Compiler. Auf vs2003 (c ++) habe ich zunächst keinen signifikanten Unterschied für Doppeltypen (64-Bit-Gleitkommazahlen) festgestellt. Beim Ausführen der Tests auf vs2010 habe ich jedoch einen großen Unterschied festgestellt, bei Multiplikationen bis zu Faktor 4 schneller. Wenn man dies nachverfolgt, scheint es, dass vs2003 und vs2010 einen anderen fpu-Code erzeugen.

Auf einem Pentium 4, 2,8 GHz, vs2003:

  • Multiplikation: 8.09
  • Abteilung: 7,97

Auf einem Xeon W3530, vs2003:

  • Multiplikation: 4,68
  • Abteilung: 4,64

Auf einem Xeon W3530, vs2010:

  • Multiplikation: 5,33
  • Abteilung: 21.05

Es scheint, dass auf vs2003 eine Division in einer Schleife (also der Divisor wurde mehrfach verwendet wurde) in eine Multiplikation mit dem Inversen übersetzt wurde. Auf vs2010 wird diese Optimierung nicht mehr angewendet (Ich vermute, da die Ergebnisse zwischen den beiden Methoden leicht unterschiedlich sind). Beachten Sie auch, dass die CPU Divisionen schneller durchführt, sobald Ihr Zähler 0.0 ist. Ich weiß nicht, welchen Algorithmus der Chip fest verdrahtet hat, aber er hängt möglicherweise von der Anzahl ab.

Bearbeiten 18-03-2013: die Beobachtung für vs2010

1
gast128

Java Android, auf Samsung GT-S5830 profiliert

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;
    }
}

Ergebnisse?

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

Division ist ca. 20% schneller als Multiplikation (!)

1
PiotrK

Wie bei den Beiträgen Nr. 24 (Multiplikation ist schneller) und Nr. 30 - aber manchmal sind beide genauso einfach zu verstehen:

1*1e-6F;

1/1e6F;

~ Ich finde beide genauso gut lesbar und muss sie Milliarden Mal wiederholen. Daher ist es nützlich zu wissen, dass die Multiplikation normalerweise schneller ist.

1
Chris

Hier ist eine dumme lustige Antwort:

x/2.0 ist nicht äquivalent zu x * 0.5

Angenommen, Sie haben diese Methode am 22. Oktober 2008 geschrieben.

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

Jetzt, 10 Jahre später, lernen Sie, dass Sie diesen Code optimieren können. Die Methode wird in Ihrer Anwendung in Hunderten von Formeln referenziert. Sie ändern es also und erleben eine bemerkenswerte Leistungssteigerung von 5%.

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

War es die richtige Entscheidung, den Code zu ändern? In der Mathematik sind die beiden Ausdrücke tatsächlich gleichwertig. In der Informatik gilt das nicht immer. Bitte lesen Sie Minimierung der Auswirkung von Genauigkeitsproblemen für weitere Details. Wenn Ihre berechneten Werte zu einem bestimmten Zeitpunkt mit anderen Werten verglichen werden, ändern Sie das Ergebnis von Edge-Fällen. Z.B.:

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

Bottom line ist; Sobald Sie sich für eines der beiden entschieden haben, bleiben Sie dabei!

1
l33t

Ich habe irgendwo gelesen, dass die Multiplikation in C/C++ effizienter ist. Keine Ahnung von interpretierten Sprachen - der Unterschied ist wahrscheinlich aufgrund des anderen Overheads vernachlässigbar.

Es sei denn, es wird zu einem Problem mit dem, was besser zu pflegen/lesbar ist - ich hasse es, wenn die Leute mir das sagen, aber es ist so wahr.

Ich würde generell die Multiplikation vorschlagen, da Sie die Zyklen nicht darauf ausrichten müssen, dass Ihr Divisor nicht 0 ist.

1
Steve

Nach einer so langen und interessanten Diskussion hier ist meine Meinung dazu: Es gibt keine endgültige Antwort auf diese Frage. Wie einige Leute darauf hingewiesen haben, hängt es sowohl von der Hardware (cf piotrk und gast128 ) als auch vom Compiler (cf @Javier s Tests) ab. Wenn die Geschwindigkeit nicht kritisch ist, wenn Ihre Anwendung keine riesigen Datenmengen in Echtzeit verarbeiten muss, entscheiden Sie sich möglicherweise für eine Klarheit mit einer Division. Wenn die Verarbeitungsgeschwindigkeit oder die Prozessorauslastung ein Problem darstellen, ist die Multiplikation möglicherweise die sicherste. .Wenn Sie nicht genau wissen, auf welcher Plattform Ihre Anwendung implementiert wird, ist Benchmark bedeutungslos. Und für die Klarheit des Codes würde ein einzelner Kommentar die Arbeit erledigen! 

0
Jean-François

Wenn wir davon ausgehen, dass eine Additions-/Subtraktionsoperation 1 kostet, multiplizieren Sie die Kosten 5 und teilen Sie die Kosten auf 20.

0
matma