Unerwartete und ungeklärte langsame (und ungewöhnliche) Speicherleistung mit Xeon Skylake SMP

27

Wir haben einen Server mit 2x Xeon Gold 6154-CPUs mit einem Supermicro X11DPH-I-Motherboard und 96 GB RAM getestet und einige sehr merkwürdige Leistungsprobleme im Vergleich zum Betrieb mit nur 1 CPU (ein Sockel ist leer) festgestellt, ähnlich wie bei zwei CPUs CPU Haswell Xeon E5-2687Wv3 (für diese Testreihe, aber andere Broadwells erzielen ähnliche Ergebnisse), Broadwell-E i7s und Skylake-X i9s (zum Vergleich).

Es ist zu erwarten, dass die Skylake Xeon-Prozessoren mit schnellerem Arbeitsspeicher in Bezug auf verschiedene Speicherfunktionen und sogar die Speicherzuweisung (in den folgenden Tests nicht behandelt, da wir eine Problemumgehung gefunden haben) eine schnellere Leistung erbringen als die Haswell-Prozessoren, jedoch mit beiden installierten CPUs Die Skylake-Xeons sind fast halb so schnell wie die Haswell-Xeons, und im Vergleich zu einem i7-6800k sogar noch weniger. Was noch seltsamer ist, ist die Verwendung von Windows VirtualAllocExNuma zur Zuweisung des NUMA-Knotens für die Speicherzuweisung, während einfache Speicherkopierfunktionen auf dem Remoteknoten gegenüber dem lokalen Knoten voraussichtlich schlechter abschneiden, während Speicherkopierfunktionen, die die SSE-, MMX- und AVX-Register verwenden, viel bewirken schneller auf dem entfernten NUMA-Knoten als auf dem lokalen Knoten (was?). Wie oben erwähnt, mit Skylake Xeons,

Ich bin nicht sicher, ob dies ein Fehler auf dem Motherboard oder der CPU ist oder mit UPI gegen QPI oder keiner der oben genannten, aber keine Kombination von BIOS-Einstellungen scheint dies zu nützen. Das Deaktivieren von NUMA (nicht in den Testergebnissen enthalten) im BIOS verbessert die Leistung aller Kopierfunktionen unter Verwendung der SSE-, MMX- und AVX-Register, aber auch alle anderen Kopierfunktionen für den einfachen Speicher leiden unter großen Verlusten.

Für unser Testprogramm haben wir sowohl Inline-Assembler-Funktionen als _mmauch Windows 10 mit Visual Studio 2017 verwendet, mit Ausnahme der Assembler-Funktionen, bei denen msvc ++ asm für x64 nicht kompiliert, sondern gcc von mingw / msys bis Kompilieren Sie eine obj-Datei mit -c -O2Flags, die wir in den msvc ++ - Linker aufgenommen haben.

Wenn das System NUMA-Knoten verwendet, testen wir beide Operatoren neu auf Speicherzuordnung mit VirtualAllocExNuma für jeden NUMA-Knoten und machen einen kumulativen Durchschnitt von 100 Speicherpufferkopien von jeweils 16 MB für jede Speicherkopierfunktion, und wir drehen, auf welcher Speicherzuordnung wir sind zwischen den einzelnen Tests.

Alle 100 Quell- und 100 Zielpuffer sind auf 64 Byte ausgerichtet (aus Gründen der Kompatibilität mit AVX512 mithilfe von Streaming-Funktionen) und werden einmal initialisiert, um Daten für die Quellpuffer und 0xff für die Zielpuffer zu inkrementieren.

Die Anzahl der Kopien, die auf jedem Gerät mit jeder Konfiguration gemittelt wurden, variierte, da sie auf einigen Geräten viel schneller und auf anderen Geräten viel langsamer waren.

Die Ergebnisse waren wie folgt:

Haswell Xeon E5-2687Wv3 1 CPU (1 leerer Sockel) auf Supermicro X10DAi mit 32 GB DDR4-2400 ( 10 c / 20 t , 25 MB L3-Cache). Denken Sie jedoch daran, dass sich der Benchmark durch 100 Paare von 16-MB-Puffern dreht, sodass wir wahrscheinlich keine L3-Cache-Treffer erhalten.

---------------------------------------------------------------------------
Averaging 7000 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 2264.48 microseconds
asm_memcpy (asm)                 averaging 2322.71 microseconds
sse_memcpy (intrinsic)           averaging 1569.67 microseconds
sse_memcpy (asm)                 averaging 1589.31 microseconds
sse2_memcpy (intrinsic)          averaging 1561.19 microseconds
sse2_memcpy (asm)                averaging 1664.18 microseconds
mmx_memcpy (asm)                 averaging 2497.73 microseconds
mmx2_memcpy (asm)                averaging 1626.68 microseconds
avx_memcpy (intrinsic)           averaging 1625.12 microseconds
avx_memcpy (asm)                 averaging 1592.58 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 2260.6 microseconds

Haswell Dual Xeon E5-2687Wv3 2 CPU auf Supermicro X10DAi mit 64 GB RAM

---------------------------------------------------------------------------
Averaging 6900 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 0(local)
---------------------------------------------------------------------------
std::memcpy                      averaging 3179.8 microseconds
asm_memcpy (asm)                 averaging 3177.15 microseconds
sse_memcpy (intrinsic)           averaging 1633.87 microseconds
sse_memcpy (asm)                 averaging 1663.8 microseconds
sse2_memcpy (intrinsic)          averaging 1620.86 microseconds
sse2_memcpy (asm)                averaging 1727.36 microseconds
mmx_memcpy (asm)                 averaging 2623.07 microseconds
mmx2_memcpy (asm)                averaging 1691.1 microseconds
avx_memcpy (intrinsic)           averaging 1704.33 microseconds
avx_memcpy (asm)                 averaging 1692.69 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 3185.84 microseconds
---------------------------------------------------------------------------
Averaging 6900 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 1
---------------------------------------------------------------------------
std::memcpy                      averaging 3992.46 microseconds
asm_memcpy (asm)                 averaging 4039.11 microseconds
sse_memcpy (intrinsic)           averaging 3174.69 microseconds
sse_memcpy (asm)                 averaging 3129.18 microseconds
sse2_memcpy (intrinsic)          averaging 3161.9 microseconds
sse2_memcpy (asm)                averaging 3141.33 microseconds
mmx_memcpy (asm)                 averaging 4010.17 microseconds
mmx2_memcpy (asm)                averaging 3211.75 microseconds
avx_memcpy (intrinsic)           averaging 3003.14 microseconds
avx_memcpy (asm)                 averaging 2980.97 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 3987.91 microseconds
---------------------------------------------------------------------------
Averaging 6900 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 3172.95 microseconds
asm_memcpy (asm)                 averaging 3173.5 microseconds
sse_memcpy (intrinsic)           averaging 1623.84 microseconds
sse_memcpy (asm)                 averaging 1657.07 microseconds
sse2_memcpy (intrinsic)          averaging 1616.95 microseconds
sse2_memcpy (asm)                averaging 1739.05 microseconds
mmx_memcpy (asm)                 averaging 2623.71 microseconds
mmx2_memcpy (asm)                averaging 1699.33 microseconds
avx_memcpy (intrinsic)           averaging 1710.09 microseconds
avx_memcpy (asm)                 averaging 1688.34 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 3175.14 microseconds

Skylake Xeon Gold 6154 1 CPU (1 leerer Sockel) auf Supermicro X11DPH-I mit 48 GB DDR4-2666 (18 c / 36 t, 24,75 MB L3-Cache)

---------------------------------------------------------------------------
Averaging 5000 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 1832.42 microseconds
asm_memcpy (asm)                 averaging 1837.62 microseconds
sse_memcpy (intrinsic)           averaging 1647.84 microseconds
sse_memcpy (asm)                 averaging 1710.53 microseconds
sse2_memcpy (intrinsic)          averaging 1645.54 microseconds
sse2_memcpy (asm)                averaging 1794.36 microseconds
mmx_memcpy (asm)                 averaging 2030.51 microseconds
mmx2_memcpy (asm)                averaging 1816.82 microseconds
avx_memcpy (intrinsic)           averaging 1686.49 microseconds
avx_memcpy (asm)                 averaging 1716.15 microseconds
avx512_memcpy (intrinsic)        averaging 1761.6 microseconds
rep movsb (asm)                  averaging 1977.6 microseconds

Skylake Xeon Gold 6154 2 CPU auf Supermicro X11DPH-I mit 96 GB DDR4-2666

---------------------------------------------------------------------------
Averaging 4100 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 0(local)
---------------------------------------------------------------------------
std::memcpy                      averaging 3131.6 microseconds
asm_memcpy (asm)                 averaging 3070.57 microseconds
sse_memcpy (intrinsic)           averaging 3297.72 microseconds
sse_memcpy (asm)                 averaging 3423.38 microseconds
sse2_memcpy (intrinsic)          averaging 3274.31 microseconds
sse2_memcpy (asm)                averaging 3413.48 microseconds
mmx_memcpy (asm)                 averaging 2069.53 microseconds
mmx2_memcpy (asm)                averaging 3694.91 microseconds
avx_memcpy (intrinsic)           averaging 3118.75 microseconds
avx_memcpy (asm)                 averaging 3224.36 microseconds
avx512_memcpy (intrinsic)        averaging 3156.56 microseconds
rep movsb (asm)                  averaging 3155.36 microseconds
---------------------------------------------------------------------------
Averaging 4100 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 1
---------------------------------------------------------------------------
std::memcpy                      averaging 5309.77 microseconds
asm_memcpy (asm)                 averaging 5330.78 microseconds
sse_memcpy (intrinsic)           averaging 2350.61 microseconds
sse_memcpy (asm)                 averaging 2402.57 microseconds
sse2_memcpy (intrinsic)          averaging 2338.61 microseconds
sse2_memcpy (asm)                averaging 2475.51 microseconds
mmx_memcpy (asm)                 averaging 2883.97 microseconds
mmx2_memcpy (asm)                averaging 2517.69 microseconds
avx_memcpy (intrinsic)           averaging 2356.07 microseconds
avx_memcpy (asm)                 averaging 2415.22 microseconds
avx512_memcpy (intrinsic)        averaging 2487.01 microseconds
rep movsb (asm)                  averaging 5372.98 microseconds
---------------------------------------------------------------------------
Averaging 4100 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 3075.1 microseconds
asm_memcpy (asm)                 averaging 3061.97 microseconds
sse_memcpy (intrinsic)           averaging 3281.17 microseconds
sse_memcpy (asm)                 averaging 3421.38 microseconds
sse2_memcpy (intrinsic)          averaging 3268.79 microseconds
sse2_memcpy (asm)                averaging 3435.76 microseconds
mmx_memcpy (asm)                 averaging 2061.27 microseconds
mmx2_memcpy (asm)                averaging 3694.48 microseconds
avx_memcpy (intrinsic)           averaging 3111.16 microseconds
avx_memcpy (asm)                 averaging 3227.45 microseconds
avx512_memcpy (intrinsic)        averaging 3148.65 microseconds
rep movsb (asm)                  averaging 2967.45 microseconds

Skylake-X i9-7940X auf ASUS ROG Rampage VI Extreme mit 32 GB DDR4-4266 (14 c / 28 t, 19,25 MB L3-Cache) (auf 3,8 GHz / 4,4 GHz Turbo übertaktet, DDR bei 4040 MHz, AVX-Zielfrequenz 3737 MHz, AVX-Zielfrequenz 512 Frequenz 3535 MHz, Ziel-Cache-Frequenz 2424 MHz)

---------------------------------------------------------------------------
Averaging 6500 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 1750.87 microseconds
asm_memcpy (asm)                 averaging 1748.22 microseconds
sse_memcpy (intrinsic)           averaging 1743.39 microseconds
sse_memcpy (asm)                 averaging 3120.18 microseconds
sse2_memcpy (intrinsic)          averaging 1743.37 microseconds
sse2_memcpy (asm)                averaging 2868.52 microseconds
mmx_memcpy (asm)                 averaging 2255.17 microseconds
mmx2_memcpy (asm)                averaging 3434.58 microseconds
avx_memcpy (intrinsic)           averaging 1698.49 microseconds
avx_memcpy (asm)                 averaging 2840.65 microseconds
avx512_memcpy (intrinsic)        averaging 1670.05 microseconds
rep movsb (asm)                  averaging 1718.77 microseconds

Broadwell i7-6800k auf ASUS X99 mit 24 GB DDR4-2400 ( 6 c / 12 t , 15 MB L3-Cache)

---------------------------------------------------------------------------
Averaging 64900 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 2522.1 microseconds
asm_memcpy (asm)                 averaging 2615.92 microseconds
sse_memcpy (intrinsic)           averaging 1621.81 microseconds
sse_memcpy (asm)                 averaging 1669.39 microseconds
sse2_memcpy (intrinsic)          averaging 1617.04 microseconds
sse2_memcpy (asm)                averaging 1719.06 microseconds
mmx_memcpy (asm)                 averaging 3021.02 microseconds
mmx2_memcpy (asm)                averaging 1691.68 microseconds
avx_memcpy (intrinsic)           averaging 1654.41 microseconds
avx_memcpy (asm)                 averaging 1666.84 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 2520.13 microseconds

Die Assembly-Funktionen werden von fast_memcpy in xine-libs abgeleitet und hauptsächlich zum Vergleich mit dem Optimierer von msvc ++ verwendet.

Source Code für den Test finden Sie unter https://github.com/marcmicalizzi/memcpy_test (es ist ein bisschen zu lang in der Post zu setzen)

Hat jemand anderes darauf gestoßen oder hat jemand einen Einblick, warum dies passieren könnte?


Update 2018-05-15 13: 40EST

Wie von Peter Cordes vorgeschlagen, habe ich den Test aktualisiert, um vorabgerufene mit nicht vorabgerufenen und NT - Speicher mit regulären Speichern zu vergleichen, und den Vorababruf für jede Funktion optimiert ( ich habe keine bedeutende Erfahrung mit dem Schreiben von Vorababrufen, also wenn Ich mache hier Fehler, lass es mich wissen und ich passe die Tests dementsprechend an. Das Prefetching hat Auswirkungen, also macht es zumindest etwas . Diese Änderungen spiegeln sich in der neuesten Überarbeitung des GitHub-Links wider, den ich zuvor für alle Benutzer erstellt habe, die nach dem Quellcode suchen.

Ich habe auch eine SSE4.1- Memcpy, da vor SSE4.1- kann ich nicht finden keine hinzugefügt _mm_stream_load(I speziell verwendet _mm_stream_load_si128) SSE - Funktionen, so sse_memcpyund sse2_memcpykann nicht vollständig mit NT speichert, und auch die avx_memcpyFunktion verwendet AVX2 Funktionen zum Laden von Streams.

Ich habe mich entschieden, noch keinen Test für reine Speicher- und reine Ladezugriffsmuster durchzuführen, da ich nicht sicher bin, ob der reine Speicher sinnvoll sein könnte, da die Daten ohne eine Belastung der Register, auf die zugegriffen wird, bedeutungslos und nicht überprüfbar wären.

Die interessanten Ergebnisse mit dem neuen Test waren, dass beim Xeon Skylake Dual Socket-Setup und nur bei diesem Setup die Speicherfunktionen tatsächlich erheblich schneller waren als die NT-Streaming-Funktionen für das Kopieren von 16 MB Speicher. Auch nur in diesem Setup (und nur bei aktiviertem LLC-Prefetch im BIOS) übertrifft Prefetchnta in einigen Tests (SSE, SSE4.1) sowohl Prefetcht0 als auch No Prefetch.

Die rohen Ergebnisse dieses neuen Tests sind zu lang, um sie zum Beitrag hinzuzufügen, sodass sie im selben Git-Repository wie der Quellcode unter veröffentlicht werden results-2018-05-15

Ich verstehe immer noch nicht, warum der Remote-NUMA-Knoten beim Streamen von NT-Speichern im Skylake-SMP-Setup schneller ist, obwohl die Verwendung von regulären Speichern immer noch schneller ist als die des lokalen NUMA-Knotens

Marc Micalizzi
quelle
1
Sie haben noch keine Gelegenheit gehabt, Ihre Daten zu verdauen, aber sehen Sie auch, warum Skylake beim Single-Threaded-Speicherdurchsatz so viel besser ist als Broadwell-E? (Vergleichen eines Quad-Core-Skylake mit einem Multicore-Broadwell und Erkennen der Nachteile einer höheren Speicher- / L3-Latenz in Multicore-Systemen, bei denen die Single-Core-Bandbreite durch die maximale Speicherparallelität in einem Core und nicht durch DRAM-Controller begrenzt ist.) Laut Tests von Mysticial und anderen Ergebnissen weist SKX im Allgemeinen eine hohe Latenz / niedrige Bandbreite pro Kern zu L3 / Speicher auf. Das sehen Sie wahrscheinlich.
Peter Cordes
1
Verwenden einige Ihrer Kopien NT-Speicher? Ich habe gerade nachgesehen, und alle Ihre Kopien außer MMX verwenden prefetchntaund NT speichert! Das ist eine sehr wichtige Tatsache, die Sie aus Ihrer Frage herausgelassen haben! Weitere Informationen zu ERMSB rep movsbvs. NT-Vektorspeichern vs. regulären Vektorspeichern finden Sie unter Verbessertes REP-MOVSB ​​für memcpy . Hier herumzuspielen wäre nützlicher als MMX vs. SSE. Verwenden Sie wahrscheinlich nur AVX und / oder AVX512 und probieren Sie NT vs. Regular aus und / oder lassen Sie den SW-Prefetch weg.
Peter Cordes
1
Haben Sie den Prefetch-Abstand für Ihre SKX-Maschinen eingestellt? SKX prefetchntaumgeht sowohl L3 als auch L2 (da L3 nicht inklusive ist), daher ist es empfindlicher für den Prefetch-Abstand (zu spät und die Daten müssen wieder vollständig aus dem DRAM kommen, nicht nur L3), daher ist es "spröder" ( empfindlich auf die richtige Entfernung). Ihre Prefetch-Entfernungen sehen jedoch unter 500 Bytes recht niedrig aus, wenn ich den asm richtig lese. @ Mysticials Tests mit SKX haben ergeben, dass prefetchntadies eine große Verlangsamung für diesen Uarch sein kann , und er empfiehlt es nicht.
Peter Cordes
1
Sie haben hier definitiv einige interessante Ergebnisse, aber wir müssen sie von verschiedenen Effekten entwirren . Wenn Sie Nummern mit und ohne NT-Speicher haben, können Sie möglicherweise nützliche Informationen zum NUMA-Verhalten erhalten. Das Auffüllen eines zweiten Sockels zwingt sogar lokale L3-Fehler, die Remote-CPU zumindest in Broadwell / Haswell auszuloten. Dual-Socket-E5-Xeons haben keinen Snoop-Filter. Ich denke , dass Gold - Xeons tun haben Snoop - Filter, weil sie fähig sind in mehr als Dual-Socket - Systeme arbeiten. Aber ich bin mir nicht sicher, wie groß es ist oder was das wirklich bedeutet: PI hat die Speicherperfektionierung für Multi-Sockets nicht durchgeführt.
Peter Cordes
2
SKX ist eine grundlegend andere Verbindung. ein Netz anstelle eines Rings. Es ist ein interessantes Ergebnis, aber nicht unglaublich und möglicherweise kein Zeichen einer Fehlkonfiguration. IDK, hoffentlich kann jemand anderes mit mehr Erfahrung mit der Hardware mehr Licht ins Dunkel bringen.
Peter Cordes

Antworten:

0

Ist dein Gedächtnis der falsche Rang? Vielleicht hat Ihr Board etwas Seltsames mit dem Speicher-Ranking, wenn Sie diese zweite CPU hinzufügen? Ich weiß, wenn Sie Quad-CPU-Maschinen haben, tun sie alle möglichen seltsamen Dinge, damit der Speicher richtig funktioniert, und wenn Sie den falsch eingestuften Speicher haben, funktioniert er manchmal, aber takten Sie zurück, um 1/4 oder 1/2 der Geschwindigkeit zu mögen. Vielleicht hat SuperMicro auf dieser Platine etwas getan, um DDR4 und Dual-CPU in Quad-Channel umzuwandeln, und es wird eine ähnliche Mathematik verwendet. Falscher Rang == 1/2 Geschwindigkeit.

der Waldläufer
quelle
Scheint nicht der Fall zu sein, der gesamte Speicher ist 1R8 und entspricht dem Rang des Supermicro-Qvl für das Motherboard. War aber einen Scheck wert!
Marc Micalizzi
Ich weiß, dass dies ein völlig anderes System ist, aber das ist es auch, worauf ich mich bezog. qrl.dell.com/Files/en-us/Html/Manuals/R920/… Sie werden feststellen, dass sich die Ranganforderungen ändern, wenn Sie die Anzahl der Sticks / CPUs erhöhen.
TheLanRanger