我们有一个简单的内存吞吐量基准测试,它只是重复执行大块内存的memcpy。
在几台不同的机器上(编译为64位),在保持操作系统(Win10-64)、处理器速度和RAM速度(DDR4-2133)相同时,Skylake机器的表现比Broadwell-E好得多。我们不是谈论一些百分点,而是约为2倍的因素。Skylake配置双通道,而对于双/三/四通道,Broadwell-E的结果并没有变化。
任何想法为什么会发生这种情况?随后的代码在VS2015 Release中编译,并报告每个memcpy完成的平均时间为:
64位:Skylake为2.2ms,Broadwell-E为4.5ms;
32位:Skylake为2.2ms,Broadwell-E为3.5ms。
我们可以通过利用多个线程在四通道Broadwell-E构建中获得更大的内存吞吐量,这很好,但是看到单线程内存访问如此明显的差异是令人沮丧的。有什么想法为什么差异如此明显?
我们还使用了各种基准测试软件,它们验证了这个简单例子所展示的 - 在Skylake上,单线程内存吞吐量要好得多。
在几台不同的机器上(编译为64位),在保持操作系统(Win10-64)、处理器速度和RAM速度(DDR4-2133)相同时,Skylake机器的表现比Broadwell-E好得多。我们不是谈论一些百分点,而是约为2倍的因素。Skylake配置双通道,而对于双/三/四通道,Broadwell-E的结果并没有变化。
任何想法为什么会发生这种情况?随后的代码在VS2015 Release中编译,并报告每个memcpy完成的平均时间为:
64位:Skylake为2.2ms,Broadwell-E为4.5ms;
32位:Skylake为2.2ms,Broadwell-E为3.5ms。
我们可以通过利用多个线程在四通道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();
}
rep movsb
以处理尾部/结尾的未对齐字节。但是,两个处理器上都运行相同的代码(没有动态调度),因此实现不是性能差异的因素。 - Cody Gray