title: Why is Skylake so much better than Broadwell-E for single-threaded memory throughput?
body: <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>