【问题标题】:Why can't clang and gcc optimize away this int-to-float conversion?为什么 clang 和 gcc 不能优化这种 int-to-float 转换?
【发布时间】:2018-01-19 23:05:51
【问题描述】:

考虑以下代码:

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)。

【问题讨论】:

    标签: gcc type-conversion clang compiler-optimization


    【解决方案1】:

    您正在寻找为浮点数启用induction variable optimization。这种优化在浮点领域通常是不安全的,因为它改变了程序语义。在您的示例中,它会起作用,因为初始值 (0.0) 和步长 (2.0) 都可以用 IEEE 格式精确表示,但这在实践中很少见。

    它可以在 -ffast-math 下启用,但似乎这在 GCC 中并不重要,因为它在早期就拒绝了非整数归纳变量(请参阅 tree-scalar-evolution.c)。

    如果您认为这是一个重要的用例,您可以考虑通过 GCC Bugzilla 提交请求。

    【讨论】:

    • 如果相同的浮点值会被多次更新,我会理解语义的关注,以便最终值可以将其错误气球变成有意义的东西。但是在精度阈值内(或两倍?)的错误 - 在本质上不精确的类型中不是可以接受的吗?
    • @einpoklum 不幸的是,标准要求编译器非常严格地遵守 IEEE 语义(保留准确的结果和潜在的异常)。这就是为什么游戏甚至一些科学代码通常使用-ffast-math 编译的原因。有关详细信息,请参阅gcc wikipage
    猜你喜欢
    • 2013-06-16
    • 2013-05-18
    • 1970-01-01
    • 1970-01-01
    • 2017-03-05
    • 2016-08-21
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    相关资源
    最近更新 更多