Why is Skylake so much better than Broadwell-E for single-threaded memory throughput?
<p>We've got a simple memory throughput benchmark. All it does is memcpy repeatedly for a large block of memory.</p>

<p>Looking at the results (compiled for 64-bit) on a few different machines, Skylake machines do significantly better than Broadwell-E, keeping OS (Win10-64), processor speed, and RAM speed (DDR4-2133) the same. We're not talking a few percentage points, <strong>but rather a factor of about 2</strong>. Skylake is configured dual-channel, and the results for Broadwell-E don't vary for dual/triple/quad-channel.</p>

<p>Any ideas why this might be happening? The code that follows is compiled in Release in VS2015, and reports average time to complete each memcpy at:</p>

<p><strong>64-bit: 2.2ms for Skylake vs 4.5ms for Broadwell-E</strong></p>

<p><strong>32-bit: 2.2ms for Skylake vs 3.5ms for Broadwell-E</strong>.</p>

<p>We can get greater memory throughput on a quad-channel Broadwell-E build by utilizing multiple threads, and that's nice, but to see such a drastic difference for single-threaded memory access is frustrating. <strong>Any thoughts on why the difference is so pronounced?</strong></p>

<p>We've also used various benchmarking software, and they validate what this simple example shows - single-threaded memory throughput is way better on Skylake.</p>

<pre><code>#include &lt;memory&gt;
#include &lt;Windows.h&gt;
#include &lt;iostream&gt;

//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(&amp;Frequency);
    while (true)
    {
        LONGLONG total = 0;
        LONGLONG max = 0;
        LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
        for (int i = 0; i &lt; NUMBER_ITERATIONS; ++i)
        {
            QueryPerformanceCounter(&amp;StartingTime);
            MemoryCopy(destinationMemoryBlock, sourceMemoryBlock, SIZE_OF_BLOCKS);
            QueryPerformanceCounter(&amp;EndingTime);
            ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
            ElapsedMicroseconds.QuadPart *= 1000000;
            ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
            total += ElapsedMicroseconds.QuadPart;
            max = max(ElapsedMicroseconds.QuadPart, max);
        }
        std::cout &lt;&lt; "Average is " &lt;&lt; total*1.0 / NUMBER_ITERATIONS / 1000.0 &lt;&lt; "ms" &lt;&lt; std::endl;
        std::cout &lt;&lt; "Max is " &lt;&lt; max / 1000.0 &lt;&lt; "ms" &lt;&lt; std::endl;
    }
    getchar();
}
</code></pre>