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

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

Глядя на результаты (скомпилированные для 64-битных систем) на нескольких разных машинах, машины Skylake работают значительно лучше, чем Broadwell-E, сохраняя ОС (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, используя несколько потоков, и это хорошо, но видеть такое резкое различие для однопоточного доступа к памяти разочаровывает. Любые мысли о том, почему разница так ярко выражена?

Мы также использовали различные программы для тестирования производительности, и они проверяют, что показывает этот простой пример - однопоточная пропускная способность памяти намного выше в Skylake.

#include <memory>
#include <Windows.h>
#include <iostream>

//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();
}

2 ответа

Решение

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

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

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


Четырехъядерному или двухъядерному чипу требуется только пара потоков (особенно, если ядра + uncore (L3) имеют высокую тактовую частоту) для насыщения пропускной способности памяти, а Skylake с быстрым двухканальным DDR4 имеет довольно большую пропускную способность.

Подробнее об этом см. В разделе "Платформы с привязкой к задержке" этого ответа о пропускной способности памяти x86. (И прочитайте другие части для memcpy/memset с циклами SIMD против rep movs/rep stosи магазины NT против обычных магазинов RFO и многое другое.)

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

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

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

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

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

Другие вопросы по тегам