你能解释一下为什么计算时间与以下代码存在差异(未优化)。我怀疑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.out 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.out 0.32s用户0.00s系统99%cpu 0.325总计
答案 0 :(得分:2)
我怀疑RVO对移动构造,但我不太确定。
是的,这几乎肯定是正在发生的事情。第一种情况是从函数的返回值初始化变量:在这种情况下,可以通过使函数初始化它来省略移动。第二种情况是从返回值中移出 - 分配;作业不能被省略。我相信GCC即使在优化级别为零也会执行省略,除非您明确禁用它。
在最后一种情况下(-O3
,现已从问题中删除),编译器可能会注意到循环没有副作用,并将其完全删除。
通过声明向量volatile
并使用优化进行编译,您可能(或可能不)获得更有用的基准。这将迫使编译器在每次迭代时实际创建/分配它,即使它认为它更清楚。
在初始化非POD数据时,循环中的自动声明被视为不良做法吗?
没有;如果有的话,在最狭窄的范围内声明事物被认为是更好的做法。因此,如果仅在循环中需要它,则在循环中声明它。在某些情况下,通过在循环外声明一个复杂的对象来避免在每次迭代中重新创建它,可以获得更好的性能。但只有在你确定性能优势(a)存在且(b)值得失去地方时才这样做。
答案 1 :(得分:1)
我没有看到您的示例与auto
有任何关系。你写了两个不同的程序。
虽然
for (size_t i = 0; i < 1000000; ++i)
auto f = foo();
相当于
for (size_t i = 0; i < 1000000; ++i)
std::vector<int> f = foo();
- 这意味着,您创建一个新的向量(并销毁旧向量)。是的,在你的foo
- 使用RVO的实现中,但这不是重点:你仍然在外部循环为vector
腾出空间的地方创建一个新的f
。
摘录
std::vector<int> f;
for (size_t i = 0; i < 1000000; ++i)
f = foo();
将分配用于现有的向量。并且,是的,对于RVO,它可能会变成 move-assign ,具体取决于foo
,并且它适用于您的情况,因此您可以预期它会很快。但它仍然 是另一回事 - 它始终是负责管理资源的f
。
但是你在这里表现得很漂亮的是遵循一般规则通常是有意义的
声明变量尽可能接近其使用。