Testowaliśmy serwer przy użyciu 2 procesorów Xeon Gold 6154 z płytą główną Supermicro X11DPH-I i 96 GB pamięci RAM. Znaleźliśmy kilka bardzo dziwnych problemów związanych z wydajnością pamięci w porównaniu do działania z tylko 1 procesorem (jedno gniazdo puste), podobny podwójny Procesor Haswell Xeon E5-2687Wv3 (dla tej serii testów, ale inne Broadwell działają podobnie), Broadwell-E i7s i Skylake-X i9s (dla porównania).
Można się spodziewać, że procesory Skylake Xeon z szybszą pamięcią działałyby szybciej niż Haswell, jeśli chodzi o różne funkcje memcpy, a nawet alokację pamięci (nie omówione w poniższych testach, jak znaleźliśmy obejście), ale zamiast tego z zainstalowanymi obydwoma procesorami , Skylake Xeons działają prawie o połowę szybciej niż Haswell Xeons, a nawet mniej w porównaniu do i7-6800k. Jeszcze dziwniejsze jest to, że przy użyciu Windows VirtualAllocExNuma do przypisania węzła NUMA do alokacji pamięci, podczas gdy funkcje zwykłego kopiowania pamięci prawdopodobnie działają gorzej w węźle zdalnym niż w węźle lokalnym, funkcje kopiowania pamięci wykorzystujące rejestry SSE, MMX i AVX wykonują wiele szybciej na zdalnym węźle NUMA niż na węźle lokalnym (co?). Jak wspomniano powyżej, w przypadku Skylake Xeons
Nie jestem pewien, czy jest to błąd na płycie głównej lub procesorze, czy z UPI vs QPI, czy też z żadnym z powyższych, ale wydaje się, że żadna kombinacja ustawień BIOS-u nie przydaje się. Wyłączenie NUMA (nieuwzględnione w wynikach testu) w biosie poprawia wydajność wszystkich funkcji kopiowania przy użyciu rejestrów SSE, MMX i AVX, ale wszystkie inne funkcje kopiowania zwykłej pamięci również ponoszą duże straty.
W naszym programie testowym testowaliśmy zarówno przy użyciu wbudowanych funkcji a _mm
wbudowanych, jak i Windows 10 z Visual Studio 2017 do wszystkiego oprócz funkcji asemblacji, które jako msvc ++ nie skompilują asm dla x64, użyliśmy gcc z mingw / msys do skompiluj plik obj przy użyciu -c -O2
flag, które zawarliśmy w linkerze msvc ++.
Jeśli system używa węzłów NUMA, testujemy obu operatorów jako nowych pod kątem alokacji pamięci za pomocą VirtualAllocExNuma dla każdego węzła NUMA i wykonujemy skumulowaną średnią 100 kopii bufora pamięci po 16 MB dla każdej funkcji kopiowania pamięci, i obracamy, na której alokacji pamięci się znajdujemy między każdym zestawem testów.
Wszystkie 100 buforów źródłowych i 100 docelowych jest wyrównanych do 64 bajtów (w celu zapewnienia zgodności z AVX512 przy użyciu funkcji przesyłania strumieniowego) i zainicjowanych jeden raz dla danych przyrostowych dla buforów źródłowych i 0xff dla buforów docelowych.
Liczba kopii uśredniana na każdym komputerze dla każdej konfiguracji była różna, ponieważ na niektórych była znacznie szybsza, a na innych wolniejsza.
Wyniki były następujące:
Haswell Xeon E5-2687Wv3 1 CPU (1 puste gniazdo) w Supermicro X10DAi z 32 GB pamięci DDR4-2400 (10c / 20t, 25 MB pamięci podręcznej L3). Pamiętaj jednak, że test porównawczy obraca się przez 100 par buforów 16 MB, więc prawdopodobnie nie otrzymujemy trafień pamięci podręcznej L3.
---------------------------------------------------------------------------
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 procesory na Supermicro X10DAi z 64 GB pamięci 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 procesor (1 puste gniazdo) na Supermicro X11DPH-I z 48 GB pamięci DDR4-2666 (18c / 36t, 24,75 MB pamięci podręcznej L3)
---------------------------------------------------------------------------
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 na Supermicro X11DPH-I z 96 GB pamięci 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 na ASUS ROG Rampage VI Extreme z 32 GB pamięci DDR4-4266 (14c / 28t, 19,25 MB pamięci podręcznej L3) (podkręcona do turbo 3,8 GHz / 4,4 GHz, DDR przy 4040 MHz, docelowa częstotliwość AVX 3737 MHz, docelowa AVX- 512 Częstotliwość 3535 MHz, docelowa częstotliwość pamięci podręcznej 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 na ASUS X99 z 24 GB pamięci DDR4-2400 (6c / 12t, 15 MB pamięci podręcznej L3)
---------------------------------------------------------------------------
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
Funkcje asemblera pochodzą z fast_memcpy w xine-libs, najczęściej używane tylko do porównania z optymalizatorem msvc ++.
Kod źródłowy testu jest dostępny na https://github.com/marcmicalizzi/memcpy_test (wpis jest nieco długi)
Czy ktoś jeszcze na to wpadł, czy ktoś ma wgląd w to, dlaczego tak się dzieje?
Aktualizacja 2018-05-15 13: 40EST
Tak jak zasugerował Peter Cordes, zaktualizowałem test, aby porównać pobieranie z wyprzedzeniem i bez pobierania z wyprzedzeniem, i sklepy NT z normalnymi sklepami, i dostroiłem pobieranie wstępne wykonane w każdej funkcji ( nie mam żadnego znaczącego doświadczenia z pisaniem pobierania z wyprzedzeniem, więc jeśli Popełniam z tym jakiekolwiek błędy, proszę dać mi znać, a odpowiednio dostosuję testy. Pobieranie wstępne ma wpływ, więc przynajmniej coś robi ). Zmiany te znajdują odzwierciedlenie w najnowszej wersji linku GitHub, który zrobiłem wcześniej dla każdego, kto szuka kodu źródłowego.
Ja również dodany memcpy SSE4.1, ponieważ przed SSE4.1 nie mogę znaleźć żadnych _mm_stream_load
(I wykorzystywane specjalnie _mm_stream_load_si128
) funkcje SSE, więc sse_memcpy
i sse2_memcpy
nie może być całkowicie korzystania ze sklepów NT i jak dobrze avx_memcpy
funkcja wykorzystuje funkcje AVX2 do ładowania strumienia.
Zdecydowałem się nie przeprowadzać jeszcze testu wzorców dostępu do czystego magazynu i czystego ładowania, ponieważ nie jestem pewien, czy czysty sklep może mieć znaczenie, ponieważ bez obciążenia rejestrów, do których uzyskuje dostęp, dane byłyby bez znaczenia i niemożliwe do zweryfikowania.
Ciekawe wyniki nowego testu były takie, że w konfiguracji Xeon Skylake Dual Socket i tylko w tej konfiguracji funkcje sklepu były znacznie szybsze niż funkcje przesyłania strumieniowego NT w przypadku kopiowania pamięci 16 MB. Również tylko w tej konfiguracji (i tylko z włączonym pobieraniem wstępnym LLC w BIOSie), prefetchnta w niektórych testach (SSE, SSE4.1) przewyższa zarówno prefetcht0, jak i brak pobierania wstępnego.
Nieprzetworzone wyniki tego nowego testu są zbyt długie, aby można je było dodać do postu, dlatego są one publikowane w tym samym repozytorium git, co kod źródłowy w obszarze results-2018-05-15
Nadal nie rozumiem, dlaczego w przypadku przesyłania strumieniowego sklepów NT zdalny węzeł NUMA jest szybszy w konfiguracji Skylake SMP, chociaż korzystanie ze zwykłych sklepów jest nadal szybsze niż w lokalnym węźle NUMA
prefetchnta
i NT sklepów! To bardzo ważny fakt, który pominąłeś w swoim pytaniu! Zobacz Encanced REP MOVSB dla memcpy, aby uzyskać więcej informacji na temat ERMSB rep movsb
vs. NT sklepów wektorowych vs. zwykłych sklepów wektorowych. Zrozumienie tego byłoby bardziej przydatne niż MMX vs. SSE. Prawdopodobnie po prostu użyj AVX i / lub AVX512 i wypróbuj NT kontra zwykłe i / lub pomijając wstępne pobieranie SW.
prefetchnta
omija zarówno L3, jak i L2 (ponieważ L3 nie obejmuje), więc jest bardziej wrażliwy na odległość pobierania z wyprzedzeniem (za późno, a dane muszą znów pochodzić z DRAM, nie tylko L3), więc jest bardziej „kruchy” ( wrażliwy na strojenie właściwej odległości). Twoje odległości pobierania wstępnego wydają się jednak dość niskie, poniżej 500 bajtów, jeśli poprawnie czytam asm. Testy @ Mysticial na SKX wykazały, że prefetchnta
może to być spowolnienie dla tego uarcha ), i nie zaleca tego.