在gcc-4.8中预先计算(C ++ 11)

时间:2013-10-13 20:45:26

标签: c++ gcc c++11

测试旧gcc-4.4.0gcc-4.6.4中的代码,编译器能够应用智能优化并预先计算 const输入的结果:< / p>

#include <iostream>
#include <chrono>
using namespace std;

const auto N = 1000000000ULL;  // constexptr is tested, no effect

unsigned long long s(unsigned long long n)
{
    auto s = 0ULL;
    for (auto i = 0ULL; i < n; i++)
        s += i;
    return s;
}

int main()
{
    auto t1 = std::chrono::high_resolution_clock::now();

    auto x = s(N);

    auto t2 = std::chrono::high_resolution_clock::now();
    auto t = std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count();
    cout << "Result: " << x << " -- time (ms):" << t/0.1e7 << endl;
}

N是一个常量值,然后编译器可以在编译时运行函数s并将结果赋给x。 (N

不需要运行时计算

gcc的不同版本(以及clang版本)的结果:

  • 最新版本( clang-3.4 result(预先计算):0.001532 ms
  • 旧版本( gcc-4.4.0 result(预先计算):0.013517 ms
  • 旧版本( gcc-4.6.4 result(预先计算):0.001 ms
  • 较新版本( gcc-4.8.0 + )不会在编译时计算result1313.78 ms !!。

问题:

  • 4.8.1中是否省略了此优化?为什么?
  • 是否有任何编译器命令/开关启用它(如果它默认禁用)?
  • 如果省略,我该如何强制编译器进行预先计算?


注意(1):我测试了-O2-O3个开关,没有效果。

注意(2):强制,我指的是编译器的命令和开关。

注意(3):函数s只是一个例子,可以用更复杂的函数代替。

4 个答案:

答案 0 :(得分:13)

我已将其作为错误提交。是的,这是版本4.8中的回归,已在5周前的较新版本中修复。请点击此处:

答案 1 :(得分:7)

您可以使用新的C ++ 11 constexpr关键字强制它在编译时运行。

首先,您必须将迭代转换为递归(此要求在C ++ 1y中删除),例如:

constexpr unsigned long long s(unsigned long long n)
{
    return n? n + s(n-1): 0;
}

或者使用尾递归(当输入是可变的时,仍适用于运行时计算):

constexpr unsigned long long s_impl( unsigned long long accum, unsigned long long n, unsigned long long n_max )
{
    return (n < n_max)? s_impl(accum + n + 1, n + 1, n_max): accum;
}
constexpr unsigned long long s(unsigned long long n)
{
    return s_impl(0, 0, n);
}

(在C ++ 1y中,您需要做的就是将constexpr关键字添加到现有实现中)

然后用

调用它
constexpr auto x = s(N);

答案 2 :(得分:3)

在编译时处理计算的C ++ 11方法是使用constexpr。可悲的是,constexpr函数在可以做的事情上有些局限。在C ++ 11中,允许constexpr函数包含空语句,static_assert()声明,typedefusing声明/指令,以及一个{{1} - 语句(我暂时感到困惑,因为我正在查看规则放宽的C ++ 14草案)。也就是说,你需要递归地表达你的函数。从好的方面来说,如果使用常量表达式调用return函数,则会在编译时对其进行求值。

除此之外,您可能希望通过常量折叠来帮助编译器。例如,它可以帮助

  1. 使函数constexpr成为s()函数。
  2. inline声明为N
  3. 确保使用合适的优化级别。

答案 3 :(得分:2)

  

4.8.1中是否省略了此优化?

看起来它已经消失了。它仍然存在于4.7.2中。

  

为什么呢?   [来自你的一条评论:]我认为优化非常好,不会伤害任何东西。

这很可能是偶然的,gcc开发人员对此并不了解。

我能想出一个很好的理由,为什么我想至少提供这个优化的上限。我在2009年被MSVC咬了一下:当我给它一台机器生成的C代码时,它试图优化它并且编译器在几分钟内挣扎。显然,它正在拼命尝试进行一些优化,这些优化应该以某种方式受到限制,以便编译器不会在7KB源文件上花费几分钟。我的观点是:您可能希望限制可能会过多地增加编译时间的优化。

然而,这似乎并非如此。我已经尝试了相当小的N s,并且也没有执行此优化。

  

如果省略,我该如何强制编译器进行预先计算?   注意(2):强制,我的意思是编译器的命令和开关

我无法欺骗gcc 4.8.1进行此优化。如果没有人很快说出这是一个已知问题,或者可以使用某些编译器标志启用它,我将提交一个bug报告。