Warum ist Skylake für den Single-Threaded-Speicherdurchsatz so viel besser als Broadwell-E?

Wir haben einen einfachen Benchmark für den Speicherdurchsatz. Es wird lediglich wiederholt für einen großen Speicherblock gespeichert.

Betrachtet man die Ergebnisse (für 64-Bit-Versionen kompiliert) auf einigen verschiedenen Computern, sind Skylake-Computer deutlich besser als Broadwell-E, wobei Betriebssystem (Win10-64), Prozessorgeschwindigkeit und RAM-Geschwindigkeit (DDR4-2133) gleich bleiben. Wir sprechen nicht ein paar Prozentpunkte, aber eher ein Faktor von etwa 2. Skylake ist zweikanalig konfiguriert, und die Ergebnisse für Broadwell-E variieren nicht für zwei, drei oder vier Kanäle.

Irgendwelche Ideen, warum dies passieren könnte? Der folgende Code wird in Release in VS2015 kompiliert und gibt die durchschnittliche Zeit zum Abschließen der einzelnen Memcpys an:

64-Bit: 2,2 ms für Skylake vs 4,5 ms für Broadwell-E

32-Bit: 2,2 ms für Skylake vs 3,5 ms für Broadwell-E.

Wir können einen höheren Speicherdurchsatz bei einem Broadwell-E-Build mit vier Kanälen erzielen, indem wir mehrere Threads verwenden, und das ist schön, aber es ist frustrierend, einen derart drastischen Unterschied beim Single-Thread-Speicherzugriff zu sehen.Möchten Sie wissen, warum der Unterschied so groß ist?

Wir haben auch verschiedene Benchmarking-Software verwendet und diese validiert, was dieses einfache Beispiel zeigt - der Single-Threaded-Speicherdurchsatz ist bei Skylake viel besser.

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

Antworten auf die Frage(4)

Ihre Antwort auf die Frage