我正在将C代码编译成Matlab MEX文件。 Matlab默认启用优化(-O2
)。我有一个简单的规范化程序,由此打破。到这里使用sum
时,当它应该是1.000时,该值仅为0.995:
int N = 10000;
double *w, sum;
for(i=0; i<N; i++) {
w[i] = 1.0/N;
}
...a couple unrelated operations...
for(k=0; k<N; k++) {
sum += w[k];
}
for(k=0; k<N; k++) {
w[k] = w[k]/sum;
}
当我用-g
编译时,一切都很好。所以我认为这一定不能达到ISO标准。然后我发现了我所缺少的东西:
sum = 0.0;
这解决了它。所以我想编译器决定我不关心那个变量的确切值,因为我没有打算正确地初始化它?有人会解释吗?
编辑:是的,我知道它未定义,但这并不能解释为什么以及它如何影响该总和的优化。 如何做出明确的决定,说明,&#34;即使他正在阅读总和,他也不必关心价值。&#34;是否以某种方式跟踪未定义的值?
答案 0 :(得分:1)
让自己置身于编译器的位置。您正在实施C标准的要求和保证。
所以你编译代码,没有优化,得到这样的东西,逐行编译代码而不进行任何分析:
- add stack space for `sum`
...
- initialize k to 0
beginning_of_for:
- if k < N is not met, goto after_for
- add w[k] to sum
- increment k
- goto beginning_of_for
after_for:
- ...
- (for example) print sum
运行且最初sum
碰巧偶然包含值0.您没有看到任何奇怪的行为,因为您很幸运(或者说不幸运)。
现在他们告诉你,编译器,优化代码。您需要环顾四周并挤出任何不必要的操作以节省时间和/或空间。你到处走走,发现这笔钱是没有初始化的。按照标准,这意味着您可以保证以后赢得从该变量中读取(即使程序员已经这样做了,但是您并不关心,因为标准说你不需要照顾)。此外,如果您将一个未初始化的值添加到某个值,您将获得另一个未初始化的值,再次确保您赢得以后读取它。
所以这是你作为编译器的假设:
- variable X is defined but not initialized
- some operations that don't read from X
- operation that writes to X a value that is not uninitialized
- operations that read from X
从您的角度来看,直到X使用不依赖于其他未初始化值的值进行初始化,然后从X读取任何值都可以给出任意值,因此您可以仅使用0来代替实际读取从那个价值。更重要的是,任何基于未初始化值的写入都可以被丢弃,因为结果仍然是未初始化的值,因此它可以是任何值。
换句话说,您之前未优化过的代码:
- add stack space for `sum`
...
- initialize k to 0
beginning_of_for:
- if k < N is not met, goto after_for
- add w[k] to sum
- increment k
- goto beginning_of_for
after_for:
- ...
- (for example) print sum
分析如下:
Pass 1:
- add stack space for `sum` [sum uninitialized]
...
- initialize k to 0 [keep this as is]
beginning_of_for:
- if k < N is not met, goto after_for [keep this as is]
- add w[k] to sum [remove this line: sum is still uninitialized]
- increment k [keep this as is]
- goto beginning_of_for [keep this as is]
after_for:
- ...
- (for example) print sum [use 0 or whatever instead of sum]
给出了这个:
- add stack space for `sum`
...
- initialize k to 0
beginning_of_for:
- if k < N is not met, goto after_for
- increment k
- goto beginning_of_for
after_for:
- ...
- (for example) print whatever
优化的下一步是:
Pass 2:
- add stack space for `sum` [sum uninitialized]
...
- initialize k to 0 [replace 0 with N because of (1)]
beginning_of_for:
- if k < N is not met, goto after_for [remove because of (1)]
- increment k [remove because of (1)]
- goto beginning_of_for [remove because of (1)]
after_for:
- ...
- (for example) print whatever [keep this as is]
(1) the for loop is empty. `k` is `int` so it is guaranteed it will not overflow
(Note: signed integer overflow is **undefined behavior** according to
the standard), so the loop terminates with a single side effect: `k` reaches
`N`. So there is no point in actually looping.
现在您的代码变为:
- add stack space for `sum`
...
- initialize k to N
- ...
- (for example) print whatever
在最后一遍中,你会得到:
Pass 3:
- add stack space for `sum` [remove because sum is unused]
...
- initialize k to N [remove because k is unused]
- ...
- (for example) print whatever [keep this as is]
这意味着你最终会留下:
- (for example) print whatever