Por que Skylake é muito melhor que Broadwell-E para taxa de transferência de memória de thread único?

Temos uma referência simples de taxa de transferência de memória. Tudo o que faz é memcpy repetidamente para um grande bloco de memória.

Observando os resultados (compilados para 64 bits) em algumas máquinas diferentes, as máquinas Skylake se saem significativamente melhor que o Broadwell-E, mantendo o SO (Win10-64), a velocidade do processador e a velocidade da RAM (DDR4-2133) iguais. Não estamos falando de alguns pontos percentuais,mas sim um fator de cerca de 2. O Skylake está configurado como canal duplo e os resultados para Broadwell-E não variam para canal duplo / triplo / quádruplo.

Alguma idéia de por que isso pode estar acontecendo? O código a seguir é compilado no Release no VS2015 e relata o tempo médio para concluir cada memcpy em:

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

32 bits: 2.2ms para Skylake vs 3.5ms para Broadwell-E.

Podemos obter maior taxa de transferência de memória em uma construção Broadwell-E de quatro canais utilizando vários threads, o que é bom, mas ver uma diferença tão drástica no acesso à memória de thread único é frustrante.Alguma idéia de por que a diferença é tão pronunciada?

Também usamos vários softwares de benchmarking, e eles validam o que esse exemplo simples mostra - o rendimento da memória single-threaded é muito melhor no 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();
}

questionAnswers(2)

yourAnswerToTheQuestion