指针加1遍历数组的元素是否更快?

Is it faster to iterate through the elements of an array with pointers incremented by 1?

本文关键字:是否 元素 遍历 数组 指针      更新时间:2023-10-16

这样做会更快吗?

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递增的连续较大数字的指针。也许硬件有特殊的加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

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

一个非常幼稚的编译器会将代码原形翻译成机器码。大多数机器确实提供了一个非常快的增量操作。它们通常还为带有偏移量的地址提供相对寻址。这可能比绝对寻址多花几个周期。所以,是的,使用指针的版本可能会更快。

但是要考虑到每台机器都是不同的,并且只要程序的可观察行为不改变,编译器就可以进行优化。考虑到这一点,我建议一个合理的编译器将从两个版本创建的代码在性能上没有差异。

任何合理的编译器都会在这两种选择的循环内生成相同的代码-我查看了为迭代std::vector而生成的代码,使用for循环和迭代器的整数或使用for( auto i: vec)类型构造[std::vector内部有两个指针用于存储值的beginend,所以像您的papb]。gcc和clang都在循环本身内生成相同的代码[在不同的编译器之间,循环的确切细节略有不同,但除此之外,没有区别]。循环的设置略有不同,但除非你经常做少于5个项目的循环,否则你为什么要担心呢?]时,重要的是循环的实际内容,而不是实际循环前的位。

对于所有性能很重要的代码,确切的代码,编译器和版本,编译器选项,处理器和模型,将对代码的执行产生影响。但是对于绝大多数的处理器和编译器,我不希望有明显的区别。如果代码真的很关键,衡量不同的替代方案,看看哪种方法最适合您的情况。

相关文章: