请考虑以下代码:
void foo(float* __restrict__ a)
{
int i; float val;
for (i = 0; i < 100; i++) {
val = 2 * i;
a[i] = val;
}
}
void bar(float* __restrict__ a)
{
int i; float val = 0.0;
for (i = 0; i < 100; i++) {
a[i] = val;
val += 2.0;
}
}
它们基于Agner Fog Optimizing software in C++中的例7.26a和7.26b,应该做同样的事情; bar
更加“高效”,因为我们在每次迭代时都不进行整数到浮点转换,而是更便宜的浮点数(在x86_64上)。
Here是这两个函数的clang和gcc结果(没有矢量化和展开)。
问题:在我看来,优化用循环索引替换乘法并加上常数值 - 当这是有益的 - 应该由编译器执行,即使(或者特别是如果)有一个涉及的类型转换。为什么这两个功能都没有发生?
请注意,如果我们使用int而不是float:
void foo(int* __restrict__ a)
{
int i; int val = 0;
for (i = 0; i < 100; i++) {
val = 2 * i;
a[i] = val;
}
}
void bar(int* __restrict__ a)
{
int i; int val = 0;
for (i = 0; i < 100; i++) {
a[i] = val;
val += 2;
}
}
clang和gcc都执行预期的优化,尽管不完全相同(参见this question)。
答案 0 :(得分:1)
您要为浮点数启用induction variable optimization。这种优化在浮点域中通常不安全,因为它会更改程序语义。在您的示例中,因为初始值(0.0
)和步长(2.0
)都可以用IEEE格式精确表示,所以它会起作用,但这在实践中很少见。
可以在-ffast-math
下启用它,但是似乎在GCC中这并不重要,因为它很早就拒绝了非积分归纳变量(请参见tree-scalar-evolution.c)。
如果您认为这是一个重要用例,则可以考虑在GCC Bugzilla处提交请求。