考虑以下代码:
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 中的示例 7.26a 和 7.26b用 C++ 优化软件 http://www.agner.org/optimize/optimizing_cpp.pdf并且应该做同样的事情;bar
更“高效”,因为我们不在每次迭代时进行整数到浮点转换,而是进行更便宜的浮点加法(在 x86_64 上)。
Here https://godbolt.org/g/c7LXQD是这两个函数的 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 都执行预期的优化,尽管方式不完全相同(请参阅这个问题 https://stackoverflow.com/questions/48354636/is-multiplying-via-the-addressing-mode-a-good-idea).
您正在寻找启用归纳变量优化 https://en.wikipedia.org/wiki/Induction_variable#Application_to_strength_reduction对于浮点数。这种优化在浮点领域通常是不安全的,因为它会改变程序语义。在你的例子中它会起作用,因为两个初始值(0.0
)和步骤(2.0
) 可以用 IEEE 格式精确表示,但在实践中这种情况很少见。
它可以在下面启用-ffast-math
但在 GCC 中这似乎并没有被认为是重要的情况,因为它很早就拒绝了非整数归纳变量(参见树标量进化.c https://github.com/gcc-mirror/gcc/blob/428c12fad2565e3568d2d822942919da78350dc0/gcc/tree-scalar-evolution.c#L3326).
如果您认为这是一个重要的用例,您可以考虑在以下地址提交请求:GCC Bugzilla https://gcc.gnu.org/bugzilla/.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)