我有一个非常简单的c程序,它将数组A中的所有元素复制回数组A.例如,
double *A;
A = (double*)malloc(sizeof(double)*SIZE);
for( i = 0; i < SIZE; i++) {
A[i] = A[i];
}
我期待编译器优化它并最终变成noop。但是,通过测量此循环的运行时并查看汇编代码,似乎该元素确实从内存加载到寄存器中,然后存储回相同的内存位置。我启用了-O3。任何人都可以向我解释为什么c编译器不优化它?或者我在这里遗漏了什么?
非常感谢。
答案 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)
为了优化循环,编译器必须识别几件事:
请记住,优化旨在消除“正常”冗余,而不是消除“课堂示例”。
答案 3 :(得分:1)
编译器没有做任何实际的思考 它只能优化与预先设定的模式匹配的东西。
即。如果代码与已经预编程到编译器中的已知无操作模式不匹配,则不会被消除。
通过加入A[i] = A[i]
,您更改的模式足以与empty loop pattern
答案 4 :(得分:0)
这里的问题是你正在使用指针。
编译器很难优化指针,因为它可以假设指针可以读/写内存中的任何位置。
转而更改为[]
数组运算符,然后重试。你应该看到你期望的优化
答案 5 :(得分:0)
作为一般的信息,编译器在优化方面遇到的两个最大的问题是循环和指针,你的示例处理两者。编译器知道值在循环中经常变化,因此在优化它们时它们非常保守。此外,A是一个指针,编译器知道指针可能因各种因素而发生变化,因此在更改指针时会退回。这就是编译器在您的示例中遇到问题的原因。
答案 6 :(得分:0)
此代码具有未定义的行为(使用具有不确定值的对象),那么为什么您对它的作用有任何期望?