测试旧gcc-4.4.0
和gcc-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版本)的结果:
0.001532 ms
。0.013517 ms
。0.001 ms
。1313.78 ms
!!。问题:
注意(1):我测试了-O2
和-O3
个开关,没有效果。
注意(2):强制,我指的是编译器的命令和开关。
注意(3):函数s
只是一个例子,可以用更复杂的函数代替。
答案 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()
声明,typedef
和using
声明/指令,以及一个{{1} - 语句(我暂时感到困惑,因为我正在查看规则放宽的C ++ 14草案)。也就是说,你需要递归地表达你的函数。从好的方面来说,如果使用常量表达式调用return
函数,则会在编译时对其进行求值。
除此之外,您可能希望通过常量折叠来帮助编译器。例如,它可以帮助
constexpr
成为s()
函数。inline
声明为N
答案 3 :(得分:2)
4.8.1中是否省略了此优化?
看起来它已经消失了。它仍然存在于4.7.2中。
为什么呢? [来自你的一条评论:]我认为优化非常好,不会伤害任何东西。
这很可能是偶然的,gcc开发人员对此并不了解。
我能想出一个很好的理由,为什么我想至少提供这个优化的上限。我在2009年被MSVC咬了一下:当我给它一台机器生成的C代码时,它试图优化它并且编译器在几分钟内挣扎。显然,它正在拼命尝试进行一些优化,这些优化应该以某种方式受到限制,以便编译器不会在7KB源文件上花费几分钟。我的观点是:您可能希望限制可能会过多地增加编译时间的优化。
然而,这似乎并非如此。我已经尝试了相当小的N
s,并且也没有执行此优化。
如果省略,我该如何强制编译器进行预先计算? 注意(2):强制,我的意思是编译器的命令和开关
我无法欺骗gcc 4.8.1进行此优化。如果没有人很快说出这是一个已知问题,或者可以使用某些编译器标志启用它,我将提交一个bug报告。