你能解释一下为什么下面的代码(没有优化)在计算时间上有如此大的差异吗?我怀疑是RVO与移动构造,但我不是很确定。
一般来说,遇到这种情况的最佳实践是什么?在初始化非POD数据时,循环中的自动声明是否被视为不良实践?
在循环中使用自动:
std::vector<int> foo()
{
return {1,2,3,4,5};
}
int main()
{
for (size_t i = 0; i < 1000000; ++i)
auto f = foo();
return 0;
}
输出:
./a.输出0.17s用户0.00s系统97% CPU总计0.177
循环外的向量示例:
std::vector<int> foo()
{
return {1,2,3,4,5};
}
int main()
{
std::vector<int> f;
for (size_t i = 0; i < 1000000; ++i)
f = foo();
return 0;
}
输出:
./a.输出0.32s用户0.00s系统99%cpu总计0.325
3条答案
按热度按时间w8biq8rn1#
我怀疑RVO与移动构造,但我不是很确定。
是的,这几乎是肯定的,第一种情况是从函数的返回值移动初始化一个变量:在这种情况下,移动可以通过使函数初始化它来省略。第二种情况是从返回值中进行move-assign;赋值不能省略。我相信GCC即使在优化级别为零时也会执行省略,除非你显式地禁用它。
在最后一种情况下(
-O3
,现在已经从问题中删除),编译器可能注意到循环没有副作用,并将其完全删除。通过声明向量
volatile
并使用优化进行编译,你可能会(也可能不会)得到一个更有用的基准测试,这将迫使编译器在每次迭代时实际创建/赋值它,即使它认为自己知道得更好。初始化非POD数据时,循环中的自动声明是否被视为不良实践?
不;如果有的话,在所需的最窄范围内声明对象被认为是更好的实践。因此,如果只在循环中需要,就在循环中声明它。在某些情况下,在循环外声明一个复杂的对象以避免在每次迭代中重新创建它可能会获得更好的性能;但只有在您确信(a)存在性能优势并且(b)值得牺牲局部性时才这样做。
bvjxkvbb2#
我看不出你的例子和
auto
有什么关系,你写了两个不同的程序。当
相当于
--这意味着,您创建一个新的向量(并销毁旧的向量)。是的,在您的
foo
-实现中使用RVO,但这不是这里的重点:您仍然在外部循环为f
腾出空间的地方创建一个新的vector
。该片段
使用assign到一个现有的向量。并且,是的,对于RVO,它可能变成一个move-assign,这取决于
foo
,在您的情况下,它是这样的,所以您可以期望它很快。但是它仍然是一个不同的东西-它总是f
负责管理资源。但你在这里非常漂亮地展示了遵循一般规则通常是有意义的
声明变量时应尽可能靠近它们的用途。
参见this Discussion
uoifb46i3#
我在电脑上测试了3个版本。手动优化的版本是最快的。
对于
-O2
,结果为: