C代码循环性能

标签 c performance intel instructions assembly

我的应用程序中有一个乘加内核,我想提高它的性能。

我使用的是 Intel Core i7-960(3.2 GHz 时钟)并且已经使用 SSE 内部函数手动实现了内核,如下所示:

 for(int i=0; i<iterations; i+=4) {
    y1 = _mm_set_ss(output[i]);
    y2 = _mm_set_ss(output[i+1]);
    y3 = _mm_set_ss(output[i+2]);
    y4 = _mm_set_ss(output[i+3]);

    for(k=0; k<ksize; k++){
        for(l=0; l<ksize; l++){
            w  = _mm_set_ss(weight[i+k+l]);

            x1 = _mm_set_ss(input[i+k+l]);
            y1 = _mm_add_ss(y1,_mm_mul_ss(w,x1));
            …
            x4 = _mm_set_ss(input[i+k+l+3]);
            y4 = _mm_add_ss(y4,_mm_mul_ss(w,x4));
        }
    }
    _mm_store_ss(&output[i],y1);
    _mm_store_ss(&output[i+1],y2);
    _mm_store_ss(&output[i+2],y3);
    _mm_store_ss(&output[i+3],y4);
 }

我知道我可以使用打包的 fp vector 来提高性能并且我已经成功地做到了,但我想知道为什么单个标量代码无法满足处理器的峰值性能。

这个内核在我的机器上的性能是每个周期约 1.6 个 FP 操作,而最大值是每个周期 2 个 FP 操作(因为 FP add + FP mul 可以并行执行)。

如果我研究生成的汇编代码是正确的,那么理想的时间表如下所示,其中 mov 指令需要 3 个周期,从加载域到 FP 域的切换延迟为相关指令需要 2 个周期,FP 乘法需要 4 个周期,FP 加法需要 3 个周期。 (请注意,乘法 -> 加法的依赖性不会导致任何切换延迟,因为这些操作属于同一域)。

schedule

根据测得的性能(最大理论性能的约 80%),每 8 个周期有约 3 条指令的开销。

我正在尝试:

  • 摆脱这种开销,或者
  • 解释它来自哪里

当然存在缓存未命中和数据未对齐的问题,这会增加移动指令的延迟,但是是否还有其他因素可以在这里发挥作用?比如注册阅读摊位之类的?

我希望我的问题很清楚,提前感谢您的回复!


更新:内部循环的程序集如下所示:

...
Block 21: 
  movssl  (%rsi,%rdi,4), %xmm4 
  movssl  (%rcx,%rdi,4), %xmm0 
  movssl  0x4(%rcx,%rdi,4), %xmm1 
  movssl  0x8(%rcx,%rdi,4), %xmm2 
  movssl  0xc(%rcx,%rdi,4), %xmm3 
  inc %rdi 
  mulss %xmm4, %xmm0 
  cmp $0x32, %rdi 
  mulss %xmm4, %xmm1 
  mulss %xmm4, %xmm2 
  mulss %xmm3, %xmm4 
  addss %xmm0, %xmm5 
  addss %xmm1, %xmm6 
  addss %xmm2, %xmm7 
  addss %xmm4, %xmm8 
  jl 0x401b52 <Block 21> 
...

最佳答案

我在评论中注意到:

  • 循环需要 5 个周期来执行。
  • “应该”需要 4 个周期。 (因为有 4 个加法和 4 个乘法)

但是,您的程序集显示了 5 个 SSE movssl 指令。根据Agner Fog's tables对于 Nehalem,所有浮点 SSE 移动指令至少是 1 个指令/周期 倒数吞吐量。

因为你有 5 个,你不能做得比 5 个周期/迭代更好


因此,为了达到最佳性能,您需要减少所拥有的负载数量。你如何做到这一点我不能立即看到这个特殊情况 - 但它可能是可能的。

一种常见的方法是使用 tiling .添加嵌套级别以改善局部性的位置。虽然它主要用于改进缓存访问,但它也可用于寄存器以减少所需的加载/存储数量。

最终,您的目标是将加载次数减少到少于添加/混合次数。所以这可能是要走的路。

关于C代码循环性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9992054/

相关文章:

c++ - 如何更新具有兼容性的 C/C++ 结构

python - 如何使用 VLOOKUP 类型在 Pandas 中的新列中添加值?

c++ - Intel Parallel Studio 仅使用 2GB RAM

python - Python c 扩展中的 null 常量

c - 我什么时候需要一个带有结构的指针

c++ - 我的程序没有在屏幕上显示字符

python - Numba 函数比 C++ 慢,循环重新排序进一步减慢 x10

c - write 和 printf,哪个更快?

c++ - 英特尔c/c++编译器: "could not locate executable icc" (and ecc)

c - 从 C 调用的 Intel 8086 汇编程序