迭代指针增加1的数组元素会更快吗?

时间:2015-07-07 21:05:07

标签: c++ c arrays pointers optimization

之类的事情会更快吗?
for ( int * pa(arr), * pb(arr+n); pa != pb; ++pa )
{ 
   // do something with *pa
}

for ( size_t k = 0; k < n; ++k )
{ 
   // do something with arr[k]
}

???

我理解arr[k]等同于*(arr+k),但是在第一种方法中,您使用的是当前指针,其增加了1,而在第二种情况下,您使用的是增量指针来自arr的连续更大的数字。也许硬件有特殊的递增方式,所以第一种方法更快?或不?只是好奇。希望我的问题有道理。

3 个答案:

答案 0 :(得分:1)

如果编译器是智能的(并且大多数编译器都是),那么两个循环的性能应该相等。

例如,我已经使用生成程序集在gcc 5.1.0中编译了代码:

int __attribute__ ((noinline)) compute1(int* arr, int n)
{
  int sum = 0;
  for(int i = 0; i < n; ++i)
  {
    sum += arr[i];
  }
  return sum;
}

int __attribute__ ((noinline)) compute2(int* arr, int n)
{
  int sum = 0;
  for(int * pa(arr), * pb(arr+n); pa != pb; ++pa)
  {
    sum += *pa;
  }
  return sum;
}

结果汇编是:

compute1(int*, int):
    testl   %esi, %esi
    jle .L4
    leal    -1(%rsi), %eax
    leaq    4(%rdi,%rax,4), %rdx
    xorl    %eax, %eax
.L3:
    addl    (%rdi), %eax
    addq    $4, %rdi
    cmpq    %rdx, %rdi
    jne .L3
    rep ret
.L4:
    xorl    %eax, %eax
    ret
compute2(int*, int):
    movslq  %esi, %rsi
    xorl    %eax, %eax
    leaq    (%rdi,%rsi,4), %rdx
    cmpq    %rdx, %rdi
    je  .L10
.L9:
    addl    (%rdi), %eax
    addq    $4, %rdi
    cmpq    %rdi, %rdx
    jne .L9
    rep ret
.L10:
    rep ret
main:
    xorl    %eax, %eax
    ret

如您所见,两个函数中最重要的部分(循环)是相同的:

.L9:
    addl    (%rdi), %eax
    addq    $4, %rdi
    cmpq    %rdi, %rdx
    jne .L9
    rep ret

但是在更复杂的示例或其他编译器中,结果可能会有所不同。因此,您应该对其进行测试和测量,但大多数编译器会生成类似的代码。

完整的代码示例:https://goo.gl/mpqSS0

答案 1 :(得分:0)

这无法回答。这取决于你的机器上的编译器和。

一个非常天真的编译器会将代码转换为到机器代码。大多数机器确实提供了非常快的增量操作。它们通常还为具有偏移量的地址提供相对寻址。这可能比绝对寻址多几个周期。所以,是的,带指针的版本可能会更快。

但考虑到每台机器都不同,并且只要程序的可观察行为没有改变,就允许编译器进行优化。鉴于此,我建议合理的编译器将从两个版本中创建性能无差异的代码。

答案 2 :(得分:0)

任何合理的编译器都会为这两个选项生成在循环内部相同的代码 - 我查看为迭代std::vector而生成的代码,使用for循环和迭代器的整数或使用{ {1}}类型构造[for( auto i: vec)内部有两个指针用于存储值的std::vectorbegin,因此与您的endpa一样。 gcc和clang都在循环内部生成相同的代码[循环的确切细节在编译器之间略有不同,但除此之外,没有区别]。循环的设置略有不同,但除非你OFTEN执行少于5项的循环[如果是这样,为什么你担心?],循环的实际内容是重要的,而不是在实际循环之前的位

与性能很重要的所有代码一样,确切的代码,编译器品牌和版本,编译器选项,处理器品牌和型号将对代码的执行方式产生影响。但对于绝大多数处理器和编译器,我预计没有可衡量的差异。如果代码非常重要,请测量不同的替代方案,看看哪种方法最适合您。