TL; DR Zusammenfassung: xor same, same
ist die beste Wahl für alle CPUs. Keine andere Methode hat einen Vorteil gegenüber dieser Methode, und sie hat zumindest einen Vorteil gegenüber jeder anderen Methode. Es wird offiziell von Intel und AMD empfohlen. Verwenden Sie im 64-Bit-Modus weiterhin xor r32, r32
, da writing a 32-bit reg zeros the upper 32. xor r64, r64
ist eine Verschwendung von einem Byte, weil es ein REX-Präfix benötigt.
Das Nullsetzen eines Vektorregisters erfolgt normalerweise am besten mit pxor xmm, xmm
. Das ist typisch, was gcc macht (sogar vor Verwendung mit FP-Anweisungen).
xorps xmm, xmm
kann sinnvoll sein. Es ist ein Byte kürzer als pxor
, aber xorps
benötigt Ausführungsport 5 auf Intel Nehalem, während pxor
auf jedem Port (0/1/5) ausgeführt werden kann. (Nehalems 2c-Bypass-Verzögerungslatenz zwischen Integer und FP ist normalerweise nicht relevant, da die Out-of-Order-Ausführung diese typischerweise am Anfang einer neuen Abhängigkeitskette verbergen kann).
Auf SnB-Familien-Mikroarchitekturen benötigt keiner der beiden Funktionen von xor-zeroing einen Ausführungsport. Auf AMD und vor Nehalem P6/Core2 Intel werden xorps
und pxor
auf die gleiche Weise behandelt (als Vektor-Integer-Anweisungen).
Die Verwendung der AVX-Version eines 128B-Vektorbefehls setzt den oberen Teil des Reg ebenfalls auf Null, so dass vpxor xmm, xmm, xmm
eine gute Wahl zum Nullsetzen von YMM (AVX1/AVX2) oder ZMM (AVX512) oder einer zukünftigen Vektorerweiterung ist. vpxor ymm, ymm, ymm
nimmt jedoch keine zusätzlichen Bytes zum Codieren und führt dasselbe aus. Das Nullsetzen des AVX512 ZMM würde zusätzliche Bytes erfordern (für das EVEX-Präfix), daher sollte XMM oder YMM-Nullsetzung bevorzugt werden.
Einige CPUs erkennen sub same,same
als Nullstellung Idiom wie xor
, aber alle CPUs, die alle Nullstellen Idiome xor
erkennen erkennen. Verwenden Sie einfach xor
, damit Sie sich keine Gedanken darüber machen müssen, welche CPU welches Nullsetzungs-Idiom erkennt.
xor
(eine anerkannte Nullstellung Idiom, im Gegensatz zu mov reg, 0
ist) einige offensichtliche und einige subtile Vorteile hat (Zusammenfassung Liste, dann werde ich auf jene erweitern):
- kleinere Codegröße als
mov reg,0
. (Alle CPUs)
- vermeidet partielle Registerstrafen für späteren Code. (Intel P6-Familie und SnB-Familie).
- verwendet keine Ausführungseinheit, spart Strom und gibt Ausführungsressourcen frei. (Intel SnB-Familie)
- kleinerer UOP (keine unmittelbaren Daten) lässt Raum in der UOP-Cachelinie für nahe gelegene Anweisungen, um bei Bedarf zu leihen. (Intel SnB-Familie).
- doesn't use up entries in the physical register file. (Intel-SnB-Familie (und P4) zumindest, möglicherweise auch AMD, da sie ein ähnliches PRF-Design verwenden, anstatt den Registerzustand in den ROB-ähnlichen Mikroarchitekturen der Intel P6-Familie beizubehalten.)
Kleinere Maschinencode Größe (2 Bytes statt 5) ist immer ein Vorteil: höhere Codedichte führt zu weniger Befehl Cache-Misses und besserer Befehlsabruf und möglicherweise Bandbreite dekodieren.
Der Vorteil der nicht eine Ausführungseinheit für xor auf Intel SnB-Familie mit Mikroarchitekturen ist gering, aber spart Strom. Es ist wahrscheinlicher, dass es auf SnB oder IvB ankommt, die nur 3 ALU-Ausführungsports haben. Haswell und später haben 4 Ausführungsports, die Integer-ALU-Anweisungen verarbeiten können, einschließlich mov r32, imm32
, so dass HSW mit perfekten Entscheidungsfindungen durch den Scheduler (was in der Praxis nicht vorkommt) auch 4 Ups pro Takt aufrechterhalten kann, selbst wenn sie alle ausgeführt werden müssen Häfen.
Weitere Details finden Sie unter my answer on another question about zeroing registers.
Bruce Dawson's blog post, die Michael Petch (in einem Kommentar zu der Frage) verknüpft weist darauf hin, dass xor
in dem Register-umbenennen Stufe behandelt wird, um eine Ausführungseinheit (Null Uops in der nicht fusionierten Domäne), ohne dass aber die Tatsache übersehen, dass es nach wie vor ist ein up in der fusionierten Domäne. Moderne Intel-CPUs können & ausgeben 4 Fused-Domain-Ups pro Takt abstellen. Von dort kommt die 4 Nullen pro Taktgrenze. Die erhöhte Komplexität der Registerumbenennungshardware ist nur einer der Gründe, die Breite des Designs auf 4 zu begrenzen. (Bruce hat einige sehr gute Blogposts geschrieben, wie seine Serie auf FP math and x87/SSE/rounding issues, die ich sehr empfehle).
auf AMD Bulldozer-Familie CPUs, mov immediate
läuft auf demselben EX0/EX1 Integer-Ausführungs Ports als xor
. mov reg,reg
kann auch auf AGU0/1 laufen, aber das ist nur für das Kopieren von Registern, nicht für die Einstellung von Sofortnachrichten. So AFAIK, auf AMD der einzige Vorteil zu xor
über mov
ist die kürzere Codierung. Es könnte auch physische Registerressourcen sparen, aber ich habe keine Tests gesehen.
Anerkannte Nullung Idiome Teil registrieren vermeiden Strafen auf Intel-CPUs, die von der vollständigen Register Teilregister getrennt umbenennen (P6 & SnB Familien).
xor
wird tag das Register als die oberen Teile genullt ist, so xor eax, eax
/inc al
/inc eax
vermeidet die üblichen Teilregister Strafe, die pre-IVB CPUs haben. Sogar ohne xor
, IvB braucht nur eine Zusammenführung von up, wenn die hohen 8bits (AH
) geändert werden und dann das ganze Register gelesen wird, und Haswell entfernt sogar das.
Von Agner Fog microarch Führung, S. 98 (Pentium M Abschnitt, durch den späteren Abschnitten einschließlich SnB verwiesen): auf Null
Der Prozessor erkennt die XOR eines Registers mit sich selbst als Einstellung. Eine spezielle Variable im Register erinnert daran, dass der obere Teil des Registers Null ist, so dass EAX = AL.Dieser Tag wird auch in einer Schleife erinnerte:
; Example 7.9. Partial register problem avoided in loop
xor eax, eax
mov ecx, 100
LL:
mov al, [esi]
mov [edi], eax ; No extra uop
inc esi
add edi, 4
dec ecx
jnz LL
(von pg82): Der Prozessor erinnert sich, dass die oberen 24 Bits von EAX sind Null, solange Sie keine Unterbrechung, Fehlvorhersage erhalten, oder andere Serialisierungsereignis.
pg82 diese Führung bestätigt auch, dass mov reg, 0
nicht als Nullstellung Idiom, zumindest auf frühes P6 Design wie PIII oder PM anerkannt ist. Ich wäre sehr überrascht, wenn sie Transistoren bei späteren CPUs einsetzen würden.
xor
Sets Fahnen, was bedeutet, Sie müssen vorsichtig sein, wenn die Bedingungen zu testen. Da setcc
leider nur mit einem 8bit Ziel verfügbar ist, müssen Sie in der Regel darauf achten, Teilstrafen zu vermeiden.
Es wäre schön gewesen, wenn x86-64 einen der entfernten Opcodes (wie AAM) für ein 16/32/64 Bit setcc r/m
mit dem im 3-Bit-Quellregister des r/m-Feld (die Art, wie einige andere Operanden-Anweisungen sie als Opcode-Bits verwenden). Aber das haben sie nicht gemacht, und das würde sowieso nicht für x86-32 helfen.
Idealerweise sollten Sie xor
/set Fahnen verwenden/setcc
/gelesenes volles Register:
...
call some_func
xor ecx,ecx ; zero *before* the test
test eax,eax
setnz cl ; cl = (some_func() != 0)
add ebx, ecx ; no partial-register penalty here
Dies hat eine optimale Leistung auf allen CPUs (keine Stände, verschmelzenden Uops oder falsche Abhängigkeiten).
Die Dinge sind komplizierter, wenn Sie nicht vor einer Flag-Einstellung Anweisung xor wollen. z.B. Sie möchten auf eine Bedingung verzweigen und dann von denselben Flags aus auf eine andere Bedingung setzen. z.B. cmp/jle
, sete
, und Sie haben entweder kein Ersatzregister, oder Sie möchten den xor
aus dem Pfad des nicht genommenen Codes zusammenhalten.
Es gibt keine erkannten Nullsetzungs-Idiome, die keine Flags beeinflussen, daher hängt die beste Wahl von der Zielmikroarchitektur ab. Bei Core2 kann das Einfügen eines zusammenführenden Ups zu einem 2- oder 3-stufigen Stillstand führen. Es scheint auf SnB billiger zu sein, aber ich habe nicht viel Zeit damit verbracht zu messen. Die Verwendung von mov reg, 0
/setcc
würde auf älteren Intel-CPUs eine erhebliche Strafe bedeuten und bei neueren Intel noch etwas schlechter sein.
setcc
/movzx r32, r8
ist wahrscheinlich die beste Alternative für Intel P6 & SnB-Familien, wenn Sie nicht vor der Flag-Einstellung Anweisung Xor-Null sein können. Das sollte besser sein, als den Test nach einem xor-Nullsetzen zu wiederholen. (Denken Sie nicht einmal an sahf
/lahf
oder pushf
/popf
). IvB kann movzx r32, r8
eliminieren (d. H. Es kann mit Registerumbenennung ohne Ausführungseinheit oder Latenz, wie xor-Nullsetzung, umgehen). Haswell und später nur regelmäßige mov
Anweisungen beseitigen, so movzx
nimmt eine Ausführungseinheit und hat nicht Null Latenz, Test machen/setcc
/movzx
schlechter als xor
/test/setcc
, aber immer noch mindestens so gut wie Test/mov r,0
/setcc
(und viel besser auf älteren CPUs).
Die Verwendung von setcc
/movzx
ohne Nullsetzung ist bei AMD/P4/Silvermont schlecht, weil sie die Depos nicht separat nach Unterregistern verfolgen. Der alte Wert des Registers wäre falsch. Die Verwendung mov reg, 0
/setcc
zum Nullsetzen/Abhängigkeit brechen ist wahrscheinlich die beste Alternative, wenn xor
/test/setcc
keine Option ist.
Natürlich, wenn Sie nicht brauchen, setcc
Ausgabe ist breiter als 8 Bits, müssen Sie nichts auf Null setzen. Achten Sie jedoch auf falsche Abhängigkeiten von anderen CPUs als P6/SnB, wenn Sie ein Register auswählen, das kürzlich Teil einer langen Abhängigkeitskette war. (Und Vorsicht verursachen eine teilweise reg Stall oder zusätzliche UOP, wenn Sie eine Funktion aufrufen, die speichern kann/Wiederherstellen der registrieren Sie verwenden Teil.)
and
mit einer sofortigen Null nicht speziell ist -kassiert als unabhängig von dem alten Wert auf irgendwelchen CPUs, die ich kenne, also bricht es nicht Abhängigkeitsketten. Es hat keine Vorteile gegenüber xor
, und viele Nachteile.
Siehe http://agner.org/optimize/ für microarch Dokumentation, einschließlich der Nullstellung Idiome erkannt werden als Abhängigkeit zu brechen (zB sub same,same
ist auf einige, aber nicht alle CPUs, während xor same,same
auf all erkannt wird.) mov
funktioniert die Abhängigkeitskette auf dem alten Wert des Bruch register (unabhängig vom Quellwert, null oder nicht, denn so funktioniert mov
). xor
bricht nur die Abhängigkeitsketten in dem speziellen Fall, in dem src und dest das gleiche Register sind, weshalb mov
aus der Liste der speziell erkannten Abhängigkeitsbrecher weggelassen wird. (Auch, weil es nicht als Nullstellung Idiom erkannt wird, mit den anderen Vorteilen, die tragen.)
Interessanterweise ist das älteste P6-Design (PPro) tat nicht erkennen xor
-zeroing als Abhängigkeit Brecher, nur als ein Nullungs-Idiom zum Zweck der Vermeidung von Partial-Register-Ständen, so dass es sich in einigen Fällen beide lohnt. (Siehe Agner Fogs Beispiel 6.17 in seinem microarch pdf. Er behauptet, dies gilt auch für P2, P3 und sogar (früh?) PM, aber ich bin skeptisch. A comment on the linked blog post sagt, es war nur PPro, die diese Aufsicht hatte wirklich unwahrscheinlich scheint, dass mehrere Generationen der Familie P6 existierten xor-Nullstellung als dep Brecher, ohne zu erkennen.)
Wenn es Ihren Code schöner oder speichert Anweisungen wirklich macht, dann sicher, Null mit mov
zu vermeiden, die zu berühren Flags, solange Sie kein anderes Leistungsproblem als die Code-Größe einführen. Das Vermeiden von Clobber-Flags ist jedoch der einzige vernünftige Grund dafür, xor
nicht zu verwenden.
Vielleicht möchten Sie dies lesen [Artikel] (https://randomascii.wordpress.com/2012/12/29/the-surpriness-subletties-of-zeroing-a-register/) –
xor vs mov: http : //stackoverflow.com/questions/1135679/does-using-xor-reg-reg-give-advantage-over-mov-reg-0 –