Performance 为什么Skylake在单线程内存吞吐量方面比Broadwell-E好得多?

Performance 为什么Skylake在单线程内存吞吐量方面比Broadwell-E好得多?,performance,x86,benchmarking,intel,cpu-architecture,Performance,X86,Benchmarking,Intel,Cpu Architecture,我们有一个简单的内存吞吐量基准。它所做的一切就是为一大块内存重复memcpy 从几个不同机器上的结果(64位编译)来看,Skylake机器的性能明显优于Broadwell-E,操作系统(Win10-64)、处理器速度和RAM速度(DDR4-2133)保持不变。我们谈论的不是几个百分点,而是大约2的一个因素。Skylake配置为双通道,双通道/三通道/四通道的Broadwell-E结果不会发生变化 你知道为什么会这样吗?以下代码在VS2015的发行版中编译,并报告在以下位置完成每个memcpy的平

我们有一个简单的内存吞吐量基准。它所做的一切就是为一大块内存重复memcpy

从几个不同机器上的结果(64位编译)来看,Skylake机器的性能明显优于Broadwell-E,操作系统(Win10-64)、处理器速度和RAM速度(DDR4-2133)保持不变。我们谈论的不是几个百分点,而是大约2的一个因素。Skylake配置为双通道,双通道/三通道/四通道的Broadwell-E结果不会发生变化

你知道为什么会这样吗?以下代码在VS2015的发行版中编译,并报告在以下位置完成每个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();
}
#包括
#包括
#包括
//防止memcpy在for循环外被优化
_declspec(noinline)void MemoryCopy(void*destinationMemoryBlock,void*sourceMemoryBlock,size\u t size)
{
memcpy(destinationMemoryBlock、sourceMemoryBlock、size);
}
int main()
{
const int SIZE_块的_=25000000;
常量int NUMBER_迭代次数=100;
void*sourceMemoryBlock=malloc(块的大小);
void*destinationMemoryBlock=malloc(块的大小);
大整数频率;
QueryPerformanceFrequency(&Frequency);
while(true)
{
龙龙合计=0;
LONGLONG max=0;
大整数开始时间、结束时间、经过的微秒;
对于(int i=0;i
<>这使得购买到BulyLead E体系结构的要求更为苛刻,并要求你真的需要额外的内核来考虑它。

我还得到了L3/TLB未命中计数。在Broadwell-E上,TLB未命中计数高出约20%,L3未命中计数高出约36%

我不认为这是“为什么”的答案因此,我不会将其标记为这样,但我认为这是目前最接近的。感谢所有在此过程中提供的有用意见。

现代CPU上的单线程内存带宽受从L1D传输到系统其余部分的最大并发/延迟的限制,而不是受DRAM控制器瓶颈的限制。每个核心具有10个行填充缓冲区(LFB),用于跟踪到L1D或从L1D发出的未完成请求。(以及16个“superqueue”条目,用于跟踪到L2或从L2发出的行)

(更新:实验表明Skylake可能有12个LFB,高于Broadwell的10个。例如,图7,以及其他性能实验,包括)


与四核或双核台式机/笔记本电脑芯片相比,Intel的多核芯片对L3/内存的延迟更高,因此在大型Xeon上,单线程内存带宽实际上要差得多,尽管多线程的最大聚合带宽要好得多。它们在连接核心、内存和内存的环总线上有更多的跳数控制器和系统代理(PCIe等)

SKX(Skylake server/AVX512,包括i9“高端桌面”芯片)在这方面非常糟糕:L3/内存延迟明显高于Broadwell-E/Broadwell EP,因此单线程带宽甚至比具有类似核心数的Broadwell更差。(SKX使用网状而不是环形总线,因为它的可扩展性更好。但在新的设计中,常数因子显然是不好的;对于中小型核心计数,未来几代可能会有更好的L3带宽/延迟。不过,每个核心的专用L2被提升到1MiB,因此可能L3有意降低功耗。)

(问题中的Skylake客户端(SKL)和后来的四核/六核台式机/笔记本电脑芯片(如Kaby Lake和Coffee Lake)仍然使用更简单的环形总线布局。只有服务器芯片发生了变化。我们还不确定冰湖客户端会做什么。)


一个四核或双核芯片只需要几个线程(特别是如果核心+非核心(L3)时钟高)就可以饱和其内存带宽,而一个具有快速DDR4双通道的Skylake具有相当大的带宽

有关这方面的更多信息,请参阅关于x86内存带宽的延迟限制平台部分(并阅读带有SIMD循环的memcpy/memset与rep movs/rep stos的其他部分,以及NT存储与常规RFO存储的其他部分,等等)


同样相关:(2017年更新了2007年那篇优秀文章中仍然正确的内容和变化)。

MSVC的memcpy库功能是否选择了基于CP的策略