为什么我的应用程序无法达到核心i7 920峰值FP性能

Ric*_*cky 3 c compiler-construction sse intel

我有一个关于我的核心i7 920的FP峰值性能的问题.我有一个应用程序执行大量的MAC操作(基本上是卷积操作),我无法通过一个因素达到CPU的峰值FP性能当使用多线程和SSE指令时,大约是8倍.当试图找出原因是什么时,我最终得到了一个简化的代码片段,在单个线程上运行而不使用同样糟糕的SSE指令:

for(i=0; i<49335264; i++)
{
    data[i] += other_data[i] * other_data2[i];
}
Run Code Online (Sandbox Code Playgroud)

如果我是正确的(数据和other_data数组都是FP),这段代码需要:

49335264 * 2 = 98670528 FLOPs
Run Code Online (Sandbox Code Playgroud)

它执行时间约为150毫秒(我非常确定这个时间是正确的,因为C计时器和英特尔VTune Profiler给出了相同的结果)

这意味着此代码段的性能如下:

98670528 / 150.10^-3 / 10^9 = 0.66 GFLOPs/sec
Run Code Online (Sandbox Code Playgroud)

这个CPU的峰值性能应该是2*3.2 GFlops/sec(2 FP单元,3.2 GHz处理器)对吗?

这个巨大的差距有什么解释吗?因为我无法解释它.

非常感谢,我真的可以使用你的帮助!

Gun*_*iez 5

我会用SSE.

编辑:我自己运行了一些测试,发现你的程序既不受内存带宽的限制(理论上的限制比你的结果高3-4倍),也没有浮点性能(甚至更高的限制),它是有限的通过操作系统延迟分配内存页面.

#include <chrono>
#include <iostream>
#include <x86intrin.h>

using namespace std::chrono;

static const unsigned size = 49335264;

float data[size], other_data[size], other_data2[size];

int main() {
#if 0
        for(unsigned i=0; i<size; i++) {
                data[i] = i;
                other_data[i] = i;
                other_data2[i] = i;
        }
#endif
    system_clock::time_point start = system_clock::now();
        for(unsigned i=0; i<size; i++) 
                data[i] += other_data[i]*other_data2[i];

    microseconds timeUsed = system_clock::now() - start;

    std::cout << "Used " << timeUsed.count() << " us, " 
              << 2*size/(timeUsed.count()/1e6*1e9) << " GFLOPS\n";
}
Run Code Online (Sandbox Code Playgroud)

翻译g++ -O3 -march=native -std=c++0x.该计划给出

Used 212027 us, 0.465368 GFLOPS
Run Code Online (Sandbox Code Playgroud)

作为输出,虽然热循环转换为

400848:       vmovaps 0xc234100(%rdx),%ymm0
400850:       vmulps 0x601180(%rdx),%ymm0,%ymm0
400858:       vaddps 0x17e67080(%rdx),%ymm0,%ymm0
400860:       vmovaps %ymm0,0x17e67080(%rdx)
400868:       add    $0x20,%rdx
40086c:       cmp    $0xbc32f80,%rdx
400873:       jne    400848 <main+0x18>
Run Code Online (Sandbox Code Playgroud)

这意味着它是完全矢量化的,每次迭代使用8个浮点数,甚至利用AVX.在玩了movntdq一些没有买任何东西的流指令后,我决定用某些东西初始化数组 - 否则它们将是零页面,只有在写入时才会映射到实际内存.改变#if 0#if 1立即产生

Used 48843 us, 2.02016 GFLOPS
Run Code Online (Sandbox Code Playgroud)

这与系统的内存带宽非常接近(4个浮点数每两个FLOPS 4个字节= 16 GBytes/s,理论上限为DDR3的2个通道,每个10,667 GBytes/s).