Welchen C ++ - Code kompiliert die X86 -Rep -Anweisung?
-
25-10-2019 - |
Frage
Ich kopiere Elemente von einem Array zum anderen in C ++. ich fand die rep movs
Anweisung in x86, die ein Array bei ESI zu einem Array bei EDI von Size ECX kopiert. Allerdings weder die for
Noch while
Schleifen, die ich versucht habe, zu einem zusammengestellt zu werden rep movs
Anweisung in VS 2008 (auf einem Intel Xeon X64 -Prozessor). Wie kann ich Code schreiben, der zu dieser Anweisung zusammengestellt wird?
Lösung
Wenn Sie genau diese Anweisung benötigen, verwenden Sie den integrierten Assembler und schreiben Sie diese Anweisung manuell. Sie können sich nicht auf den Compiler verlassen, um einen bestimmten Maschinencode zu erzeugen - Auch wenn es es in einer Zusammenstellung ausgibt, kann es sich entscheiden, während der nächsten Zusammenstellung ein anderes Äquivalent auszugeben.
Andere Tipps
Ehrlich gesagt sollten Sie nicht. Rep ist eine Art veraltetes Übertrag im Befehlssatz und eigentlich ziemlich langsam, da sie eine mikrokodierte Unterroutine innerhalb der CPU aufrufen muss, die eine ROM -Lokup -Latenz hat und ebenfalls nicht ausgerichtet ist.
In fast jeder Implementierung werden Sie feststellen, dass die memcpy()
Compiler Intrinsic ist beide einfacher zu bedienen und läuft schneller.
Unter MSVC gibt es die __movsxxx
& __stosxxx
Intrinsik, die a erzeugen werden REP
vorangestellte Anweisung.
Es gibt auch einen "Hack", um intrinsisch zu erzwingen memset
AKA REP STOS
Nach VC9+, da das Intrinsische aufgrund der SSE2 -Verzweigung in der CRT nicht mehr verlässt. Das ist besser als __stosxxx
Aufgrund der Tatsache kann der Compiler es für Konstanten optimieren und korrekt bestellen.
#define memset(mem,fill,size) memset((DWORD*)mem,((fill) << 24|(fill) << 16|(fill) << 8|(fill)),size)
__forceinline void memset(DWORD* pStart, unsigned long dwFill, size_t nSize)
{
//credits to Nepharius for finding this
DWORD* pLast = pStart + (nSize >> 2);
while(pStart < pLast)
*pStart++ = dwFill;
if((nSize &= 3) == 0)
return;
if(nSize == 3)
{
(((WORD*)pStart))[0] = WORD(dwFill);
(((BYTE*)pStart))[2] = BYTE(dwFill);
}
else if(nSize == 2)
(((WORD*)pStart))[0] = WORD(dwFill);
else
(((BYTE*)pStart))[0] = BYTE(dwFill);
}
Natürlich REP
Ist nicht immer das Beste, was man verwenden kann, wenn Sie sich besser verwenden, wenn Sie sich besser verwenden memcpy
, es wird entweder auf SSE2 oder auf SSE2 verzweigen REPS MOV
Basierend auf Ihrem System (unter MSVC), es sei denn, Sie möchten eine benutzerdefinierte Montage für "heiße" Bereiche schreiben ...
Repräsentant und Freunde waren einmal nett, als die X86-CPU ein industrieller KISC-Prozessor mit einer Pipeline war.
Aber das hat sich geändert. Heutzutage, wenn der Prozessor begegnet irgendein Anweisungen, das erste, das es tut, besteht Kann verwendet werden, um die Schreib- nach dem Schreiben von Sequenzen in Einzelgeschnitten zu vereinfachen, etc.). Diese Maschinerie eignet sich gut für Anweisungen, die sich in einigen VLIW-ähnlichen Opcodes übersetzt, aber nicht in Maschinencode, die in Schlaufen übersetzt werden. Schleifenüberletzter Maschinencode führt wahrscheinlich dazu, dass die Ausführungspipeline zum Stillstand kommt.
Anstatt Hunderttausende von Transistoren für den Bau von CPU-Zirkuitrie zum Umgang mit Schleifen von Teilen der Mikroopien in der Ausführungspipeline auszugeben Schreiben Sie Ihre eigenen verdammten Loops!
Daher wird es selten verwendet, wenn Maschinen Code schreiben. Wenn Sie in einer binären ausführbaren Datei auftreten, ist es wahrscheinlich eine menschliche Versammlung, die es nicht besser kannte, oder ein Cracker, der die wenigen Bytes, die es für die Verwendung von es anstelle einer tatsächlichen Schleife, wirklich brauchte, die sie geschrieben hat.
(Nehmen Sie jedoch alles, was ich gerade mit einem Körnchen Salz geschrieben habe. Vielleicht stimmt das nicht mehr. Ich bin nicht mehr 100% auf dem neuesten
Ich verwende die Rep* Prefix -Varianten mit CMPS*, MOVS*, SCAs* und STOS* Anweisungsvarianten, um Inline -Code zu generieren, der die Codegröße minimiert, unnötige Anrufe/Sprünge vermeidet und dadurch die von den Caches erledigten Arbeiten unterhält. Die Alternative besteht darin, Parameter einzurichten und ein Memset oder ein Memcpy an einem anderen Ort aufzurufen, was insgesamt schneller sein kann, wenn ich hundert Bytes oder mehr kopieren möchte, aber wenn es nur eine Frage von 10 bis 20 Bytes mit Rep ist, ist es schneller (oder zumindest Das letzte Mal habe ich gemessen).
Da mein Compiler die Spezifikation und Verwendung von Inline -Montagefunktionen ermöglicht und deren Registerverbrauch/-änderung in den Optimierungsaktivitäten enthält, kann ich sie verwenden, wenn die Umstände stimmen.
In historischer Hinweis - keinen Einblick in die Strategien des Herstellers - gab es eine Zeit, in der die Anweisungen "Rep Movs*" (etc) sehr langsam waren. Ich denke, es war ungefähr die Zeit des Pentium/Pentium -MMX. Ein Kollege von mir (der mehr Einblicke hatte als ich) sagte, dass die Hersteller den Chipbereich verringert hatten (<=> weniger Transistoren/mehr Mikrocode), die der Rep -Handhabung zugewiesen wurden, und es benutzte, um andere, verwendete Anweisungen schneller zu machen.
In den fünfzehn Jahren, da Rep wieder relativ schneller geworden ist, würde dies mehr Transistoren/weniger Mikrocode vermuten lassen.