___ antwort4378849 ___
Nur ein Experiment bezüglich der Antworten zu "... es wird LEA
" verwendet:
Der folgende Code:
%Vor%
wird mit gcc -fomit-frame-pointer -O8 -m{32|64}
(für 32bit oder 64bit) in den folgenden Assembly-Code kompilieren:
- x86, 32bit:
int main(int argc, char **argv)
{
#ifdef USE_SHIFTOR
return (argc << 1 | 1);
#else
return (2 * argc + 1);
#endif
}
- x86, 64Bit:
080483a0 <main>:
80483a0: 8b 44 24 04 mov 0x4(%esp),%eax
80483a4: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
80483a8: c3 ret
- x86, 64Bit,
-DUSE_SHIFTOR
: 00000000004004c0 <main>:
4004c0: 8d 44 3f 01 lea 0x1(%rdi,%rdi,1),%eax
4004c4: c3 retq
- x86, 32bit,
-DUSE_SHIFTOR
: 080483a0 <main>:
80483a0: 8b 44 24 04 mov 0x4(%esp),%eax
80483a4: 01 c0 add %eax,%eax
80483a6: 83 c8 01 or 00000000004004c0 <main>:
4004c0: 8d 04 3f lea (%rdi,%rdi,1),%eax
4004c3: 83 c8 01 or %pre%x1,%eax
4004c6: c3 retq
x1,%eax
80483a9: c3 ret
Tatsächlich stimmt es, dass die meisten Fälle LEA
verwenden. Dennoch ist der Code für beide Fälle gleich nicht . Dafür gibt es zwei Gründe:
Der Zusatz - kann überlaufen und umlaufen, während Bitoperationen wie
<<
oder |
nicht möglich sind
-
(x + 1) == (x | 1)
ist nur wahr, wenn !(x & 1)
else die Addition auf das nächste Bit überträgt. Im Allgemeinen führt das Hinzufügen von nur dazu, dass in der Hälfte der Fälle das niedrigste Bit gesetzt wird.
Während wir (und der Compiler, wahrscheinlich) wissen, dass der zweite notwendigerweise anwendbar ist, ist der erste immer noch eine Möglichkeit. Der Compiler erzeugt daher einen anderen Code, da die "oder-Version" erfordert, dass Bit 0 auf 1 gesetzt wird.
___ qstntxt ___
Ich verstehe, dass die Antwort wahrscheinlich Hardware-spezifisch ist, aber ich bin neugierig, ob es eine allgemeinere Intuition gibt, die ich vermisse?
Ich habe diese Frage & amp; Angesichts der Antwort frage ich mich nun, ob ich meinen Ansatz im Allgemeinen ändern sollte, um "(i & lt; & lt; 1 | 1)" anstelle von "(2 * i + 1)" zu verwenden?
___ qstnhdr ___ In C ++, was ist schneller? (2 * i + 1) oder (i 1 | 1)?
___ antwort4373527 ___
Ausgabe von gcc mit der Option -S (keine Compilerflags angegeben):
%Vor%
Ich bin nicht sicher, welches ist was, aber ich glaube nicht, dass es wichtig ist.
Wenn der Compiler überhaupt keine Optimierungen vornimmt, würde die zweite wahrscheinlich zu schnelleren Assemblerbefehlen führen. Wie lange jede Anweisung dauert, hängt vollständig von der Architektur ab. Die meisten Compiler optimieren sie so, dass sie die gleichen Anweisungen auf Baugruppenebene haben.
___ answer12025811 ___
Ich habe das gerade mit gcc-4.7.1 unter Verwendung der Quelle von FrankH getestet, der generierte Code ist
%Vor%
egal, ob die Shift- oder die Multiplikationsversion verwendet wird.
___ antwort4376029 ___
%code% ist möglicherweise schneller als die anderen beiden,
weil Addition schneller ist als Multiplikation und schneller sein kann als shift.
___ answer4373837 ___
Niemand interessiert sich. Noch sollten sie.
Hör auf, dir darüber Sorgen zu machen und deinen Code korrekt, einfach und fertig zu machen.
___ tag123c ___ C ++ ist eine universelle Programmiersprache. Es wurde ursprünglich als Erweiterung von C entworfen und behält eine ähnliche Syntax, ist aber jetzt eine völlig andere Sprache. Verwenden Sie dieses Tag für Fragen zu Code, der mit einem C ++ - Compiler kompiliert werden soll.
___ tag123performance ___ Für Fragen zur Messung oder Verbesserung der Code- und Anwendungseffizienz.
___ tag123bitshift ___ Eine Bit-Shift-Operation verschiebt die in einer Binärzahl oder einem Bitmuster enthaltenen Bits nach links oder rechts.
___ answer4377505 ___
Je schneller die erste Form ist (diejenige mit der Rechtsverschiebung), tatsächlich dauert die Ausführung der shr-Anweisung im schlimmsten Fall 4 Taktzyklen, im besten Fall die Mul 10. Die beste Form sollte jedoch vom Compiler entschieden werden, da sie eine vollständige Sicht auf die anderen (Assembly-) Anweisungen hat.
___ answer4373506 ___
Da der ISO-Standard keine Leistungsanforderungen vorschreibt, hängt dies von der Implementierung, den ausgewählten Compiler-Flags, der Ziel-CPU und möglicherweise der Phase des Mondes ab.
Diese Art von Optimierungen (die ein paar Zyklen sparen) verblassen fast immer in der Geringfügigkeit in Bezug auf den Return on Investment, gegen Makro-Level-Optimierungen wie die Algorithmusauswahl.
Achten Sie in erster Linie auf die Lesbarkeit des Codes. Wenn Sie beabsichtigen, Bits und %code% zu verschieben, verwenden Sie die Bit-Shift-Version. Wenn Sie beabsichtigen, zu multiplizieren, verwenden Sie die %code% Version. Sorgen Sie sich erst um die Leistung, wenn Sie festgestellt haben, dass ein Problem vorliegt.
Jeder ordentliche Compiler wird es weit besser optimieren als Sie es können: -)
___ answer4373500 ___
Jeder außer dem hirntoten Compiler sieht diese Ausdrücke als äquivalent und kompiliert sie zu demselben ausführbaren Code.
In der Regel lohnt es sich nicht, sich allzu viele Gedanken über die Optimierung einfacher arithmetischer Ausdrücke wie dieser zu machen, da Compiler am besten optimiert werden können. (Im Gegensatz zu vielen anderen Fällen, in denen ein "intelligenter Compiler" das Richtige tun könnte, fällt jedoch ein tatsächlicher Compiler aus.)
Dies wird übrigens zu dem gleichen Paar von Anweisungen auf PPC, Sparc und MIPS führen: eine Verschiebung, gefolgt von einem Add. Auf dem ARM wird es auf einen einzelnen fusionierten Shift-Add-Befehl herunterkochen, und auf x86 wird es wahrscheinlich ein einzelnes %code% op.
sein
___ tag123assembly ___ Assemblersprache (asm) Programmierfragen. Achten Sie darauf, auch mit dem Prozessor und / oder Befehlssatz, die Sie verwenden, sowie den Assembler TAG. WARNUNG: Verwenden Sie für .NET-Assemblies stattdessen das Tag [.net-assembly]. Verwenden Sie für Java ASM stattdessen das Tag [java-bytecode-asm].
___