【问题标题】:Is it faster to iterate through the elements of an array with pointers incremented by 1? [duplicate]使用指针递增 1 迭代数组元素是否更快? [复制]
【发布时间】:2015-07-07 21:05:06
【问题描述】:

做类似的事情会更快

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 的方法,所以第一种方法更快?或不?只是好奇。希望我的问题有意义。

【问题讨论】:

  • 测量,证明,然后询问!为什么你会期望现代 c++ 编译器不会针对完全相同的代码进行优化??
  • 您的代码在 C 语言中是非法的,请删除 [c] 标签,或将代码编辑为有效的 C

标签: c++ c arrays pointers optimization


【解决方案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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2014-04-22
        • 1970-01-01
        • 2018-08-07
        • 1970-01-01
        • 2011-07-10
        • 2023-02-13
        • 2021-06-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多