为什么这个循环没有优化?

时间:2011-10-06 21:09:57

标签: c

我有一个非常简单的c程序,它将数组A中的所有元素复制回数组A.例如,

double *A;
A = (double*)malloc(sizeof(double)*SIZE);
for( i = 0; i < SIZE; i++) {
  A[i] = A[i];
}

我期待编译器优化它并最终变成noop。但是,通过测量此循环的运行时并查看汇编代码,似乎该元素确实从内存加载到寄存器中,然后存储回相同的内存位置。我启用了-O3。任何人都可以向我解释为什么c编译器不优化它?或者我在这里遗漏了什么?

非常感谢。

7 个答案:

答案 0 :(得分:7)

从硬件角度来看,加载和保存双精度并非无操作;如果它是IEEE double的几个陷阱表示之一,它的位值可能会改变。

例如,如果将NaN加载到寄存器中,它将被写为规范的NaN值,该值可能不是相同的按位值。

答案 1 :(得分:7)

我的gcc(版本4.6.1)优化了它

$ cat 7680489.c
#include <stdlib.h>

#define SIZE 100

int main(void) {
  double *a;
  size_t i;

  a = calloc(SIZE, sizeof *a); /* initialize elements */
  for (i = 0; i < SIZE; i++) a[i] = a[i];
  free(a);

  return 0;
}
$ gcc -std=c89 -O3 -S 7680489.c
$ cat 7680489.s
        .file   "7680489.c"
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB3:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $8, %esi
        movl    $100, %edi
        call    calloc
        movq    %rax, %rdi
        call    free
        xorl    %eax, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE3:
        .size   main, .-main
        .ident  "GCC: (Debian 4.6.1-4) 4.6.1"
        .section        .note.GNU-stack,"",@progbits

我看不到循环。使用malloc而不是calloc时,程序集输出非常相似。我切换到calloc以避免使用具有不确定值的对象(感谢R ..)。

答案 2 :(得分:2)

为了优化循环,编译器必须识别几件事:

  • 加载/存储不会导致数据修改(例如,由于浮点NaN转换)
  • 两个阵列地址相同
  • 两个数组索引表达式相同
  • 在考虑地址和索引之后,加载&amp;商店不重叠但完全一致。
  • 商店不会“踩”其他商店的结果,也不会“暂停”尚未加载的商品。
  • 加载/存储不会导致存储故障。这反过来要求编译器认识到存储来自malloc,并且循环不会在分配结束之后进行索引。
  • 循环将以有限的迭代次数终止
  • 可能还有其他几个我没想到的

请记住,优化旨在消除“正常”冗余,而不是消除“课堂示例”。

答案 3 :(得分:1)

编译器没有做任何实际的思考 它只能优化与预先设定的模式匹配的东西。

即。如果代码与已经预编程到编译器中的已知无操作模式不匹配,则不会被消除。

通过加入A[i] = A[i],您更改的模式足以与empty loop pattern

不匹配

答案 4 :(得分:0)

这里的问题是你正在使用指针。

编译器很难优化指针,因为它可以假设指针可以读/写内存中的任何位置。

转而更改为[]数组运算符,然后重试。你应该看到你期望的优化

答案 5 :(得分:0)

作为一般的信息,编译器在优化方面遇到的两个最大的问题是循环和指针,你的示例处理两者。编译器知道值在循环中经常变化,因此在优化它们时它们非常保守。此外,A是一个指针,编译器知道指针可能因各种因素而发生变化,因此在更改指针时会退回。这就是编译器在您的示例中遇到问题的原因。

答案 6 :(得分:0)

此代码具有未定义的行为(使用具有不确定值的对象),那么为什么您对它的作用有任何期望?