Rendimiento de memoria lento (e inusual) inesperado e inexplicable con Xeon Skylake SMP


27

Hemos estado probando un servidor con 2 CPU Xeon Gold 6154 con una placa base Supermicro X11DPH-I y 96 GB de RAM, y encontramos algunos problemas de rendimiento muy extraños que rodean la memoria en comparación con solo 1 CPU (un socket vacío), doble similar CPU Haswell Xeon E5-2687Wv3 (para esta serie de pruebas, pero otras Broadwell tienen un rendimiento similar), Broadwell-E i7 y Skylake-X i9 (para comparación).

Se esperaría que los procesadores Skylake Xeon con memoria más rápida funcionen más rápido que Haswell cuando se trata de varias funciones de memoria e incluso asignación de memoria (no se cubre en las pruebas a continuación, ya que encontramos una solución alternativa), sino con ambas CPU instaladas , los Skylake Xeons funcionan a casi la mitad de la velocidad que los Haswell Xeons, y aún menos en comparación con un i7-6800k. Lo que es aún más extraño es cuando se usa Windows VirtualAllocExNuma para asignar el nodo NUMA para la asignación de memoria, mientras que las funciones de copia de memoria normal se desempeñan peor en el nodo remoto frente al nodo local, las funciones de copia de memoria que utilizan los registros SSE, MMX y AVX realizan mucho más rápido en el nodo remoto NUMA que en el nodo local (¿qué?). Como se señaló anteriormente, con Skylake Xeons,

No estoy seguro de si esto es un error en la placa base o la CPU, o con UPI vs QPI, o ninguno de los anteriores, pero ninguna combinación de configuraciones de BIOS parece aprovechar esto. La desactivación de NUMA (no incluida en los resultados de la prueba) en la BIOS mejora el rendimiento de todas las funciones de copia utilizando los registros SSE, MMX y AVX, pero todas las demás funciones de copia de memoria simple también sufren grandes pérdidas.

Para nuestro programa de prueba, probamos tanto el uso de funciones de ensamblaje en línea como _mmintrínsecas, utilizamos Windows 10 con Visual Studio 2017 para todo excepto las funciones de ensamblaje, que como msvc ++ no compilará asm para x64, utilizamos gcc de mingw / msys para compila un archivo obj usando -c -O2banderas, que incluimos en el enlazador msvc ++.

Si el sistema está usando nodos NUMA, probamos ambos operadores nuevos para la asignación de memoria con VirtualAllocExNuma para cada nodo NUMA y hacemos un promedio acumulativo de 100 copias de memoria intermedia de 16MB cada una para cada función de copia de memoria, y rotamos en qué asignación de memoria estamos entre cada conjunto de pruebas.

Los 100 búferes de origen y 100 de destino están alineados a 64 bytes (para compatibilidad hasta AVX512 utilizando funciones de transmisión) y se inicializan una vez para aumentar los datos de los búferes de origen y 0xff para los búferes de destino.

El número de copias promediadas en cada máquina con cada configuración varió, ya que fue mucho más rápido en algunas y mucho más lento en otras.

Los resultados fueron los siguientes:

Haswell Xeon E5-2687Wv3 1 CPU (1 zócalo vacío) en Supermicro X10DAi con 32 GB DDR4-2400 (10c / 20t, 25 MB de caché L3). Pero recuerde, el punto de referencia gira a través de 100 pares de búferes de 16 MB, por lo que probablemente no obtengamos resultados de caché 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 en Supermicro X10DAi con 64 GB de 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 zócalo vacío) en Supermicro X11DPH-I con 48GB DDR4-2666 (18c / 36t, 24.75 MB de caché 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 en Supermicro X11DPH-I con 96GB 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 en ASUS ROG Rampage VI Extreme con 32GB DDR4-4266 (14c / 28t, 19.25 MB de caché L3) (overclockeado a 3.8GHz / 4.4GHz turbo, DDR a 4040MHz, Target AVX Frequency 3737MHz, Target AVX- 512 Frecuencia 3535MHz, frecuencia de caché objetivo 2424MHz)

---------------------------------------------------------------------------
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 en ASUS X99 con 24GB DDR4-2400 (6c / 12t, 15 MB de caché 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

Las funciones de ensamblaje se derivan de fast_memcpy en xine-libs, utilizadas principalmente para comparar con el optimizador de msvc ++.

El código fuente de la prueba está disponible en https://github.com/marcmicalizzi/memcpy_test (es un poco largo para poner en la publicación)

¿Alguien más se ha encontrado con esto o alguien tiene alguna idea de por qué esto podría estar sucediendo?


Actualización 2018-05-15 13: 40EST

Entonces, según lo sugerido por Peter Cordes, actualicé la prueba para comparar la captación previa y la no captada, y las tiendas NT frente a las tiendas normales, y ajusté la captación previa realizada en cada función ( no tengo ninguna experiencia significativa con la escritura de captación previa, así que si Estoy cometiendo algún error con esto, por favor avíseme y ajustaré las pruebas en consecuencia. La captación previa tiene un impacto, por lo que al menos está haciendo algo ). Estos cambios se reflejan en la última revisión del enlace de GitHub que hice anteriormente para cualquiera que busque el código fuente.

También he añadido un establecimiento de memoria SSE4.1, puesto que antes de SSE4.1 no se encuentra ninguna _mm_stream_load(he usado específicamente _mm_stream_load_si128) funciones de la ESS, por lo que sse_memcpyy sse2_memcpyno puede ser completamente usando tiendas NT, y así la avx_memcpyfunción utiliza funciones AVX2 para la carga de la corriente.

Opté por no hacer una prueba para los patrones de acceso puro a la tienda y a la carga pura todavía, ya que no estoy seguro de si la tienda pura podría ser significativa, ya que sin una carga en los registros a los que está accediendo, los datos no tendrían sentido y no se podrían verificar.

Los resultados interesantes con la nueva prueba fueron que en la configuración Xeon Skylake Dual Socket y solo en esa configuración, las funciones de almacenamiento eran en realidad significativamente más rápidas que las funciones de transmisión NT para la copia de memoria de 16MB. Además, solo en esa configuración (y solo con la captación previa de LLC habilitada en BIOS), la captación previa en algunas pruebas (SSE, SSE4.1) supera tanto a la captación previa 0 como a la no captación previa.

Los resultados sin procesar de esta nueva prueba son demasiado largos para agregarlos a la publicación, por lo que se publican en el mismo repositorio git que el código fuente en results-2018-05-15

Todavía no entiendo por qué para la transmisión de las tiendas NT, el nodo remoto NUMA es más rápido bajo la configuración de Skylake SMP, aunque el uso de las tiendas regulares es aún más rápido que eso en el nodo local NUMA


1
Todavía no he tenido la oportunidad de digerir sus datos, pero vea también ¿Por qué Skylake es mucho mejor que Broadwell-E para el rendimiento de la memoria de un solo subproceso? (comparar un Skylake de cuatro núcleos con un Broadwell de muchos núcleos, y ver la desventaja de una mayor latencia de memoria / L3 en sistemas de muchos núcleos donde el ancho de banda de un solo núcleo está limitado por la concurrencia máxima de memoria en un núcleo, no por los controladores DRAM). SKX tiene alta latencia / bajo ancho de banda por núcleo a L3 / memoria en general, de acuerdo con las pruebas de Mysticial y otros resultados. Probablemente lo estés viendo.
Peter Cordes

1
¿Alguna de sus copias usa tiendas NT? ¡Acabo de comprobar, y todas sus copias, excepto MMX, están usando prefetchntay NT almacena! ¡Ese es un hecho muy importante que dejó fuera de su pregunta! Consulte MEV REV MOVSB ​​mejorado para obtener más información sobre ERMSB rep movsbfrente a las tiendas de vectores NT frente a las tiendas de vectores normales. Jugar con eso sería más útil que MMX vs. SSE. Probablemente solo use AVX y / o AVX512 e intente NT vs. regular, y / o omita la captación previa de SW.
Peter Cordes

1
¿Ajustó la distancia de captación previa para sus máquinas SKX? SKX prefetchntaomite L3 y L2 (porque L3 no incluye), por lo que es más sensible a la distancia de captación previa (demasiado tarde y los datos tienen que llegar desde DRAM nuevamente, no solo L3), por lo que es más "frágil" ( sensible a sintonizar la distancia correcta). Sin embargo, sus distancias de captación previa parecen bastante bajas, menos de 500 bytes si estoy leyendo el asm correctamente. Las pruebas de @ Mysticial en SKX han encontrado que prefetchntapuede ser una gran desaceleración en esa uarca ), y no lo recomienda.
Peter Cordes

1
Definitivamente tiene algunos resultados interesantes aquí, pero necesitamos desenredarlos de varios efectos . Tener números con y sin tiendas NT puede decirnos algo útil sobre el comportamiento de NUMA. Al rellenar un segundo socket, incluso las fallas locales de L3 detectan la CPU remota, al menos en Broadwell / Haswell. Los Ee Xeons de doble zócalo no tienen un filtro snoop. Creo oro Xeons hacer tener filtros snoop, porque son capaces de operar en más de los sistemas de doble socket. Pero no estoy seguro de qué tan grande es, o qué significa eso realmente: PI no ha realizado el ajuste de rendimiento de la memoria en varios zócalos.
Peter Cordes

2
SKX es una interconexión fundamentalmente diferente; una malla en lugar de un anillo. Es un resultado interesante, pero no increíble y puede no ser un signo de una mala configuración. IDK, espero que alguien más con más experiencia con el hardware pueda arrojar más luz.
Peter Cordes

Respuestas:


0

¿Es tu memoria el rango incorrecto? ¿Quizás su placa tiene algo extraño con el ranking de memoria cuando agrega esa segunda CPU? Sé que cuando tienes máquinas Quad CPU, hacen todo tipo de cosas raras para que la memoria funcione correctamente y, si tienes la memoria clasificada incorrecta, a veces funcionará, pero volverá a la velocidad de 1/4 o 1/2 de la velocidad. Quizás SuperMicro hizo algo en esa placa para convertir el DDR4 y la CPU dual en Quad Channel y está usando matemática similar. Rango incorrecto == 1/2 velocidad.


No parece ser el caso, toda la memoria es 1R8 y coincide con el rango del supermicro qvl para la placa base. Valió la pena un cheque!
Marc Micalizzi

Sé que este es un sistema completamente diferente, pero esto es a lo que me refería también. qrl.dell.com/Files/en-us/Html/Manuals/R920/… Notará que los requisitos de rango cambian cuando aumenta la cantidad de barras / CPU.
thelanranger
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.