这是一个有界循环,它递增同一缓冲区的两个位置。
unsigned int getid();
void foo(unsigned int *counter, unsigned int n) {
unsigned int A = getid();
unsigned int B = getid();
for (unsigned int i = 0; i < n; i++) {
++counter[A];
++counter[B];
}
}
根据编译器资源管理器(https://godbolt.org/z/b1sjf5669)的结果,即使在-O3优化级别,程序仍然存在涉及
add 1
命令的循环。
相反,如果去掉
++counter[B]
,编译器可以减少循环,将程序优化为counter[A] += n
。 (https://godbolt.org/z/4YoPcz5rT)
为什么编译器是保守的,即在这种情况下不将代码转换为
counter[A] += n, counter[B] += n
?
我知道一些别名问题会导致循环减少失败。然而,这里只有一个缓冲区,即使
A=B
,优化代码也不会改变结果。
正如评论中已经说过的,增量实际上是 3 个操作数:加载、添加、存储。
保守地说,不允许编译器重新排序
MayAlias
内存操作。
为了做你想做的事,编译器必须证明计算结果不依赖于内存别名,这在一般情况下是不平凡的。
我认为这个优化的范围太小,实施起来不合理。