¿Por qué Skylake es mucho mejor que Broadwell-E para el rendimiento de la memoria de un único subproceso?

Tenemos un punto de referencia de rendimiento de memoria simple. Todo lo que hace es memcpy repetidamente para un gran bloque de memoria.

Mirando los resultados (comstackdos para 64 bits) en algunas máquinas diferentes, las máquinas Skylake son significativamente mejores que Broadwell-E, manteniendo el sistema operativo (Win10-64), la velocidad del procesador y la velocidad de la RAM (DDR4-2133) de la misma manera. No estamos hablando de algunos puntos porcentuales, sino más bien de un factor de aproximadamente 2 . Skylake está configurado como de doble canal y los resultados para Broadwell-E no varían para el canal dual / triple / cuádruple.

Alguna idea de por qué esto esta pasando? El código que sigue se comstack en Release en VS2015 e informa el tiempo promedio para completar cada memcpy en:

64 bits: 2.2 ms para Skylake vs 4.5ms para Broadwell-E

32 bits: 2,2 ms para Skylake frente a 3,5 ms para Broadwell-E .

Podemos obtener un mayor rendimiento de la memoria en una construcción Broadwell-E de cuatro canales utilizando múltiples hilos, y eso es bueno, pero ver una diferencia tan drástica para el acceso a la memoria de un solo subproceso es frustrante. ¿Alguna idea de por qué la diferencia es tan pronunciada?

También hemos utilizado varios softwares de benchmarking, y ellos validan lo que muestra este simple ejemplo: el rendimiento de la memoria de un único subproceso es mucho mejor en Skylake.

#include  #include  #include  //Prevent the memcpy from being optimized out of the for loop _declspec(noinline) void MemoryCopy(void *destinationMemoryBlock, void *sourceMemoryBlock, size_t size) { memcpy(destinationMemoryBlock, sourceMemoryBlock, size); } int main() { const int SIZE_OF_BLOCKS = 25000000; const int NUMBER_ITERATIONS = 100; void* sourceMemoryBlock = malloc(SIZE_OF_BLOCKS); void* destinationMemoryBlock = malloc(SIZE_OF_BLOCKS); LARGE_INTEGER Frequency; QueryPerformanceFrequency(&Frequency); while (true) { LONGLONG total = 0; LONGLONG max = 0; LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds; for (int i = 0; i < NUMBER_ITERATIONS; ++i) { QueryPerformanceCounter(&StartingTime); MemoryCopy(destinationMemoryBlock, sourceMemoryBlock, SIZE_OF_BLOCKS); QueryPerformanceCounter(&EndingTime); ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart; ElapsedMicroseconds.QuadPart *= 1000000; ElapsedMicroseconds.QuadPart /= Frequency.QuadPart; total += ElapsedMicroseconds.QuadPart; max = max(ElapsedMicroseconds.QuadPart, max); } std::cout << "Average is " << total*1.0 / NUMBER_ITERATIONS / 1000.0 << "ms" << std::endl; std::cout << "Max is " << max / 1000.0 << "ms" << std::endl; } getchar(); } 

El ancho de banda de la memoria de un solo subproceso en las CPU modernas está limitado por max_concurrency / latency de las transferencias desde L1D al rest del sistema, no por los cuellos de botella del controlador de DRAM. Cada núcleo tiene 10 búferes de relleno de línea (LFB) que rastrean solicitudes pendientes a / desde L1D. (Y 16 entradas “superqueue” que rastrean líneas hacia / desde L2).

Los chips de muchos núcleos de Intel tienen una mayor latencia a L3 / memoria que los chips quad-core o dual-desktop / laptop, por lo que el ancho de banda de memoria de un solo subproceso es mucho peor en un Xeon grande, aunque el ancho de banda agregado máximo con muchos hilos es mucho mejor. Tienen muchos más saltos en el bus de anillo que conecta núcleos, controladores de memoria y el agente del sistema (PCIe, etc.).

SKX (Skylake-server / AVX512, incluidos los chips i9 “high-end desktop”) es realmente malo para esto: la latencia L3 / memoria es significativamente mayor que para Broadwell-E / Broadwell-EP, por lo que el ancho de banda de un solo subproceso es incluso peor que en un Broadwell con un conteo de núcleos similar. (SKX usa una malla en lugar de un bus de anillo porque se escala mejor, vea esto para detalles sobre ambos . Pero aparentemente los factores constantes son malos en el nuevo diseño, tal vez las generaciones futuras tendrán mejor latencia / ancho de banda L3 para conteos de núcleos pequeños / medianos Sin embargo, el L2 privado por núcleo se incrementa hasta 1MiB, así que tal vez L3 es intencionalmente lento para ahorrar energía.


Un chip de cuatro o dos núcleos solo necesita un par de hilos (especialmente si los núcleos + uncore (L3) tienen un reloj alto) para saturar su ancho de banda de memoria, y un Skylake con un canal doble DDR4 rápido tiene bastante ancho de banda.

Para obtener más información al respecto, consulte la sección Plataformas vinculadas a la latencia de esta respuesta sobre el ancho de banda de la memoria x86. (Y lea las otras partes para memcpy / memset con bucles SIMD vs. rep movs/rep stos , y tiendas NT frente a tiendas RFO comunes, y más.)

También relacionado: ¿Qué debería saber todo progtwigdor sobre la memoria? (Actualización de 2017 sobre lo que sigue siendo cierto y lo que ha cambiado en ese excelente artículo de 2007).

Finalmente obtuve VTune (evaluación) en funcionamiento. Da un puntaje de DRAM enlazado de .602 (entre 0 y 1) en Broadwell-E y .324 en Skylake, con una gran parte del retraso de Broadwell-E proveniente de Latencia de memoria. Dado que las tarjetas de memoria tienen la misma velocidad (excepto el canal dual configurado en Skylake y el canal cuádruple en Broadwell-E), mi mejor opción es que algo sobre el controlador de memoria en Skylake es tremendamente mejor.

Hace que comprar en la architecture Broadwell-E sea una decisión mucho más difícil, y requiere que realmente necesite los núcleos adicionales para siquiera considerarlo.

También obtuve recuentos de miss L3 / TLB. En Broadwell-E, el recuento de desapariciones de TLB fue aproximadamente un 20% mayor, y el recuento de desapariciones de L3 fue un 36% mayor.

No creo que esto sea realmente una respuesta para “por qué”, así que no lo marcaré como tal, pero es lo más cercano que creo que llegaré a uno por el momento. Gracias por todos los comentarios útiles en el camino.