Erweitertes REP MOVSB ​​für memcpy

71

Ich möchte erweitertes REP MOVSB ​​(ERMSB) verwenden, um eine hohe Bandbreite für eine benutzerdefinierte zu erhalten memcpy.

ERMSB wurde mit der Ivy Bridge-Mikroarchitektur eingeführt. Weitere Informationen finden Sie im Abschnitt "Erweiterter REP MOVSB- und STOSB-Betrieb (ERMSB)" im Intel-Optimierungshandbuch, wenn Sie nicht wissen, was ERMSB ist.

Ich weiß nur, dass ich dies direkt mit der Inline-Montage tun kann. Ich habe die folgende Funktion von https://groups.google.com/forum/#!topic/gnu.gcc.help/-Bmlm_EG_fE erhalten

Wenn ich dies jedoch benutze, ist die Bandbreite viel geringer als bei memcpy. Erhält __movsb15 GB / s und memcpy26 GB / s mit meinem i7-6700HQ (Skylake) -System, Ubuntu 16.10, DDR4 bei 2400 MHz Dual Channel 32 GB, GCC 6.2.

Warum ist die Bandbreite bei so viel geringer REP MOVSB? Was kann ich tun, um es zu verbessern?

Hier ist der Code, mit dem ich das getestet habe.


Der Grund, an dem ich interessiert bin, rep movsbbasiert auf diesen Kommentaren

Beachten Sie, dass Sie auf Ivybridge und Haswell mit zu großen Puffern, die in MLC passen, movntdqa mit rep movsb schlagen können. movntdqa verursacht eine RFO in LLC, rep movsb nicht ... rep movsb ist beim Streaming in den Speicher auf Ivybridge und Haswell erheblich schneller als movntdqa (aber beachten Sie, dass es vor Ivybridge langsam ist!)

Was fehlt / ist nicht optimal in dieser memcpy-Implementierung?


Hier sind meine Ergebnisse auf dem gleichen System von Tinymembnech .

Beachten Sie, dass auf meinem System SSE2 copy prefetchedauch schneller ist als MOVSB copy.


In meinen ursprünglichen Tests habe ich den Turbo nicht deaktiviert. Ich habe den Turbo deaktiviert und erneut getestet und es scheint keinen großen Unterschied zu machen. Das Ändern der Energieverwaltung macht jedoch einen großen Unterschied.

Wenn ich es tue

Ich sehe manchmal über 20 GB / s mit rep movsb.

mit

Das Beste, was ich sehe, sind ungefähr 17 GB / s. Scheint memcpyaber nicht empfindlich auf das Power Management zu reagieren.


Ich habe die Frequenz (mit turbostat) mit und ohne aktiviertem SpeedStep überprüft , mit performanceund mit powersaveLeerlauf, einer 1-Kern-Last und einer 4-Kern-Last. Ich habe Intels MKL-Dichtematrix-Multiplikation ausgeführt, um eine Last zu erstellen und die Anzahl der verwendeten Threads festzulegen OMP_SET_NUM_THREADS. Hier ist eine Tabelle der Ergebnisse (Zahlen in GHz).

Dies zeigt, dass powersavedie CPU auch bei deaktiviertem SpeedStep immer noch auf die Leerlauffrequenz von taktet 0.8 GHz. Nur mit performanceohne SpeedStep läuft die CPU mit einer konstanten Frequenz.

Ich habe zB sudo cpufreq-set -r performance(weil cpufreq-setes seltsame Ergebnisse lieferte) verwendet, um die Leistungseinstellungen zu ändern. Dies schaltet den Turbo wieder ein, so dass ich den Turbo danach deaktivieren musste.

Z Boson
quelle
7
@KerrekSB Weißt du, was der erweiterte Repräsentant movsb ist?
Fuz
3
@ KerrekSB, ja, es ist in Abschnitt "3.7.6 Verbesserte REP MOVSB- und STOSB-Operation (ERMSB)
Z Boson
3
Das Optimierungshandbuch schlägt vor, dass ERMSB eine kleine Codegröße und einen kleinen Durchsatz besser bereitstellen kann als herkömmliches REP-MOV / STO. Die Implementierung von memcpy mit ERMSB erreicht jedoch möglicherweise nicht den gleichen Durchsatz wie die Verwendung von 256-Bit- oder 128-Bit-AVX-Alternativen. abhängig von Länge und Ausrichtungsfaktoren. " Ich verstehe dies so, dass es für Situationen verbessert wurde, in denen Sie möglicherweise bereits Wiederholungsanweisungen verwendet haben, aber nicht darauf abzielen, mit modernen Alternativen mit hohem Durchsatz zu konkurrieren.
Kerrek SB
5
@Zboson Mein Glibc memcpy()verwendet AVX NT-Stores. Und sowohl NT-Stores als auch ERMSB verhalten sich schreibkombinierend und sollten daher keine RFOs erfordern. Meine Benchmarks auf meinem eigenen Computer zeigen jedoch, dass mein memcpy()ERMSB und mein ERMSB beide 2/3 der Gesamtbandbreite erreichen, wie es Ihr memcpy()(aber nicht Ihr ERMSB) getan hat. Daher gibt es eindeutig irgendwo eine zusätzliche Bustransaktion, und es stinkt sehr wie ein RFO.
Iwillnotexist Idonotexist
3
Es ist auch interessant festzustellen, dass eine schnelle String-Leistung beispielsweise bei Linux-Kernel-Methoden wie read()und beim write()Kopieren von Daten in den Benutzerbereich tatsächlich sehr relevant ist: Der Kernel kann (kann) keine SIMD-Register oder SIMD-Code verwenden. Für ein schnelles Memcpy muss es entweder 64-Bit-Lade- / Speicher verwenden, oder in jüngerer Zeit wird es verwendet rep movsboder rep rmovdwenn festgestellt wird, dass sie in der Architektur schnell sind. So profitieren sie von großen Zügen, ohne dass sie explizit verwendet xmmoder ymmregistriert werden müssen.
BeeOnRope

Antworten:

97

Dies ist ein Thema, das mir sehr am Herzen liegt und das ich kürzlich untersucht habe. Ich werde es daher aus verschiedenen Blickwinkeln betrachten: Geschichte, einige technische Hinweise (meistens akademisch), Testergebnisse auf meiner Box und schließlich der Versuch, Ihre eigentliche Frage zu beantworten wann und wo rep movsbkönnte Sinn machen.

Teilweise ist dies ein Aufruf zum Teilen von Ergebnissen. Wenn Sie Tinymembench ausführen und die Ergebnisse zusammen mit Details Ihrer CPU- und RAM-Konfiguration teilen können, wäre dies großartig. Besonders wenn Sie ein 4-Kanal-Setup, eine Ivy Bridge-Box, eine Server-Box usw. haben.

Geschichte und offizielle Beratung

Die Leistungshistorie der Anweisungen zum schnellen Kopieren von Zeichenfolgen war eine Art Treppenstufen-Angelegenheit - dh Perioden stagnierender Leistung, die sich mit großen Upgrades abwechselten, die sie in Einklang brachten oder sogar schneller als konkurrierende Ansätze. Zum Beispiel gab es einen Leistungssprung in Nehalem (hauptsächlich für Startkosten) und erneut für Ivy Bridge (die meisten zielen auf den Gesamtdurchsatz für große Kopien ab). In diesem Thread finden Sie jahrzehntealte Einblicke in die Schwierigkeiten bei der Implementierung der rep movsAnweisungen eines Intel-Ingenieurs .

Um zum Beispiel die Einführung von Ivy Bridge, die typisch in Führungen vorhergehenden Rat ist , sie zu vermeiden oder sie benutzen sehr sorgfältig 1 .

Die aktuelle (na ja, Juni 2016) Führung hat eine Vielzahl von verwirrenden und etwas inkonsistent Ratschlägen, wie 2 :

Die spezifische Variante der Implementierung wird zur Ausführungszeit basierend auf Datenlayout, Ausrichtung und dem ECX-Wert (Counter) ausgewählt. Beispielsweise sollte MOVSB ​​/ STOSB mit dem REP-Präfix für eine optimale Leistung mit einem Zählerwert kleiner oder gleich drei verwendet werden.

Also für Kopien von 3 oder weniger Bytes? Sie benötigen dafür zunächst kein repPräfix, da Sie mit einer behaupteten Startlatenz von ~ 9 Zyklen mit einem einfachen DWORD oder QWORD movmit ein wenig Bit-Twiddling besser dran sind, um die nicht verwendeten Bytes zu maskieren ( oder vielleicht mit 2 expliziten Bytes, Wort movs, wenn Sie wissen, dass die Größe genau drei ist).

Sie sagen weiter:

String MOVE / STORE-Anweisungen weisen mehrere Datengranularitäten auf. Für eine effiziente Datenverschiebung sind größere Datengranularitäten vorzuziehen. Dies bedeutet, dass eine bessere Effizienz erreicht werden kann, indem ein beliebiger Zählerwert in eine Anzahl von Doppelwörtern plus Einzelbytebewegungen mit einem Zählwert kleiner oder gleich 3 zerlegt wird.

Dies scheint auf der aktuellen Hardware mit ERMSB sicherlich falsch zu sein, wo rep movsbes mindestens genauso schnell oder schneller ist als die movdoder movqVarianten für große Kopien.

Im Allgemeinen enthält dieser Abschnitt (3.7.5) des aktuellen Leitfadens eine Mischung aus vernünftigen und stark veralteten Ratschlägen. Dies ist bei den Intel-Handbüchern üblich, da sie für jede Architektur schrittweise aktualisiert werden (und angeblich auch im aktuellen Handbuch Architekturen im Wert von fast zwei Jahrzehnten abdecken) und alte Abschnitte häufig nicht aktualisiert werden, um sie zu ersetzen oder bedingte Hinweise zu geben das gilt nicht für die aktuelle Architektur.

Anschließend wird ERMSB in Abschnitt 3.7.6 explizit behandelt.

Ich werde den verbleibenden Rat nicht erschöpfend durchgehen, aber ich werde die guten Teile im Abschnitt "Warum verwenden?" Unten zusammenfassen.

Weitere wichtige Aussagen aus dem Handbuch sind, dass Haswell rep movsbfür die interne Verwendung von 256-Bit-Operationen erweitert wurde.

Technische Überlegungen

Dies ist nur eine kurze Zusammenfassung der zugrunde liegenden Vor- und Nachteile, die die repAnweisungen vom Standpunkt der Implementierung aus haben .

Vorteile für rep movs

  1. Wenn ein repmovs-Befehl ausgegeben wird, weiß die CPU , dass ein ganzer Block bekannter Größe übertragen werden soll. Dies kann dazu beitragen, den Betrieb so zu optimieren, dass er mit diskreten Anweisungen nicht möglich ist, zum Beispiel:

    • Das Vermeiden der RFO-Anforderung, wenn bekannt ist, dass die gesamte Cache-Zeile überschrieben wird.
    • Sofortige und genaue Ausgabe von Prefetch-Anforderungen. Das Hardware-Prefetching macht einen guten Job beim Erkennen von memcpyähnlichen Mustern, aber es dauert immer noch ein paar Lesevorgänge, um zu starten, und es werden viele Cache-Zeilen über das Ende des kopierten Bereichs hinaus "vorabgerufen". rep movsbkennt genau die Regionsgröße und kann genau vorabrufen.
  2. Anscheinend gibt es keine Garantie für die Bestellung zwischen den Filialen innerhalb von 3 innerhalb einer einzigen, rep movswas dazu beitragen kann, den Kohärenzverkehr und einfach andere Aspekte der Blockverschiebung zu vereinfachen, im Gegensatz zu einfachen movAnweisungen, die einer ziemlich strengen Speicherreihenfolge folgen müssen 4 .

  3. Im Prinzip rep movskönnte die Anweisung verschiedene Architekturtricks nutzen, die in der ISA nicht verfügbar sind. Beispielsweise können Architekturen breitere interne Datenpfade haben, die der ISA 5 verfügbar macht, und rep movsdiese intern verwenden.

Nachteile

  1. rep movsbmuss eine bestimmte Semantik implementieren, die möglicherweise stärker ist als die zugrunde liegende Softwareanforderung. Verbietet insbesondere memcpyüberlappende Regionen und kann diese Möglichkeit ignorieren, rep movsberlaubt sie jedoch und muss das erwartete Ergebnis liefern. Bei aktuellen Implementierungen wirkt sich dies hauptsächlich auf den Startaufwand aus, wahrscheinlich jedoch nicht auf den Durchsatz bei großen Blöcken. Ebenso rep movsbmüssen byte-granulare Kopien unterstützt werden, selbst wenn Sie sie tatsächlich zum Kopieren großer Blöcke verwenden, die ein Vielfaches einer großen Zweierpotenz sind.

  2. Die Software verfügt möglicherweise über Informationen zu Ausrichtung, Kopiergröße und möglichem Aliasing, die bei Verwendung nicht an die Hardware übertragen werden können rep movsb. Compiler können häufig die Ausrichtung der Speicherblöcke 6 bestimmen und so einen Großteil der Startarbeiten vermeiden, rep movsdie bei jedem Aufruf erforderlich sind .

Testergebnisse

Hier sind die Testergebnisse für viele verschiedene tinymembenchKopiermethoden auf meinem i7-6700HQ bei 2,6 GHz (schade, dass ich die identische CPU habe, sodass wir keinen neuen Datenpunkt erhalten ...):

Einige wichtige Imbissbuden:

  • Die rep movsMethoden sind schneller als alle anderen Methoden, die nicht "nicht zeitlich" sind 7 , und erheblich schneller als die "C" -Ansätze, bei denen jeweils 8 Bytes kopiert werden.
  • Die "nicht-zeitlichen" Methoden sind um bis zu 26% schneller als die anderen rep movs- aber das ist ein viel kleineres Delta als das von Ihnen angegebene (26 GB / s gegenüber 15 GB / s = ~ 73%).
  • Wenn Sie keine nicht-temporären Speicher verwenden, ist die Verwendung von 8-Byte-Kopien von C genauso gut wie das SSE-Laden / Speichern mit 128 Bit Breite. Dies liegt daran, dass eine gute Kopierschleife genügend Speicherdruck erzeugen kann, um die Bandbreite zu sättigen (z. B. 2,6 GHz * 1 Speicher / Zyklus * 8 Byte = 26 GB / s für Speicher).
  • In tinymembench gibt es keine expliziten 256-Bit-Algorithmen (außer wahrscheinlich dem "Standard" memcpy), aber aufgrund des obigen Hinweises spielt dies wahrscheinlich keine Rolle.
  • Der erhöhte Durchsatz der nicht-zeitlichen Speicheransätze gegenüber den zeitlichen Ansätzen beträgt ungefähr das 1,45-fache, was sehr nahe an dem 1,5-fachen liegt, das Sie erwarten würden, wenn NT 1 von 3 Übertragungen eliminiert (dh 1 Lesen, 1 Schreiben für NT gegen 2 liest, 1 schreibt). Die rep movsAnsätze liegen in der Mitte.
  • Die Kombination aus relativ geringer Speicherlatenz und bescheidener 2-Kanal-Bandbreite bedeutet, dass dieser spezielle Chip seine Speicherbandbreite von einem einzelnen Thread aus sättigen kann, was das Verhalten dramatisch ändert.
  • rep movsdscheint die gleiche Magie wie rep movsbauf diesem Chip zu verwenden. Das ist interessant, weil ERMSB nur explizit movsbauf frühere Bögen abzielt und frühere Tests mit ERMSB zeigen, dass die movsbLeistung viel schneller ist als movsd. Dies ist meistens akademisch, da movsbes allgemeiner ist als movsdsowieso.

Haswell

Wenn wir uns die Haswell-Ergebnisse ansehen, die freundlicherweise von iwillnotexist in den Kommentaren zur Verfügung gestellt wurden, sehen wir dieselben allgemeinen Trends (die relevantesten extrahierten Ergebnisse):

Der rep movsbAnsatz ist immer noch langsamer als der nicht-zeitliche memcpy, hier jedoch nur um etwa 14% (im Vergleich zu ~ 26% im Skylake-Test). Der Vorteil der NT-Techniken gegenüber ihren zeitlichen Verwandten liegt jetzt bei ~ 57%, sogar etwas mehr als der theoretische Vorteil der Bandbreitenreduzierung.

Wann sollten Sie verwenden rep movs?

Zum Schluss noch ein Stich zu Ihrer eigentlichen Frage: Wann oder warum sollten Sie sie verwenden? Es stützt sich auf das oben Gesagte und führt einige neue Ideen ein. Leider gibt es keine einfache Antwort: Sie müssen verschiedene Faktoren abwägen, darunter einige, die Sie wahrscheinlich nicht einmal genau kennen, wie beispielsweise zukünftige Entwicklungen.

Ein Hinweis, dass die Alternative rep movsbzu der optimierten libc memcpy(einschließlich der vom Compiler eingefügten Kopien) oder einer handgerollten memcpyVersion sein kann. Einige der folgenden Vorteile gelten nur im Vergleich zu der einen oder anderen dieser Alternativen (z. B. "Einfachheit" hilft gegen eine handgerollte Version, aber nicht gegen integrierte memcpy), andere gelten für beide.

Einschränkungen der verfügbaren Anweisungen

In einigen Umgebungen sind bestimmte Anweisungen oder die Verwendung bestimmter Register eingeschränkt. Beispielsweise ist im Linux-Kernel die Verwendung von SSE / AVX- oder FP-Registern im Allgemeinen nicht zulässig. Daher können die meisten optimierten memcpyVarianten nicht verwendet werden, da sie auf SSE- oder AVX-Registern movbasieren und auf x86 eine einfache 64-Bit- basierte Kopie verwendet wird. Für diese Plattformen rep movsbermöglicht die Verwendung den größten Teil der Leistung eines optimierten, memcpyohne die Einschränkung des SIMD-Codes zu brechen.

Ein allgemeineres Beispiel könnte Code sein, der auf viele Hardwaregenerationen abzielen muss und der kein hardwarespezifisches Dispatching verwendet (z cpuid. B. using ). Hier könnten Sie gezwungen sein, nur ältere Befehlssätze zu verwenden, was jegliches AVX usw. ausschließt. Dies rep movsbkönnte hier ein guter Ansatz sein, da es den "versteckten" Zugriff auf breitere Lasten und Speicher ermöglicht, ohne neue Befehle zu verwenden. Wenn Sie auf Hardware vor ERMSB abzielen, müssen Sie jedoch prüfen, ob die rep movsbLeistung dort akzeptabel ist ...

Zukunftssicherheit

Ein schöner Aspekt davon rep movsbist, dass es theoretisch die architektonische Verbesserung zukünftiger Architekturen ohne Quellenänderungen nutzen kann, die explizite Bewegungen nicht können. Als beispielsweise 256-Bit-Datenpfade eingeführt wurden, rep movsbkonnten diese (wie von Intel behauptet) genutzt werden, ohne dass Änderungen an der Software erforderlich waren. Software mit 128-Bit-Verschiebungen (die vor Haswell optimal war) musste geändert und neu kompiliert werden.

Dies ist sowohl ein Vorteil für die Softwarewartung (keine Änderung der Quelle erforderlich) als auch ein Vorteil für vorhandene Binärdateien (keine Notwendigkeit, neue Binärdateien bereitzustellen, um die Verbesserung zu nutzen).

Wie wichtig dies ist, hängt von Ihrem Wartungsmodell ab (z. B. wie oft neue Binärdateien in der Praxis bereitgestellt werden) und es ist sehr schwierig zu beurteilen, wie schnell diese Anweisungen in Zukunft voraussichtlich sein werden. Zumindest ist Intel eine Art Leitfaden für Anwendungen in dieser Richtung, indem es sich zu einer zumindest angemessenen Leistung in der Zukunft verpflichtet ( 15.3.3.6 ):

REP MOVSB ​​und REP STOSB werden auf zukünftigen Prozessoren weiterhin eine recht gute Leistung erbringen.

Überlappung mit nachfolgenden Arbeiten

Dieser Vorteil wird memcpynatürlich nicht in einem einfachen Benchmark angezeigt, bei dem sich per Definition keine späteren Arbeiten überschneiden müssen. Daher müsste die Höhe des Nutzens in einem realen Szenario sorgfältig gemessen werden. Um den größtmöglichen Vorteil zu erzielen, muss möglicherweise der Code um das System neu organisiert werden memcpy.

Auf diesen Vorteil wird von Intel in ihrem Optimierungshandbuch (Abschnitt 11.16.3.4) und in ihren Worten hingewiesen:

Wenn bekannt ist, dass die Anzahl mindestens tausend Byte oder mehr beträgt, kann die Verwendung von erweitertem REP MOVSB ​​/ STOSB einen weiteren Vorteil bieten, um die Kosten des nicht verbrauchenden Codes zu amortisieren. Die Heuristik kann am Beispiel von Cnt = 4096 und memset () als Beispiel verstanden werden:

• Eine 256-Bit-SIMD-Implementierung von memset () muss 128 Instanzen des 32-Byte-Speicherbetriebs mit VMOVDQA ausgeben / ausführen, bevor die nicht verbrauchenden Befehlssequenzen in den Ruhestand gehen können.

• Eine Instanz von erweitertem REP STOSB mit ECX = 4096 wird als langer Micro-Op-Fluss dekodiert, der von der Hardware bereitgestellt wird, wird jedoch als eine Anweisung zurückgezogen. Es gibt viele store_data-Operationen, die abgeschlossen sein müssen, bevor das Ergebnis von memset () verwendet werden kann. Da der Abschluss der Speicherdatenoperation von der Stilllegung der Programmreihenfolge abgekoppelt ist, kann ein wesentlicher Teil des nicht verbrauchenden Codestreams durch die Ausgabe / Ausführung und Stilllegung verarbeitet werden, was im Wesentlichen kostenlos ist, wenn die nicht verbrauchende Sequenz nicht konkurriert für Speicherpufferressourcen.

Intel sagt also, dass nach einigen Uops der Code danach rep movsbausgegeben wurde, aber während viele Geschäfte noch im Flug sind und der rep movsbgesamte noch nicht in den Ruhestand gegangen ist, können Uops, die Anweisungen befolgen, weitere Fortschritte bei der Außerbetriebnahme erzielen Maschinen als sie könnten, wenn dieser Code nach einer Kopierschleife kam.

Die Uops aus einer expliziten Lade- und Speicherschleife müssen alle in der Programmreihenfolge separat in den Ruhestand versetzt werden. Das muss passieren, um im ROB Platz für folgende Uops zu schaffen.

Es scheint nicht viele detaillierte Informationen darüber zu geben, wie lange mikrocodierte Anweisungen rep movsbgenau funktionieren. Wir wissen nicht genau, wie Mikrocode-Zweige einen anderen Strom von Uops vom Mikrocode-Sequenzer anfordern oder wie sich die Uops zurückziehen. Wenn die einzelnen Uops nicht separat in den Ruhestand gehen müssen, nimmt der gesamte Befehl möglicherweise nur einen Platz im ROB ein?

Wenn das Front-End, das die OoO-Maschinerie speist, eine rep movsbAnweisung im UOP-Cache sieht , aktiviert es das Microcode Sequencer ROM (MS-ROM), um Mikrocode-Uops in die Warteschlange zu senden, die die Ausgabe- / Umbenennungsphase speist. Es ist wahrscheinlich nicht möglich, dass sich andere Uops damit einmischen und 8 ausgeben / ausführen, während rep movsbnoch ausgegeben wird, aber nachfolgende Anweisungen können direkt nach dem letzten rep movsbUOP abgerufen / dekodiert und ausgegeben werden, während ein Teil der Kopie noch nicht ausgeführt wurde . Dies ist nur dann nützlich, wenn zumindest ein Teil Ihres nachfolgenden Codes nicht vom Ergebnis des memcpy(was nicht ungewöhnlich ist) abhängt .

Jetzt ist die Größe dieses Vorteils begrenzt: Sie können höchstens N Befehle (eigentlich Uops) über den langsamen rep movsbBefehl hinaus ausführen. An diesem Punkt werden Sie stehen bleiben, wobei N die ROB-Größe ist . Bei aktuellen ROB-Größen von ~ 200 (192 bei Haswell, 224 bei Skylake) bedeutet dies einen maximalen Vorteil von ~ 200 Zyklen freier Arbeit für nachfolgenden Code mit einem IPC von 1. In 200 Zyklen können Sie ungefähr 800 Byte bei 10 GB kopieren / s, so dass Sie für Kopien dieser Größe möglicherweise freie Arbeit in der Nähe der Kosten der Kopie erhalten (in gewisser Weise, um die Kopie kostenlos zu machen).

Wenn die Kopiengröße jedoch viel größer wird, nimmt die relative Bedeutung dieser schnell ab (z. B. wenn Sie stattdessen 80 KB kopieren, beträgt die freie Arbeit nur 1% der Kopierkosten). Trotzdem ist es für Kopien von bescheidener Größe ziemlich interessant.

Kopierschleifen blockieren auch die Ausführung nachfolgender Anweisungen nicht vollständig. Intel geht nicht detailliert auf die Größe des Vorteils ein oder darauf, welche Art von Kopien oder umgebendem Code den größten Nutzen bringt. (Heißes oder kaltes Ziel oder Quelle, Code mit hohem ILP oder niedrigem ILP und hoher Latenz nach).

Codegröße

Die ausgeführte Codegröße (einige Bytes) ist im Vergleich zu einer typischen optimierten memcpyRoutine mikroskopisch . Wenn die Leistung durch i-Cache-Fehler (einschließlich UOP-Cache) eingeschränkt wird, kann die reduzierte Codegröße von Vorteil sein.

Auch hier können wir die Größe dieses Vorteils anhand der Größe der Kopie begrenzen. Ich werde es nicht numerisch herausarbeiten, aber die Intuition ist, dass das Reduzieren der dynamischen Codegröße um B Bytes höchstens C * BCache-Misses für eine Konstante C einsparen kann . Jeder Aufruf, um memcpydie Cache-Miss-Kosten (oder den Nutzen) einmal zu verursachen, Der Vorteil eines höheren Durchsatzes hängt jedoch von der Anzahl der kopierten Bytes ab. Bei großen Übertragungen dominiert daher ein höherer Durchsatz die Cache-Effekte.

Auch dies wird nicht in einem einfachen Benchmark angezeigt, bei dem die gesamte Schleife zweifellos in den UOP-Cache passt. Sie benötigen einen realen In-Place-Test, um diesen Effekt zu bewerten.

Architekturspezifische Optimierung

Sie haben berichtet, dass Ihre Hardware rep movsberheblich langsamer war als die Plattform memcpy. Aber auch hier gibt es Berichte über das gegenteilige Ergebnis auf früherer Hardware (wie Ivy Bridge).

Das ist durchaus plausibel, da es den Anschein hat, dass die String-Move-Operationen in regelmäßigen Abständen geliebt werden - aber nicht in jeder Generation. Daher kann es sein, dass sie schneller oder zumindest an die Architekturen gebunden sind (an diesem Punkt kann sie aufgrund anderer Vorteile gewinnen) auf den neuesten Stand gebracht, nur um in der nachfolgenden Hardware ins Hintertreffen zu geraten.

Zitat von Andy Glew, der ein oder zwei Dinge darüber wissen sollte, nachdem er diese auf dem P6 implementiert hat:

Die große Schwäche, schnelle Zeichenfolgen im Mikrocode zu erstellen, war, [...] dass der Mikrocode mit jeder Generation verstimmt war und immer langsamer wurde, bis jemand dazu kam, ihn zu reparieren. Genau wie eine Bibliothek fällt eine Männerkopie verstimmt. Ich nehme an, dass es möglich ist, dass eine der verpassten Möglichkeiten darin bestand, 128-Bit-Ladevorgänge und -Speicher zu verwenden, wenn sie verfügbar wurden, und so weiter.

In diesem Fall kann es als eine weitere "plattformspezifische" Optimierung angesehen werden, die in den typischen All-Trick-in-the-Book- memcpyRoutinen angewendet wird, die Sie in Standardbibliotheken und JIT-Compilern finden: jedoch nur für Architekturen, bei denen dies besser ist . Für JIT- oder AOT-kompilierte Inhalte ist dies einfach, für statisch kompilierte Binärdateien ist jedoch ein plattformspezifischer Versand erforderlich, der jedoch häufig bereits vorhanden ist (manchmal zur Verbindungszeit implementiert), oder das mtuneArgument kann verwendet werden, um eine statische Entscheidung zu treffen.

Einfachheit

Selbst auf Skylake, wo es den Anschein hat, als sei es hinter den absolut schnellsten nicht-zeitlichen Techniken zurückgefallen, ist es immer noch schneller als die meisten Ansätze und sehr einfach . Dies bedeutet weniger Zeit für die Validierung, weniger mysteriöse Fehler, weniger Zeit für die Optimierung und Aktualisierung einer Monster- memcpyImplementierung (oder umgekehrt weniger Abhängigkeit von den Launen der Standard-Bibliotheksimplementierer, wenn Sie sich darauf verlassen).

Latenzgebundene Plattformen

Speicherdurchsatzgebundene Algorithmen 9 können tatsächlich in zwei Hauptregimen arbeiten: DRAM-Bandbreitengebunden oder Parallelitäts- / Latenzzeitgebunden.

Der erste Modus ist der, mit dem Sie wahrscheinlich vertraut sind: Das DRAM-Subsystem verfügt über eine bestimmte theoretische Bandbreite, die Sie anhand der Anzahl der Kanäle, der Datenrate / -breite und der Frequenz recht einfach berechnen können. Zum Beispiel hat mein DDR4-2133-System mit 2 Kanälen eine maximale Bandbreite von 2,133 * 8 * 2 = 34,1 GB / s, wie in ARK angegeben .

Sie werden nicht mehr als diese Rate von DRAM (und normalerweise etwas weniger aufgrund verschiedener Ineffizienzen) aufrechterhalten, die über alle Kerne auf dem Socket hinzugefügt werden (dh es ist eine globale Grenze für Single-Socket-Systeme).

Die andere Grenze wird dadurch festgelegt, wie viele gleichzeitige Anforderungen ein Kern tatsächlich an das Speichersubsystem senden kann. Stellen Sie sich vor, ein Kern könnte nur eine Anforderung gleichzeitig für eine 64-Byte-Cache-Zeile ausführen. Wenn die Anforderung abgeschlossen ist, können Sie eine weitere ausgeben. Nehmen Sie auch eine sehr schnelle Speicherlatenz von 50 ns an. Dann würden Sie trotz der großen DRAM-Bandbreite von 34,1 GB / s nur 64 Bytes / 50 ns = 1,28 GB / s oder weniger als 4% der maximalen Bandbreite erhalten.

In der Praxis können Kerne mehr als eine Anforderung gleichzeitig ausgeben, jedoch nicht eine unbegrenzte Anzahl. Es versteht sich normalerweise, dass zwischen dem L1 und dem Rest der Speicherhierarchie nur 10 Zeilenfüllpuffer pro Kern und zwischen L2 und dem DRAM etwa 16 Füllpuffer vorhanden sind. Das Prefetching konkurriert um dieselben Ressourcen, trägt jedoch zumindest dazu bei, die effektive Latenz zu verringern. Weitere Informationen finden Sie in den großartigen Beiträgen, die Dr. Bandwidth zu diesem Thema verfasst hat , hauptsächlich in den Intel-Foren.

Trotzdem die meisten sind jüngste CPUs durch begrenzt diesen Faktor, nicht die RAM - Bandbreite. Normalerweise erreichen sie 12 bis 20 GB / s pro Kern, während die RAM-Bandbreite 50+ GB / s betragen kann (auf einem 4-Kanal-System). Nur einige neuere 2-Kanal- "Client" -Kerne der letzten Generation, die einen besseren Uncore zu haben scheinen, können möglicherweise mehr Zeilenpuffer die DRAM-Grenze für einen einzelnen Kern erreichen, und unsere Skylake-Chips scheinen einer von ihnen zu sein.

Jetzt gibt es natürlich einen Grund, warum Intel Systeme mit einer DRAM-Bandbreite von 50 GB / s entwirft, während aufgrund von Parallelitätsbeschränkungen nur <20 GB / s pro Kern aufrechterhalten werden sollen: Die erstere Grenze ist sockelweit und die letztere ist pro Kern. Jeder Core auf einem 8-Core-System kann also Anforderungen im Wert von 20 GB / s senden. Ab diesem Zeitpunkt sind sie wieder DRAM-begrenzt.

Warum mache ich das immer weiter? Da die beste memcpyImplementierung häufig davon abhängt, in welchem ​​Regime Sie arbeiten. Sobald Sie DRAM BW-begrenzt sind (wie unsere Chips anscheinend, aber die meisten nicht auf einem einzelnen Kern), wird die Verwendung von nicht-zeitlichen Schreibvorgängen sehr wichtig, da dies das spart Read-for-Ownership, das normalerweise 1/3 Ihrer Bandbreite verschwendet. Sie sehen das genau in den obigen Testergebnissen: Die memcpy-Implementierungen, die keine NT-Speicher verwenden, verlieren 1/3 ihrer Bandbreite.

Wenn Sie jedoch auf Parallelität beschränkt sind, gleicht sich die Situation aus und kehrt sich manchmal um. Sie haben DRAM-Bandbreite zur Verfügung, sodass NT-Speicher nicht helfen und sogar Schaden anrichten können, da sie die Latenz erhöhen können, da die Übergabezeit für den Leitungspuffer möglicherweise länger ist als in einem Szenario, in dem der Vorabruf die RFO-Leitung in LLC (oder sogar) bringt L2) und dann wird der Speicher in LLC für eine effektiv geringere Latenz abgeschlossen. Schließlich haben Server- Uncores tendenziell viel langsamere NT-Speicher als Client-Speicher (und eine hohe Bandbreite), was diesen Effekt verstärkt.

Auf anderen Plattformen stellen Sie möglicherweise fest, dass NT-Stores weniger nützlich sind (zumindest, wenn Sie sich für Single-Threaded-Leistung interessieren) und vielleicht rep movsbdort gewinnen (wenn sie das Beste aus beiden Welten bieten ).

Wirklich, dieser letzte Punkt ist ein Aufruf für die meisten Tests. Ich weiß, dass NT-Speicher ihren offensichtlichen Vorteil für Single-Threaded-Tests auf den meisten Bögen (einschließlich der aktuellen Server-Bögen) verlieren, aber ich weiß nicht, wie rep movsbdie Leistung relativ ...

Verweise

Andere gute Informationsquellen, die oben nicht integriert sind.

comp.arch Untersuchung von rep movsbversus Alternativen. Viele gute Hinweise zur Verzweigungsvorhersage und eine Implementierung des Ansatzes, den ich oft für kleine Blöcke vorgeschlagen habe: Verwenden Sie überlappende erste und / oder letzte Lese- / Schreibvorgänge, anstatt zu versuchen, nur genau die erforderliche Anzahl von Bytes zu schreiben (z. B. Implementierung alle Kopien von 9 bis 16 Bytes als zwei 8-Byte-Kopien, die sich in bis zu 7 Bytes überlappen können).


1 Vermutlich soll es auf Fälle beschränkt werden, in denen beispielsweise die Codegröße sehr wichtig ist.

2 Siehe Abschnitt 3.7.5: REP-Präfix und Datenverschiebung.

3 Es ist wichtig zu beachten, dass dies nur für die verschiedenen Geschäfte innerhalb der einzelnen Anweisung selbst gilt: Sobald der Vorgang abgeschlossen ist, wird der Geschäftsblock in Bezug auf vorherige und nachfolgende Geschäfte weiterhin geordnet angezeigt. Code kann also Geschäfte von der rep movsReihenfolge aus in Bezug zueinander anzeigen, jedoch nicht in Bezug auf vorherige oder nachfolgende Geschäfte (und es ist die letztere Garantie, die Sie normalerweise benötigen). Dies ist nur dann ein Problem, wenn Sie das Ende des Kopierziels als Synchronisationsflag anstelle eines separaten Speichers verwenden.

4 Beachten Sie, dass nicht-zeitliche diskrete Speicher auch die meisten Bestellanforderungen vermeiden, obwohl sie in der Praxis rep movsnoch mehr Freiheit bieten, da für WC / NT-Speicher noch einige Bestellbeschränkungen bestehen.

5 Dies war im letzten Teil der 32-Bit-Ära üblich, als viele Chips 64-Bit-Datenpfade hatten (z. B. zur Unterstützung von FPUs, die den 64-Bit- doubleTyp unterstützten). Heutzutage haben "kastrierte" Chips wie die Marken Pentium oder Celeron AVX deaktiviert, aber vermutlich rep movskann der Mikrocode immer noch 256b Lasten / Speicher verwenden.

6 Zum Beispiel aufgrund von Sprachausrichtungsregeln, Ausrichtungsattributen oder Operatoren, Aliasing-Regeln oder anderen Informationen, die zur Kompilierungszeit festgelegt wurden. Im Falle einer Ausrichtung können sie, selbst wenn die genaue Ausrichtung nicht bestimmt werden kann, zumindest Ausrichtungsprüfungen aus Schleifen herausheben oder auf andere Weise redundante Prüfungen eliminieren.

7 Ich gehe davon aus, dass "Standard" memcpyeinen nicht-zeitlichen Ansatz wählt, der für diese Puffergröße sehr wahrscheinlich ist.

8 Das ist nicht unbedingt offensichtlich, da es der Fall sein könnte, dass der UOP-Stream, der durch den rep movsbeinfach monopolisierten Versand generiert wird, und dann dem expliziten movFall sehr ähnlich sieht . Es scheint jedoch nicht so zu funktionieren - Uops aus nachfolgenden Anweisungen können sich mit Uops aus dem Mikrocodierten vermischen rep movsb.

9 Das heißt, diejenigen, die eine große Anzahl unabhängiger Speicheranforderungen ausgeben und damit die verfügbare DRAM-zu-Kern-Bandbreite sättigen können, von denen memcpyein Aushängeschild wäre (und für rein latenzgebundene Lasten wie Zeigerjagd gilt).

BeeOnRope
quelle
3
@BeeOnRope: Hier sind meine Ergebnisse ; Die Datei enthält die System- und Compilerinformationen. Es hat ERMS-Unterstützung, aber die Ergebnisse zeigen, dass es auf diesem System nicht so wettbewerbsfähig ist. erklärt meine Schwierigkeiten, einen Gewinn-Test dafür zu finden. Würde es Ihnen etwas ausmachen, Ihrer Antwort einen Kommentar hinzuzufügen, dass tinymembench nur 64-Bit-ausgerichtete Kopien und Füllungen ausführt? Obwohl es perfekt auf die hier gestellte Frage anwendbar ist, handelt es sich ausschließlich um eine Teilmenge der typischen Anwendungsfälle in realen Anwendungen.
Nominelles Tier
3
@MaximMasiutin - Die Diskussion über die Verzweigungsvorhersage ist wahrscheinlich eine ganz andere Frage zu SO wert, aber die kurze Antwort lautet, dass die genauen Techniken für die neuesten Chips nicht bekannt gegeben wurden, aber Sie sich wahrscheinlich etwas ansehen , das TAGE unter Intel sehr ähnlich ist und Wahrnehmungen auf AMD. Generell empfehle ich nur, die Anleitungen 1, 2 und 3 von Agner vollständig zu lesen .
BeeOnRope
2
Das genaue Verhalten spielt jedoch normalerweise keine Rolle: Nehmen Sie einfach an, dass der Prädiktor einfach die Richtung vorhersagt, die er am häufigsten sieht, und dass Sie einen Zyklus von ~ 20 bezahlen, es sei denn, Ihre Abfolge von Zweigen folgt einem einfachen (ish) sich wiederholenden Muster Strafe jedes Mal, wenn der Zweig den "anderen" Weg geht. Sie können die tatsächliche Leistung jedes Zweigs in Ihrer Anwendung mit perf statund perf record -e branch-misses:ppunter Linux (und was auch immer das Äquivalent unter Windows ist) leicht überprüfen .
BeeOnRope
3
@PeterCordes - ja, scheinen ich gewesen inkonsequent zu haben im movsdVergleich zu movsb, an einigen Stellen behaupten , sie die gleiche Leistung auf haben ermsPlattformen, aber oben Ich sage , dass frühere Versuche an früheren Archs mit ERMSB zeigen movsbals viel schneller durchführenmovsd . Das ist spezifisch genug, dass ich die Daten gesehen haben muss, aber ich kann sie in diesem Thread nicht finden. Es könnte von einem dieser beiden großen Threads in RWT stammen oder die Beispiele im Intel-Handbuch bilden.
BeeOnRope
2
Das Intel-Handbuch enthält beispielsweise Abbildung 3-4. Memcpy-Leistungsvergleich für Längen bis zu 2 KB, der zeigt, dass rep movsd(plus ein Nachlauf movsbfür die letzten drei Bytes) auf Ivy Bridge erheblich schlechter skaliert als movsbbis zu 256 Bytes. An diesem Punkt scheint die Steigung gleich zu sein. Es gibt einige Ivy Bridge Ergebnisse hier , die zeigen , rep movsdetwa 3% langsamer als rep movsb, aber vielleicht ist das im Rahmen der Messfehler und nicht groß , auch wenn es nicht.
BeeOnRope
12

Enhanced REP MOVSB ​​(Ivy Bridge und höher) #

Die Ivy Bridge-Mikroarchitektur (Prozessoren, die in den Jahren 2012 und 2013 veröffentlicht wurden) führte Enhanced REP MOVSB ​​ein (wir müssen noch das entsprechende Bit überprüfen) und ermöglichte es uns, den Speicher schnell zu kopieren.

Die billigsten Versionen späterer Prozessoren - Kaby Lake Celeron und Pentium, die 2017 veröffentlicht wurden - verfügen nicht über AVX, das für eine schnelle Speicherkopie hätte verwendet werden können, aber dennoch über das erweiterte REP-MOVSB. Einige der ab 2018 veröffentlichten mobilen und stromsparenden Architekturen von Intel, die nicht auf SkyLake basierten, kopieren mit REP MOVSB ​​etwa zweimal mehr Bytes pro CPU-Zyklus.

REP MOVSB ​​(ERMSB) ist nur dann schneller als eine AVX-Kopie oder eine allgemeine Registerkopie, wenn die Blockgröße mindestens 256 Byte beträgt. Für die Blöcke unter 64 Bytes ist es viel langsamer, da es in ERMSB einen hohen internen Start gibt - ungefähr 35 Zyklen.

Siehe das Intel-Handbuch zur Optimierung, Abschnitt 3.7.6 Erweiterter REP MOVSB- und STOSB-Betrieb (ERMSB) http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia- 32-Architekturen-Optimierungshandbuch.pdf

  • Die Startkosten betragen 35 Zyklen.
  • Sowohl die Quell- als auch die Zieladresse müssen an einer 16-Byte-Grenze ausgerichtet sein.
  • Die Quellregion sollte sich nicht mit der Zielregion überschneiden.
  • Die Länge muss ein Vielfaches von 64 sein, um eine höhere Leistung zu erzielen.
  • Die Richtung muss vorwärts sein (CLD).

Wie ich bereits sagte, beginnt REP MOVSB, andere Methoden zu übertreffen, wenn die Länge mindestens 256 Byte beträgt. Um jedoch den klaren Vorteil gegenüber der AVX-Kopie zu erkennen, muss die Länge mehr als 2048 Byte betragen. Es sollte auch beachtet werden, dass die bloße Verwendung von AVX (256-Bit-Register) oder AVX-512 (512-Bit-Register) für die Speicherkopie manchmal schlimme Folgen haben kann, wie z. B. AVX / SSE-Übergangsstrafen oder reduzierte Turbofrequenz. Der REP MOVSB ​​ist also eine sicherere Methode zum Kopieren von Speicher als AVX.

In Bezug auf die Auswirkung der Ausrichtung, wenn REP MOVSB ​​vs. AVX kopiert wird, enthält das Intel-Handbuch die folgenden Informationen:

  • Wenn der Quellpuffer nicht ausgerichtet ist, sind die Auswirkungen auf die ERMSB-Implementierung im Vergleich zu 128-Bit-AVX ähnlich.
  • Wenn der Zielpuffer nicht ausgerichtet ist, kann die Auswirkung auf die ERMSB-Implementierung eine Verschlechterung von 25% sein, während die 128-Bit-AVX-Implementierung von memcpy im Vergleich zu einem 16-Byte-ausgerichteten Szenario nur 5% verschlechtern kann.

Ich habe Tests mit Intel Core i5-6600 unter 64-Bit durchgeführt und REP MOVSB ​​memcpy () mit einem einfachen MOV RAX [SRC] verglichen. MOV [DST], RAX-Implementierung, wenn die Daten in den L1-Cache passen :

REP MOVSB ​​memcpy ():

MOV RAX ... memcpy ():

Selbst in 128-Bit-Blöcken ist REP MOVSB ​​langsamer als nur eine einfache MOV RAX-Kopie in einer Schleife (nicht abgewickelt). Die ERMSB-Implementierung beginnt, die MOV-RAX-Schleife nur ab 256-Byte-Blöcken zu übertreffen.

#Normal (nicht erweitert) REP MOVS auf Nehalem und höher #

Überraschenderweise hatten frühere Architekturen (Nehalem und später), die noch kein erweitertes REP-MOVB hatten, eine recht schnelle Implementierung von REP MOVSD / MOVSQ (aber nicht REP MOVSB ​​/ MOVSW) für große Blöcke, aber nicht groß genug, um den L1-Cache zu überdimensionieren.

Das Intel Optimization Manual (2.5.6 REP String Enhancement) enthält die folgenden Informationen zur Nehalem-Mikroarchitektur - Intel Core i5-, i7- und Xeon-Prozessoren, die 2009 und 2010 veröffentlicht wurden.

REP MOVSB

Die Latenz für MOVSB ​​beträgt 9 Zyklen, wenn ECX <4; Andernfalls verursachen REP MOVSB ​​mit ECX> 9 Startkosten von 50 Zyklen.

  • winziger String (ECX <4): Die Latenz von REP MOVSB ​​beträgt 9 Zyklen;
  • kleine Zeichenfolge (ECX liegt zwischen 4 und 9): Keine offiziellen Informationen im Intel-Handbuch, wahrscheinlich mehr als 9 Zyklen, aber weniger als 50 Zyklen;
  • lange Zeichenfolge (ECX> 9): Startkosten für 50 Zyklen.

Mein Fazit: REP MOVSB ​​ist auf Nehalem fast nutzlos.

MOVSW / MOVSD / MOVSQ

Zitat aus dem Intel Optimization Manual (2.5.6 REP String Enhancement):

  • Kurze Zeichenfolge (ECX <= 12): Die Latenz von REP MOVSW / MOVSD / MOVSQ beträgt ungefähr 20 Zyklen.
  • Schnelle Zeichenfolge (ECX> = 76: ohne REP MOVSB): Die Prozessorimplementierung bietet Hardwareoptimierung, indem so viele Daten in 16 Byte wie möglich verschoben werden. Die Latenz der REP-String-Latenz variiert, wenn sich eine der 16-Byte-Datenübertragungen über die Cache-Zeilengrenze erstreckt: = Split-frei: Die Latenz besteht aus Startkosten von ca. 40 Zyklen, und jeweils 64 Datenbytes addieren 4 Zyklen. = Cache-Aufteilung: Die Latenz besteht aus Startkosten von ca. 35 Zyklen und jeweils 64 Datenbytes addieren 6 Zyklen.
  • Zwischenzeichenfolgenlängen: Die Latenz von REP MOVSW / MOVSD / MOVSQ hat Startkosten von etwa 15 Zyklen plus einem Zyklus für jede Iteration der Datenbewegung in Wort / Wort / Wort.

Intel scheint hier nicht richtig zu sein. Aus dem obigen Zitat geht hervor, dass REP MOVSW für sehr große Speicherblöcke genauso schnell ist wie REP MOVSD / MOVSQ. Tests haben jedoch gezeigt, dass nur REP MOVSD / MOVSQ schnell sind, während REP MOVSW auf Nehalem und Westmere sogar langsamer als REP MOVSB ​​ist .

Nach den Informationen von Intel im Handbuch sind die Startkosten bei früheren Intel-Mikroarchitekturen (vor 2008) sogar noch höher.

Fazit: Wenn Sie nur Daten kopieren müssen, die in den L1-Cache passen, sind nur 4 Zyklen zum Kopieren von 64 Datenbytes hervorragend, und Sie müssen keine XMM-Register verwenden!

#REP MOVSD / MOVSQ ist die universelle Lösung, die auf allen Intel-Prozessoren (kein ERMSB erforderlich) hervorragend funktioniert, wenn die Daten in den L1-Cache passen. #

Hier sind die Tests von REP MOVS *, wenn sich die Quelle und das Ziel im L1-Cache befanden, von Blöcken, die groß genug sind, um nicht ernsthaft von den Startkosten betroffen zu sein, aber nicht so groß, dass sie die Größe des L1-Cache überschreiten. Quelle: http://users.atw.hu/instlatx64/

Yonah (2006-2008)

Nehalem (2009-2010)

Westmere (2010-2011)

Ivy Bridge (2012-2013) - mit erweitertem REP-MOVSB ​​(alle nachfolgenden CPUs verfügen auch über erweitertes REP-MOVSB)

SkyLake (2015-2016)

Kaby Lake (2016-2017)

Cannon Lake, mobil (Mai 2018 - Februar 2020)

Cascade Lake, Server (April 2019)

Comet Lake, Desktop, Workstation, Mobile (August 2019)

Ice Lake, mobil (September 2019)

Tremont, geringer Stromverbrauch (September 2020)

Tiger Lake, mobil (Oktober 2020)

Wie Sie sehen, unterscheidet sich die Implementierung von REP MOVS von Mikroarchitektur zu Mikroarchitektur erheblich. Auf einigen Prozessoren wie Ivy Bridge ist REP MOVSB ​​am schnellsten, wenn auch nur geringfügig schneller als REP MOVSD / MOVSQ, aber ohne Zweifel funktioniert REP MOVSD / MOVSQ auf allen Prozessoren seit Nehalem sehr gut - Sie benötigen nicht einmal "Enhanced REP" MOVSB ​​", da REP MOVSD auf Ivy Bridge (2013) mit Enhacnced REP MOVSB die gleichen Byte-pro-Uhr-Daten wie auf Nehalem (2010) ohne Enhacnced REP MOVSB ​​anzeigt , während REP MOVSB ​​erst seit SkyLake (2015) sehr schnell wurde. - doppelt so schnell wie auf der Ivy Bridge. Dieses Enhacnced REP MOVSB- Bit in der CPUID kann also verwirrend sein - es zeigt nur, dass es REP MOVSBper se in Ordnung ist, aber nicht, dass eines REP MOVS*schneller ist.

Die verwirrendste ERMBSB-Implementierung ist die Ivy Bridge-Mikroarchitektur. Ja, auf sehr alten Prozessoren hat REP MOVS * für große Blöcke vor ERMSB eine Cache-Protokollfunktion verwendet, die für regulären Code nicht verfügbar ist (kein RFO). Dieses Protokoll wird jedoch auf Ivy Bridge mit ERMSB nicht mehr verwendet. Laut Andy Glews Kommentaren zu einer Antwort auf "Warum sind komplizierte Memcpy / Memsets überlegen?" aus einer Antwort von Peter CordesEine Cache-Protokollfunktion, die für regulären Code nicht verfügbar ist, wurde früher auf älteren Prozessoren verwendet, auf Ivy Bridge jedoch nicht mehr. Und es gibt eine Erklärung dafür, warum die Startkosten für REP MOVS * so hoch sind: „Der große Aufwand für die Auswahl und Einrichtung der richtigen Methode ist hauptsächlich auf das Fehlen einer Vorhersage der Mikrocode-Verzweigung zurückzuführen.“ Es gab auch einen interessanten Hinweis, dass Pentium Pro (P6) 1996 REP MOVS * mit 64-Bit-Mikrocode-Lade- und -Speichern und einem No-RFO-Cache-Protokoll implementierte - im Gegensatz zu ERMSB in Ivy Bridge haben sie die Speicherreihenfolge nicht verletzt.

Haftungsausschluss

  1. Diese Antwort ist nur für die Fälle relevant, in denen die Quell- und Zieldaten in den L1-Cache passen. Abhängig von den Umständen sollten die Besonderheiten des Speicherzugriffs (Cache usw.) berücksichtigt werden. Prefetch und NTI können in bestimmten Fällen bessere Ergebnisse liefern, insbesondere auf Prozessoren, die noch nicht über das erweiterte REP-MOVSB ​​verfügen. Selbst auf diesen älteren Prozessoren hat REP MOVSD möglicherweise eine Cache-Protokollfunktion verwendet, die für regulären Code nicht verfügbar ist.
  2. Die Informationen in dieser Antwort beziehen sich nur auf Intel-Prozessoren und nicht auf Prozessoren anderer Hersteller wie AMD, die möglicherweise bessere oder schlechtere Implementierungen von REP MOVS * -Anweisungen aufweisen.
  3. Ich habe nur zur Bestätigung Testergebnisse für SkyLake und Kaby Lake vorgelegt - diese Architekturen haben dieselben Daten für den Zyklus pro Anweisung.
  4. Alle Produktnamen, Marken und eingetragenen Marken sind Eigentum ihrer jeweiligen Inhaber.
Maxim Masiutin
quelle
2
Interessante L1D-Pufferdaten mittlerer Größe. Es kann jedoch nicht die ganze Geschichte sein. Einige der Vorteile von ERMSB (wie eine schwächere Bestellung der Geschäfte) werden nur bei größeren Puffern sichtbar, die nicht in den Cache passen. Selbst normale Fast-Strings rep movssollten ein No-RFO-Protokoll verwenden, selbst auf CPUs vor ERMSB.
Peter Cordes
3
Wenn ich es richtig verstehe, haben Sie gerade die L1D-only-Zahlen aus den instlatx64-Ergebnissen entfernt. So ist das Ergebnis wirklich , dass alle movsb, movsd, movsqführen in etwa gleich auf allen aktuellen Intel - Plattformen. Der interessanteste Imbiss ist wahrscheinlich "nicht verwenden movsw". Sie vergleichen sich nicht mit einer expliziten Schleife von movAnweisungen (einschließlich 16-Byte-Verschiebungen auf 64-Bit-Plattformen, die garantiert verfügbar sind), die in vielen Fällen wahrscheinlich schneller sein wird. Sie wissen nicht, was auf AMD-Plattformen passiert und wann die Größe die L1-Größe überschreitet.
BeeOnRope
2
Schließlich sollten Sie beachten, dass nichts anderes als rep movsbtatsächlich implementiert wird memcpy(und keiner von ihnen implementiert memmove), sodass Sie zusätzlichen Code für die anderen Varianten benötigen. Dies ist wahrscheinlich nur bei kleinen Größen von Bedeutung.
BeeOnRope
1
Ja, dieses Zitat ist genau das, worauf ich mich bezog.
Peter Cordes
1
@MaximMasiutin - Woher erhalten Sie, dass das ERMSB kein No-RFO-Protokoll mehr verwendet, das für regulären Code nicht verfügbar ist? Es wird sicherlich immer noch ein Nicht-RFO-Protokoll verwendet, zumindest für große Kopien, da es eine Leistung erzielt, die wirklich nur mit Nicht-RFO möglich ist (dies ist am offensichtlichsten stosb, gilt aber auch für die movVarianten). Es ist fraglich, ob dies immer noch "nicht für regulären Code verfügbar" ist, da Sie mit NT-Speichern den gleichen Effekt erzielen. Es ist daher nicht klar, ob "nicht für regulären Code verfügbar" nur NT-Speicher auf Plattformen bedeutet, die dies nicht getan haben haben sie oder etwas anderes als NT-Geschäfte.
BeeOnRope
8

Sie sagen, dass Sie wollen:

Eine Antwort, die zeigt, wann ERMSB nützlich ist

Aber ich bin nicht sicher, ob es bedeutet, was Sie denken, dass es bedeutet. In den 3.7.6.1-Dokumenten, auf die Sie verlinken, heißt es ausdrücklich:

Die Implementierung von memcpy mithilfe von ERMSB erreicht je nach Länge und Ausrichtungsfaktoren möglicherweise nicht den gleichen Durchsatz wie bei Verwendung von 256-Bit- oder 128-Bit-AVX-Alternativen.

Nur weil dies CPUIDauf die Unterstützung von ERMSB hinweist, ist dies keine Garantie dafür, dass REP MOVSB ​​der schnellste Weg ist, Speicher zu kopieren. Es bedeutet nur, dass es nicht so schlecht saugt wie in einigen früheren CPUs.

Nur weil es Alternativen gibt, die unter bestimmten Bedingungen schneller laufen können, bedeutet dies nicht, dass REP MOVSB ​​nutzlos ist. Nachdem die Leistungseinbußen, die diese Anweisung früher verursacht hat, weg sind, ist sie möglicherweise wieder eine nützliche Anweisung.

Denken Sie daran, es ist ein winziges Stück Code (2 Bytes!) Im Vergleich zu einigen der komplizierteren Memcpy-Routinen, die ich gesehen habe. Da das Laden und Ausführen großer Codestücke auch eine Strafe mit sich bringt (indem Sie einen Teil Ihres anderen Codes aus dem Cache der CPU werfen), wird der „Vorteil“ von AVX et al. Manchmal durch die Auswirkungen auf den Rest Ihres Computers ausgeglichen Code. Kommt darauf an, was du tust.

Sie fragen auch:

Warum ist die Bandbreite bei REP MOVSB ​​so viel geringer? Was kann ich tun, um es zu verbessern?

Es wird nicht möglich sein, "etwas zu tun", um REP MOVSB ​​schneller laufen zu lassen. Es macht was es macht.

Wenn Sie die höheren Geschwindigkeiten von memcpy sehen möchten, können Sie die Quelle dafür ausgraben. Es ist irgendwo da draußen. Oder Sie können von einem Debugger aus darauf zurückgreifen und die tatsächlich verwendeten Codepfade anzeigen. Ich gehe davon aus, dass einige dieser AVX-Anweisungen verwendet werden, um mit 128 oder 256 Bit gleichzeitig zu arbeiten.

Oder Sie können einfach ... Nun, Sie haben uns gebeten, es nicht zu sagen.

David Wohlferd
quelle
Ich habe REP MOVSBim L3-Cache auf Größen getestet und tatsächlich ist es mit einer SSE / AVX-Lösung konkurrenzfähig. Aber ich habe es noch nicht eindeutig besser gefunden. Und für Größen, die größer als der L3-Cache sind, gewinnen nicht-temporäre Speicher immer noch viel Zeit. Ihr Punkt zur Codegröße ist interessant und erwägenswert. Ich weiß nicht viel über Mikrocode. REP MOVSBwird mit Mikrocode implementiert, so dass, obwohl es nicht viel des Code-Cache verbraucht und nur als ein Befehl zählt, es dennoch viele der Ports und / oder Mikro-Ops verbrauchen kann.
Z Boson
"habe es noch nicht eindeutig besser gefunden." Besser als was? "Erweitert" ist nicht dasselbe wie "Optimal". Ich habe keinen Ort gesehen, der versprochen hätte, der beste Künstler zu sein. Ich glaube nicht, dass diese CPU-Flagge das vermitteln soll. Es ist besser als auf Plattformen, auf denen eine Strafe verhängt wurde (sogar über eine movq / cmp-Schleife). "Codegröße" ist nicht immer leicht zu erkennen. Genau wie Speicher, der in Cache-Zeilen gespeichert ist, die in die CPU und aus der CPU ausgetauscht werden, funktioniert auch Code. Das Blättern in einem riesigen alten Memcpy bedeutet, dass ein Teil Ihres anderen Codes entfernt wird.
David Wohlferd
Siehe das Ende meiner Frage, wo ich einen Kommentar zitiere, der besagt, dass ERMSB auch für große Größen besser sein sollte als nicht temporäre Speicher.
Z Boson
1
Warten! Sie haben Beweise, rep movsbdie besser sind als die Alternativen? Ich möchte mehr darüber hören. Zur Verdeutlichung suche ich keine Antwort, die nur zeigt, wo rep movsbes für große Arrays besser ist (vielleicht ist das sowieso nicht wahr). Es würde mich interessieren, wo ein Beispiel rep movsbbesser ist als Alternativen.
Z Boson
1
Diese Antwort nagelt wirklich, was gesagt werden muss. Der Schlüssel ist , dass memcpyist sehr optimiert, alle möglichen verrückten Dinge tut , um die meisten Geschwindigkeit zu bekommen. Wenn Sie die Implementierung Ihrer Bibliothek studieren, werden Sie wahrscheinlich erstaunt sein. (Wenn Sie nicht den Compiler von Microsoft verwenden, werden Sie möglicherweise enttäuscht sein, aber Sie würden diese Frage nicht stellen.) Es ist sehr unwahrscheinlich, dass Sie eine von Hand abgestimmte memcpyFunktion in Bezug auf Geschwindigkeit übertreffen , und wenn Sie könnten, dann Es ist auch sehr wahrscheinlich, dass die Glibc-Leute bei der Abstimmung auf Ivy Bridge oder eine andere Architektur, die diese Verbesserungen unterstützt, darauf umsteigen würden.
Cody Gray
7

Dies ist keine Antwort auf die angegebenen Fragen, sondern nur meine Ergebnisse (und persönlichen Schlussfolgerungen), wenn ich versuche, dies herauszufinden.

Zusammenfassend: GCC optimiert bereits memset()/ memmove()/ memcpy()(siehe z. B. gcc / config / i386 / i386.c: expand_set_or_movmem_via_rep () in den GCC-Quellen; suchen Sie auch stringop_algsin derselben Datei nach architekturabhängigen Varianten). Es gibt also keinen Grund, massive Gewinne durch die Verwendung Ihrer eigenen Variante mit GCC zu erwarten (es sei denn, Sie haben wichtige Dinge wie Ausrichtungsattribute für Ihre ausgerichteten Daten vergessen oder nicht ausreichend spezifische Optimierungen wie aktiviert -O2 -march= -mtune=). Wenn Sie zustimmen, sind die Antworten auf die angegebene Frage in der Praxis mehr oder weniger irrelevant.

(Ich wünschte nur , es war ein memrepeat(), das Gegenteil von den im memcpy()Vergleich zu memmove(), die den Anfangsteil eines Puffers wiederholen würden den gesamten Puffer zu füllen.)


Ich habe derzeit einen Ivy Bridge-Computer im Einsatz (Core i5-6200U-Laptop, Linux 4.4.0 x86-64-Kernel, mit ermsIn- /proc/cpuinfoFlags). Da ich herausfinden wollte, ob ich einen Fall finden kann, in dem eine benutzerdefinierte memcpy () - Variante, die auf basiert rep movsb, eine einfache Variante übertreffen würde memcpy(), habe ich einen übermäßig komplizierten Benchmark geschrieben.

Die Kernidee ist, dass das Hauptprogramm drei große Speicherbereiche zuweist: original,, currentund correct, jeweils genau gleich groß und mindestens seitenausgerichtet. Die Kopiervorgänge sind in Gruppen zusammengefasst, wobei jede Gruppe unterschiedliche Eigenschaften aufweist, z. B. dass alle Quellen und Ziele ausgerichtet sind (auf eine bestimmte Anzahl von Bytes) oder alle Längen innerhalb desselben Bereichs liegen. Jeder Satz wird beschrieben eine Reihe von unter Verwendung von src, dst, nTriolen, wo alle srczu src+n-1und dstzu dst+n-1ist vollständig innerhalb der currentUmgebung.

Ein Xorshift * PRNG wird verwendet, um originalzufällige Daten zu initialisieren . (Wie ich oben gewarnt habe, ist dies zu kompliziert, aber ich wollte sicherstellen, dass ich keine einfachen Verknüpfungen für den Compiler hinterlasse.) Der correctBereich wird erhalten, indem mit originalDaten in begonnen currentwird und alle Triplets im aktuellen Satz unter Verwendung von memcpy()bereitgestellt angewendet werden durch die C-Bibliothek und Kopieren des currentBereichs nach correct. Auf diese Weise kann überprüft werden, ob sich jede Benchmark-Funktion korrekt verhält.

Jeder Satz von Kopiervorgängen wird mit derselben Funktion sehr oft zeitgesteuert, und der Median davon wird zum Vergleich verwendet. (Meiner Meinung nach ist der Median beim Benchmarking am sinnvollsten und bietet eine sinnvolle Semantik - die Funktion ist mindestens die Hälfte der Zeit mindestens so schnell.)

Um Compiler-Optimierungen zu vermeiden, lasse das Programm die Funktionen und Benchmarks zur Laufzeit dynamisch laden. Die Funktionen haben alle die gleiche Form. void function(void *, const void *, size_t)Beachten Sie, dass sie im Gegensatz zu memcpy()und memmove()nichts zurückgeben. Die Benchmarks (benannte Sätze von Kopiervorgängen) werden dynamisch durch einen Funktionsaufruf generiert (der currentunter anderem den Zeiger auf den Bereich und seine Größe als Parameter verwendet).

Leider habe ich noch kein Set gefunden wo

würde schlagen

Verwendung gcc -Wall -O2 -march=ivybridge -mtune=ivybridgevon GCC 5.4.0 auf dem oben genannten Core i5-6200U-Laptop mit einem 64-Bit-Kernel unter Linux-4.4.0. Das Kopieren von Blöcken mit einer Ausrichtung von 4096 Byte und einer Größe kommt jedoch nahe.

Dies bedeutet, dass ich zumindest bisher keinen Fall gefunden habe, in dem die Verwendung einer rep movsbmemcpy-Variante sinnvoll wäre. Dies bedeutet nicht, dass es keinen solchen Fall gibt. Ich habe nur keinen gefunden.

(An diesem Punkt ist der Code ein Spaghetti-Durcheinander, auf das ich mich mehr schäme als stolz bin, daher werde ich die Veröffentlichung der Quellen unterlassen, es sei denn, jemand fragt. Die obige Beschreibung sollte jedoch ausreichen, um eine bessere zu schreiben.)


Das überrascht mich allerdings nicht sehr. Der C-Compiler kann viele Informationen über die Ausrichtung der Operandenzeiger ableiten und darüber, ob die Anzahl der zu kopierenden Bytes eine Konstante zur Kompilierungszeit ist, ein Vielfaches einer geeigneten Zweierpotenz. Diese Informationen können und werden / sollten vom Compiler verwendet werden, um die C-Bibliothek memcpy()/ memmove()-Funktionen durch eigene zu ersetzen .

GCC macht genau das (siehe z. B. gcc / config / i386 / i386.c: expand_set_or_movmem_via_rep () in den GCC-Quellen; suchen Sie auch stringop_algsin derselben Datei nach architekturabhängigen Varianten). Tatsächlich memcpy()/ memset()/ memmove()wurde schon einige x86 - Prozessor - Varianten bereits separat optimiert; Es würde mich ziemlich überraschen, wenn die GCC-Entwickler die Unterstützung von erms noch nicht aufgenommen hätten.

GCC bietet verschiedene Funktionsattribute, mit denen Entwickler einen gut generierten Code sicherstellen können. alloc_align (n)Teilt GCC beispielsweise mit, dass die Funktion Speicher zurückgibt, der auf mindestens nBytes ausgerichtet ist. Eine Anwendung oder Bibliothek kann auswählen, welche Implementierung einer Funktion zur Laufzeit verwendet werden soll, indem sie eine "Resolver-Funktion" (die einen Funktionszeiger zurückgibt) erstellt und die Funktion mithilfe des ifunc (resolver)Attributs definiert.

Eines der häufigsten Muster, die ich in meinem Code dafür verwende, ist

Wo ptrist ein Zeiger, alignmentist die Anzahl der Bytes, an denen es ausgerichtet ist; GCC weiß / nimmt dann an, dass dies pointerauf alignmentBytes ausgerichtet ist .

Ein weiteres integrierte in nützlich, wenn auch viel schwieriger zu verwenden , richtig ist __builtin_prefetch(). Um die Gesamtbandbreite / -effizienz zu maximieren, habe ich festgestellt, dass die Minimierung der Latenzen in jeder Unteroperation die besten Ergebnisse liefert. (Zum Kopieren verstreuter Elemente in einen aufeinanderfolgenden temporären Speicher ist dies schwierig, da das Vorabrufen normalerweise eine vollständige Cache-Zeile umfasst. Wenn zu viele Elemente vorabgerufen werden, wird der größte Teil des Caches durch das Speichern nicht verwendeter Elemente verschwendet.)

Nominelles Tier
quelle
i5-6200U Laptop ist nicht Ivy Bridge. Es ist Skylake. Es wäre interessant, Tingybenchmark auf einem Ivy Bridge-System zu sehen.
Z Boson
@ Zboson: Ganz richtig ; Vielen Dank für den Hinweis. Ich weiß nicht, woher ich diese Annahme habe; wahrscheinlich zog es von meinem Hintern. Autsch. Erklärt auch meine Ergebnisse.
Nominelles Tier
4

Es gibt weitaus effizientere Möglichkeiten, Daten zu verschieben. memcpyHeutzutage generiert die Implementierung von architekturspezifischem Code aus dem Compiler, der basierend auf der Speicherausrichtung der Daten und anderen Faktoren optimiert wird. Dies ermöglicht eine bessere Verwendung von nicht-zeitlichen Cache-Anweisungen und XMM und anderen Registern in der x86-Welt.

Wenn Sie Hardcode verwenden, wird rep movsbdiese Verwendung von Intrinsics verhindert.

Wenn memcpySie also etwas schreiben, das an eine bestimmte Hardware gebunden ist, und wenn Sie sich nicht die Zeit nehmen, eine hochoptimierte memcpyFunktion in der Baugruppe zu schreiben (oder C-Level-Intrinsics verwenden), sind Sie es Es ist weitaus besser, wenn der Compiler es für Sie herausfindet.

David Hoelzer
quelle
1
Tatsächlich ist die Verwendung von rep movsd mit erweitertem rep movsb langsamer. Bitte lesen Sie, was diese Funktion bedeutet, bevor Sie solche Antworten schreiben.
Fuz
1
Ich habe memcpy hier einen Brauch besprochen . Ein Kommentar lautet: "Beachten Sie, dass Sie auf Ivybridge und Haswell mit zu großen Puffern, die in MLC passen, movntdqa mit rep movsb schlagen können; movntdqa verursacht eine RFO in LLC, rep movsb nicht." Ich kann etwas so Gutes bekommen wie memcpymit movntdqa. Meine Frage ist, wie ich so gut oder besser damit rep movsbumgehen kann.
Z Boson
2
Dies ist hauptsächlich für die Bildung. Ich versuche, etwas über ERMSB zu lernen. Das Endziel ist es, die höchstmögliche Bandbreite aus dem Hauptspeicher zu erhalten. Ich habe den Code in meiner Frage angegeben, den ich verwende. Das ist alles was ich tue.
Z Boson
3
Diese Antwort scheint nicht mit den Realitäten von "Fast String Move" -Anweisungen wie ERMSB in Verbindung zu stehen und wiederholt den Irrtum, dass Sie den Compiler für den Code mit der höchsten Leistung für Sie herausfinden lassen sollten. Jetzt gewährt, für die meisten Codes, und die meisten Entwickler, um hohe Leistung Code erhalten Sie sollten den Compiler Figur es für Sie heraus lassen, aber es ist fast immer eine Ebene über die eine Person sehr versiert in den Details kann es schneller machen (zB , weil sie mehr über die Form der Daten wissen usw.). Die Frage fällt in diese Kategorie, da sie explizit die schnellen String-
Operationen
4
@fuz: Eigentlich ist auf allen aktuellen CPUs, die ERMSB implementieren, rep movsdanscheinend auch schnell . (Auch wenn Sie Recht haben, dass Intel ERMSB nur als Bewerbung für rep movsdb/ dokumentiert stosb)
Peter Cordes
1

Als allgemeine memcpy()Richtlinie:

a) Wenn die zu kopierenden Daten winzig sind (weniger als 20 Byte) und eine feste Größe haben, lassen Sie den Compiler dies tun. Grund: Der Compiler kann normale movAnweisungen verwenden und den Startaufwand vermeiden.

b) Wenn die zu kopierenden Daten klein sind (weniger als 4 KB) und garantiert ausgerichtet sind, verwenden Sie rep movsb(wenn ERMSB unterstützt wird) oder rep movsd(wenn ERMSB nicht unterstützt wird). Grund: Die Verwendung einer SSE- oder AVX-Alternative hat einen enormen "Startaufwand", bevor etwas kopiert wird.

c) Wenn die zu kopierenden Daten klein sind (weniger als etwa 4 KB) und nicht garantiert sind, dass sie ausgerichtet sind, verwenden Sie rep movsb. Grund: Die Verwendung von SSE oder AVX oder die Verwendung rep movsdfür den Großteil davon plus einige rep movsbam Anfang oder Ende hat zu viel Overhead.

d) Verwenden Sie für alle anderen Fälle Folgendes:

Grund: Dies ist so langsam, dass Programmierer gezwungen sind, eine Alternative zu finden, bei der keine großen Datenmengen kopiert werden müssen. und die resultierende Software wird erheblich schneller sein, da das Kopieren großer Datenmengen vermieden wurde.

Brendan
quelle
2
"Die Verwendung einer SSE- oder AVX-Alternative hat einen enormen" Startaufwand ", bevor etwas kopiert wird." Was ist dieser enorme Startaufwand, auf den Sie sich beziehen? Können Sie mehr Details dazu geben?
Z Boson
2
@Zboson: Überprüfen, ob die Startadresse geeignet ausgerichtet ist / nicht (sowohl für Quelle als auch für Ziel), ob die Größe ein schönes Vielfaches ist, ob rep movsbsie trotzdem verwendet werden sollte usw. (alle mit potenziellen Fehlvorhersagen für Zweige). Bei den meisten CPUs ist SSE / AVX ausgeschaltet, um Strom zu sparen, wenn Sie es nicht verwenden, sodass Sie von der "SSE / AVX-Einschaltlatenz" betroffen sein können. Dann Overhead für Funktionsaufrufe (zu aufgebläht, um inline zu sein), einschließlich des Speicherns / Wiederherstellens aller SSE / AVX-Register, die vom Anrufer verwendet wurden. Wenn nichts anderes SSE / AVX verwendet, wird der SSE / AVX-Status während des Taskwechsels zusätzlich gespeichert / wiederhergestellt.
Brendan
1
@ Zboson: Auch; wenn die Leute klug wäre , würde sie mehrere Varianten haben, wie memcpy_small(), memcpy_large_unaligned(), memcpy_large_aligned()usw. Dies würde dazu beitragen , einige der Startkopf loszuwerden (die Prüfung, etc). Leider sind die Leute eher faul als schlau und (soweit ich das beurteilen kann) macht das eigentlich niemand.
Brendan
2
@BeeOnRope: Beide Kommentare waren an @ Brendan gerichtet, da ich mit dieser Antwort nicht einverstanden war. Entschuldigen Sie die Verwirrung, ich habe Sie nur angerufen, falls Sie daran interessiert waren, ein Beispiel dafür zu sehen, worüber Sie in einem früheren Kommentar über den Startaufwand für ein Vektor-Memcpy gesprochen haben, das niedrig ist, um nichts zu widersprechen, was Sie gesagt haben.
Peter Cordes
2
@CodyGray - In der Praxis sind die Überlegungen zur Ausrichtung für rep movsbund rep movsd(und rep movsq) auf neuerer Hardware meistens gleich . Sicher, funktioniert rep movsb konzeptionell mit Bytes, aber unter dem Deckmantel versuchen alle Anweisungen zum Verschieben von Zeichenfolgen, größere Teile von Bytes zu verschieben, damit sie alle von einer besseren Ausrichtung profitieren (und diese vorteilhafte Ausrichtung beträgt normalerweise 16, 32 oder 64 Bytes, ist also nicht wirklich verwandt zu den primitiven Größen der Operationen). Es ist ähnlich, wie memcpyImplementierungen im Allgemeinen von der Ausrichtung profitieren, obwohl sie konzeptionell mit Bytes arbeiten.
BeeOnRope