众所周知,编译器可以混合赋值顺序以优化执行, 所以-
a=b;
c=d;
可以实际执行
c=d;
a=b;
但是使用以下代码:
a=b;
x=a;
func(x);
调用func(x)
时,x
必须首先包含b
,否则结果可能不可预测。
现在,下面的代码怎么样:
int *addr1 = some_addr;
int *addr2 = (int *)0xf00;
/* The following applies:
* some_other_addr >= some_addr
*/
for (addr1; addr1 < some_other_addr; addr1++)
{
*addr1 += 1;
}
*addr2 *= 8;
当addr2
指向for
循环范围内的地址时,我们需要知道是否 promise *addr2
将在 8 的乘法之前递增,就好像没有一样,并且一些优化步骤将 *addr2 *= 8;
放置在 for
循环之前, 的结果*addr2
将与未经优化执行时有所不同。
如果 some_addr
和 some_other_addr
在范围内定义并且它们作为参数传递,答案会有所不同吗?因为在第一种情况下,编译器很容易知道 *addr2
位于 for
循环的范围内,而在第二种情况下,情况就不那么明显了。
而且,如果我们从程序集角度来看,我们以 bss
部分初始化的示例 reset_handler
代码片段为例:
ldr r1, =__BSS_SIZE__
cmp r1, #0
beq FINISHED_LABEL
ldr r0, =__BSS_START__
ldr r2, =0
LOOP_LABEL:
str r2, [r0]
add r0, r4
subs r1, r4
bne LOOP_LABEL
如果此代码之后的下一条指令(位于FINISHED_LABEL
)从bss范围内的地址加载一个值(ldr
),是否 promise 内容将有效(0) 当时?
最佳答案
编译器必须做的事情才能做到这一点,称为 "alias analysis" .
如果编译器可以证明 addr2
不在 addr1
循环的范围内,它可以对其重新排序或保留 *addr2
在整个循环中的寄存器中。
对于像 for(...; addr1++) { *addr1 += *addr2; 这样的情况,这是一个非常有用的优化。 }
以避免每次都重新加载 addr2
,原因之一 the restrict
keyword存在。
如果输入可能重叠,编译器可以(并且确实)发出检查重叠的代码并运行优化的(例如自动向量化) 如果没有重叠则循环,或者如果有重叠则运行安全循环。
如果编译器无法证明转换将给出与 C 抽象机相同的最终结果,则它无法执行转换。 (我说“最终”是因为存储到内存的顺序不是可观察结果的一部分,除非您使用 std::atomic 。因此不允许编译时转换破坏 单线程代码,与乱序CPU的做法非常相似:为单个线程提供一切都按程序顺序发生的错觉。)
as-if 规则仅允许在不会导致 UB 的所有情况下进行优化,包括像 unsigned size = 0xffffffff
,这通常会导致编译器无法进行您希望的优化,除非您调整源代码。
UB 是允许某些优化的关键(例如不在循环内重做带符号数组索引的符号扩展)。请参阅 What Every C Programmer Should Know About Undefined Behavior #1/3 .
关于c - 内存分配优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49275474/