缓存未命中?我该怎么看?

时间:2012-04-08 19:38:37

标签: c performance optimization

给出以下代码:

for (int i=0; i<n; i++)
{
  counter += myArray[i];
}

循环展开版本:

for (int i=0; i<n; i+=4)
{
  counter1 += myArray[i+0];
  counter2 += myArray[i+1];
  counter3 += myArray[i+2];
  counter4 += myArray[i+3];
}

total = counter1+ counter2 + counter3+ counter4;
  1. 为什么我们在第一个版本中有缓存未命中?
  2. 第二个版本确实比第一个版本更好吗?为什么?
  3. 问候

1 个答案:

答案 0 :(得分:4)

  

为什么我们在第一个版本中有缓存缺失?

正如奥利在评论中指出的那样。这个问题没有根据。如果数据已经在缓存中,那么就不会有缓存未命中。

除此之外,两个示例之间的内存访问没有区别。因此,它们不会成为它们之间任何性能差异的一个因素。

  

第二个版本确实比第一个版本更好吗?为什么?

通常,要做的就是实际测量。但在这个特殊的例子中,我会说它可能会更快。不是因为更好的缓存访问,而是因为循环展开。

您正在进行的优化称为“Node-Splitting”,您可以在其中分隔counter变量以破坏依赖关系链。

但是,在这种情况下,您正在进行一个简单的减少操作。许多现代编译器都能够识别这种模式,并为您进行节点分割。

它更快吗?最有可能。但是你应该检查编译器是否为你做了。


对于记录:我刚刚在Visual Studio 2010上对此进行了测试。
我很惊讶它是 无法 做这个优化。

; 129  : 
; 130  :     int counter = 0;
; 131  : 
; 132  :     for (int i=0; i<n; i++)
    mov ecx, DWORD PTR n$[rsp]
    xor edx, edx
    test    ecx, ecx
    jle SHORT $LN1@main
$LL3@main:

; 133  :     {
; 134  :         counter += myArray[i];

    add edx, DWORD PTR [rax]
    add rax, 4
    dec rcx
    jne SHORT $LL3@main
$LN1@main:

; 135  :     }

Visual Studio 2010似乎无法为此(微不足道的)示例执行“节点拆分”......