我创建了这个程序。它没有任何兴趣,但使用处理能力。
使用objdump -d
查看输出,即使使用O3进行编译,我也可以在结尾处看到三个rand
调用和相应的mov
指令。
为什么编译器没有意识到内存不会被使用,只是用while(1){}
替换下半部分?我使用的是gcc
,但我最感兴趣的是标准要求。
/*
* Create a program that does nothing except slow down the computer.
*/
#include <cstdlib>
#include <unistd.h>
int getRand(int max) {
return rand() % max;
}
int main() {
for (int thread = 0; thread < 5; thread++) {
fork();
}
int len = 1000;
int *garbage = (int*)malloc(sizeof(int)*len);
for (int x = 0; x < len; x++) {
garbage[x] = x;
}
while (true) {
garbage[getRand(len)] = garbage[getRand(len)] - garbage[getRand(len)];
}
}
答案 0 :(得分:10)
因为GCC不够智能,无法在动态分配的内存上执行此优化。但是,如果您将garbage
更改为本地数组,GCC会将循环编译为:
.L4:
call rand
call rand
call rand
jmp .L4
这只是反复调用rand
(这是必需的,因为调用有副作用),但优化了读写。
如果GCC更智能,它还可以优化rand
次来电,因为它的副作用只会影响以后的rand
次来电,而且在这种情况下也没有。{但是,这种优化可能会浪费编译器编写者的注意力。时间。
答案 1 :(得分:5)
通常,它不能告诉rand()
这里没有可观察到的副作用,并且不需要删除这些调用。
它可以删除写入,但可能是使用数组足以抑制它。
标准既不要求也不禁止它正在做什么。只要程序具有正确的可观察行为,任何优化都只是实现的质量问题。
答案 2 :(得分:3)
此代码导致未定义行为,因为它具有无限循环且没有可观察行为。因此,任何结果都是允许的。
在C ++ 14中,文本是1.10 / 27:
实现可能假设任何线程最终将执行以下操作之一:
- 终止,
- 调用库I / O函数,
- 访问或修改易失性对象,或
- 执行同步操作或原子操作。
[注意:这是为了允许编译器转换,例如删除空循环,即使无法证明终止也是如此。 - 后注]
我不会说rand()
算作I / O函数。
答案 3 :(得分:0)
让它有机会因阵列溢出而崩溃!编译器不会推测getRand
的输出范围。