Abbiamo testato un server utilizzando 2 CPU Xeon Gold 6154 con una scheda madre Supermicro X11DPH-I e 96 GB di RAM, e abbiamo riscontrato alcuni strani problemi di prestazioni nella memoria rispetto al funzionamento con solo 1 CPU (un socket vuoto), simile doppio CPU Haswell Xeon E5-2687Wv3 (per questa serie di test, ma altri Broadwell funzionano in modo simile), Broadwell-E i7 e Skylake-X i9 (per confronto).
Ci si aspetterebbe che i processori Skylake Xeon con memoria più veloce funzionassero più velocemente di Haswell quando si tratta di varie funzioni memcpy e persino allocazione della memoria (non trattata nei test di seguito, come abbiamo trovato una soluzione alternativa), ma invece con entrambe le CPU installate , gli Skylake Xeons si comportano a quasi la metà della velocità degli Haswell Xeons e ancor meno se confrontati con un i7-6800k. Ciò che è ancora più strano, è quando si utilizza Windows VirtualAllocExNuma per assegnare il nodo NUMA per l'allocazione della memoria, mentre le funzioni di copia della memoria normale hanno prestazioni peggiori sul nodo remoto rispetto al nodo locale, le funzioni di copia della memoria che utilizzano i registri SSE, MMX e AVX offrono molto più veloce sul nodo NUMA remoto rispetto al nodo locale (cosa?). Come notato sopra, con Skylake Xeons,
Non sono sicuro che si tratti di un bug sulla scheda madre o sulla CPU, o con UPI vs QPI, o nessuna delle precedenti, ma nessuna combinazione di impostazioni del BIOS sembra avvalersi di questo. La disabilitazione di NUMA (non inclusa nei risultati dei test) nel BIOS migliora le prestazioni di tutte le funzioni di copia utilizzando i registri SSE, MMX e AVX, ma anche tutte le altre funzioni di copia di memoria normale subiscono grandi perdite.
Per il nostro programma di test, abbiamo testato sia usando le funzioni di assemblaggio in linea, sia _mm
intrinseco, abbiamo usato Windows 10 con Visual Studio 2017 per tutto tranne le funzioni di assemblaggio, che come msvc ++ non compilerà asm per x64, abbiamo usato gcc da mingw / msys a compila un file obj usando -c -O2
flags, che abbiamo incluso nel linker msvc ++.
Se il sistema utilizza nodi NUMA, testiamo entrambi gli operatori nuovi per l'allocazione della memoria con VirtualAllocExNuma per ciascun nodo NUMA ed eseguiamo una media cumulativa di 100 copie del buffer di memoria di 16 MB ciascuna per ciascuna funzione di copia della memoria e ruotiamo su quale allocazione di memoria ci troviamo tra ogni serie di test.
Tutti i 100 buffer di origine e 100 di destinazione sono allineati a 64 byte (per compatibilità fino a AVX512 mediante le funzioni di streaming) e inizializzati una volta su dati incrementali per i buffer di origine e 0xff per i buffer di destinazione.
Il numero di copie mediate su ogni macchina con ogni configurazione variava, poiché era molto più veloce su alcuni e molto più lento su altri.
I risultati sono stati i seguenti:
Haswell Xeon E5-2687Wv3 1 CPU (1 socket vuoto) su Supermicro X10DAi con DDR4-2400 da 32 GB (10c / 20t, 25 MB di cache L3). Ma ricorda, il benchmark ruota attraverso 100 coppie di buffer da 16 MB, quindi probabilmente non stiamo ottenendo hit della cache 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 cpu su Supermicro X10DAi con ram da 64 GB
---------------------------------------------------------------------------
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 socket vuoto) su Supermicro X11DPH-I con 48 GB DDR4-2666 ( 18c / 36t, 24,75 MB di cache 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 su Supermicro X11DPH-I con DDR4-2666 da 96 GB
---------------------------------------------------------------------------
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 su ASUS ROG Rampage VI Extreme con DDR4-4266 da 32 GB (14c / 28t, 19,25 MB di cache L3) (overcloccato a 3,8 GHz / 4,4 GHz turbo, DDR a 4040 MHz, Target AVX Frequenza 3737 MHz, Target AVX- 512 Frequenza 3535 MHz, frequenza cache target 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 su ASUS X99 con DDR4-2400 da 24 GB (6c / 12t, 15 MB di cache 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
Le funzioni di assemblaggio sono derivate da fast_memcpy in xine-libs, usate principalmente solo per confrontare con l'ottimizzatore di msvc ++.
Il codice sorgente per il test è disponibile su https://github.com/marcmicalizzi/memcpy_test (è un po 'lungo da inserire nel post)
Qualcun altro ha incontrato questo o qualcuno ha qualche idea sul perché questo potrebbe accadere?
Aggiornamento 15/05/2018 13: 40EST
Quindi, come suggerito da Peter Cordes, ho aggiornato il test per confrontare prefetched e non prefetched, e i negozi NT e quelli normali, e ho ottimizzato il prefetching eseguito in ciascuna funzione ( non ho alcuna esperienza significativa con la scrittura del prefetching, quindi se Sto commettendo errori con questo, per favore fatemi sapere e regolerò i test di conseguenza. Il prefetching ha un impatto, quindi almeno sta facendo qualcosa ). Queste modifiche si riflettono nell'ultima revisione dal collegamento GitHub che ho apportato in precedenza per chiunque cerchi il codice sorgente.
Ho anche aggiunto un memcpy SSE4.1, dal momento che prima di SSE4.1 io non riesco a trovare alcuna _mm_stream_load
(io in particolare usato _mm_stream_load_si128
) funzioni SSE, in modo sse_memcpy
e sse2_memcpy
non può essere completamente con negozi NT, e così la avx_memcpy
funzione utilizza funzioni AVX2 per il caricamento dello stream.
Ho deciso di non fare ancora un test per i modelli di accesso puro al negozio e al carico puro, poiché non sono sicuro che il negozio puro possa essere significativo, poiché senza un carico per i registri a cui accede, i dati sarebbero privi di significato e non verificabili.
I risultati interessanti con il nuovo test sono stati che sulla configurazione Xeon Skylake Dual Socket e solo su quella configurazione, le funzioni di archiviazione erano in realtà significativamente più veloci delle funzioni di streaming NT per la copia della memoria da 16 MB. Anche solo su quella configurazione (e solo con LLC prefetch abilitato nel BIOS), prefetchnta in alcuni test (SSE, SSE4.1) supera sia prefetcht0 che nessun prefetch.
I risultati non elaborati di questo nuovo test sono troppo lunghi per essere aggiunti al post, quindi vengono pubblicati nello stesso repository git del codice sorgente in results-2018-05-15
Non capisco ancora perché per lo streaming di negozi NT, il nodo NUMA remoto sia più veloce con l'installazione SMP di Skylake, anche se l'utilizzo di negozi regolari è ancora più veloce di quello sul nodo NUMA locale
prefetchnta
e negozi NT! È un fatto molto importante che hai lasciato fuori dalla tua domanda! Vedi Enhanced REP MOVSB per memcpy per ulteriori discussioni sui rep movsb
negozi vettoriali ERMSB vs. NT rispetto ai normali negozi vettoriali. Fare casino con quello sarebbe più utile di MMX vs. SSE. Probabilmente basta usare AVX e / o AVX512 e provare NT contro normale e / o tralasciando il prefetch SW.
prefetchnta
ignora L3 e L2 (perché L3 non è inclusivo), quindi è più sensibile alla distanza di prefetch (troppo tardi e i dati devono arrivare di nuovo dalla DRAM, non solo L3), quindi è più "fragile" ( sensibile alla regolazione della giusta distanza). Le distanze di prefetch sembrano piuttosto basse, tuttavia, sotto i 500 byte se sto leggendo l'asm correttamente. I test di @ Mysticial su SKX hanno scoperto che prefetchnta
può essere un grande rallentamento su quel Uarch ), e lui non lo consiglia.