Почему 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();
}