Почему Skylake намного лучше, чем Broadwell-E для однопоточной памяти?

У нас есть простой тест пропускной способности памяти. Все, что он делает, это memcpy несколько раз для большого блока памяти.

Рассматривая результаты (скомпилированные для 64-бит) на нескольких разных машинах, машины Skylake значительно лучше, чем Broadwell-E, сохраняя OS (Win10-64), скорость процессора и скорость ОЗУ (DDR4-2133) одинаково. Мы не говорим о нескольких процентных пунктах, а скорее примерно в 2 раза . Skylake настроен двухканальным, а результаты для Broadwell-E не меняются для двух / трех / четырехканальных каналов.

Любые идеи, почему это может произойти? Следующий код компилируется в Release в VS2015 и сообщает среднее время для завершения каждой memcpy по адресу:

64-бит: 2,2 мс для Skylake против 4,5 мс для Broadwell-E

32-бит: 2,2 мс для Skylake против 3,5 мс для Broadwell-E .

Мы можем увеличить пропускную способность памяти на четырехканальной шине Broadwell-E, используя несколько streamов, и это приятно, но увидеть такую ​​резкую разницу в однопоточном доступе к памяти вызывает разочарование. Любые мысли о том, почему разница так выражена?

Мы также использовали различное программное обеспечение для бенчмаркинга, и они подтверждают, что показывает этот простой пример – пропускная способность однопоточной памяти на 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(); } 

Однопоточная пропускная способность памяти на современных процессорах ограничена max_concurrency / latency переносов от L1D до остальной части системы, а не узкими местами DRAM-controllerа. Каждое kernel ​​имеет 10 буферов линейной заливки (LFB), которые отслеживают выдающиеся запросы в / из L1D. (И 16 записей «суперэкспо», которые отслеживают линии в / из L2).

Многоядерные чипы Intel имеют более высокую задержку для L3 / памяти, чем четырехъядерные или двухъядерные чипы для настольных ПК / ноутбуков, поэтому пропускная способность одной streamовой памяти на самом деле намного хуже на большом Xeon, хотя максимальная совокупная пропускная способность со многими streamами намного лучше. Они имеют намного больше перелетов на кольцевой шине, которая соединяет ядра, controllerы памяти и системный агент (PCIe и т. Д.).

SKX (Skylake-server / AVX512, включая чипы classа high-end i9) действительно плохо для этого: L3 / латентность памяти значительно выше, чем для Broadwell-E / Broadwell-EP, поэтому однопоточная пропускная способность еще хуже чем на Бродвелл с аналогичным подсчетом ядра. (SKX использует сетку вместо кольцевой шины, потому что это масштабируется лучше, см. Это для подробностей об обоих . Но, по-видимому, постоянные факторы являются плохими в новом дизайне, возможно, у будущих поколений будет лучшая ширина полосы пропускания / латентность L3 для подсчета среднего и среднего ядра . Частный сквозной L2 набит до 1MiB, хотя, возможно, L3 намеренно медленнее, чтобы экономить электроэнергию.)


Для четырехъядерного или двухъядерного чипа требуется только несколько streamов (особенно, если Core 2 + Core (L3) имеет высокую частоту), чтобы насытить его пропускную способность памяти, а Skylake с быстрым DDR4-каналом имеет довольно большую пропускную способность.

Подробнее об этом см. В разделе «Связанные с Латентностью платформы» этого ответа о пропускной способности памяти x86. (И прочитайте другие части для memcpy / memset с циклами SIMD и rep movs/rep stos , а также NT-хранилища против обычных хранилищ RFO и т. Д.).

Также связано: что каждый программист должен знать о памяти? (Обновление 2017 года о том, что по-прежнему верно и что изменилось в этой замечательной статье с 2007 года).

Наконец, я получил VTune (evalutation). Он дает оценку DRAM, равную 0,602 (от 0 до 1) на Broadwell-E и 0,324 на Skylake, причем огромная часть задержки Broadwell-E поступает из задержки памяти. Учитывая, что карты памяти имеют одинаковую скорость (кроме двухканальной конфигурации в Skylake и четырехканальном в Broadwell-E), я думаю, что что-то о controllerе памяти в Skylake просто потрясающе лучше.

Это делает покупку в архитектуре Broadwell-E более жестким вызовом и требует, чтобы вам действительно нужны дополнительные ядра, чтобы даже рассмотреть их.

Я также получил подсчет пропусков L3 / TLB. На Broadwell-E количество промахов TLB было примерно на 20% выше, а L3 – на 36% выше.

Я не думаю, что это действительно ответ на вопрос «почему», поэтому я не буду отмечать его как таковой, но так близко, насколько я думаю, пока я пока не получу его. Спасибо за все полезные комментарии на этом пути.

Давайте будем гением компьютера.