C++ 自动入环和优化
你能解释一下为什么以下代码(未优化)在计算时间上存在如此大的差异吗。我怀疑RVO vs move construction,但我不是很确定 一般来说,遇到这种情况时,最佳做法是什么?在初始化非POD数据时,循环中的自动声明是否被视为不好的做法 在循环内使用自动:C++ 自动入环和优化,c++,loops,optimization,c++11,auto,C++,Loops,Optimization,C++11,Auto,你能解释一下为什么以下代码(未优化)在计算时间上存在如此大的差异吗。我怀疑RVO vs move construction,但我不是很确定 一般来说,遇到这种情况时,最佳做法是什么?在初始化非POD数据时,循环中的自动声明是否被视为不好的做法 在循环内使用自动: std::vector<int> foo() { return {1,2,3,4,5}; } int main() { for (size_t i = 0; i < 1000000; ++i)
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;
}
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;
}
std::vector foo()
{
返回{1,2,3,4,5};
}
int main()
{
对于(大小i=0;i<1000000;++i)
自动f=foo();
返回0;
}
输出:
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;
}
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.17s用户0.00s系统97%cpu 0.177总计
循环外的向量实例:
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;
}
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;
}
std::vector foo()
{
返回{1,2,3,4,5};
}
int main()
{
std::向量f;
对于(大小i=0;i<1000000;++i)
f=foo();
返回0;
}
输出:
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;
}
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总计
我怀疑RVO vs move construction,但我不是很确定
是的,这几乎可以肯定正在发生的事情。第一种情况下,move从函数的返回值初始化变量:在这种情况下,可以通过使函数在适当的位置初始化来省略move。第二个案例移动从返回值赋值;任务不能省略。我相信GCC即使在优化级别为零时也会执行省略,除非您明确禁用它
在最后一种情况下(使用-O3
,现在已从问题中删除),编译器可能会注意到循环没有副作用,并将其完全删除
通过声明向量volatile
并进行优化编译,您可能(也可能不会)获得更有用的基准测试。这将迫使编译器在每次迭代中实际创建/分配它,即使它认为自己知道得更好
在初始化非POD数据时,循环中的自动声明是否被视为不好的做法
没有;如果说有什么区别的话,在需要的最窄范围内声明内容被认为是更好的做法。因此,如果它只在循环中需要,请在循环中声明它。在某些情况下,通过在循环外部声明一个复杂的对象来避免在每次迭代中重新创建它,可以获得更好的性能;但是,只有当您确信性能优势(a)存在并且(b)值得损失本地性时,才可以这样做。我认为您的示例与
自动
没有任何关系。你写了两个不同的程序
当
使用将指定给现有向量。是的,使用RVO时,它可能会变成一个移动分配,这取决于foo
,这是您的情况,因此您可以预期它会很快。但这仍然是另一回事——负责管理资源的总是f
但是你在这里展示的非常漂亮的是,遵循一般规则通常是有意义的
声明尽可能接近其用途的变量
请参见在一个循环中有声明和初始化,而在另一个循环中只有赋值。这是有意的吗?如果你在比较橙子和橙子,你不应该在循环中移动
std::vector f
?如果你怀疑移动构造快得多,使用你自己的vector类,将移动构造函数向前移动到复制构造函数,然后再次测量。或者看看这个组件,看看它在做什么。或者在类的复制/移动构造函数和赋值运算符中添加计数器,看看实际调用的是什么。任何具有良好优化“技能”的编译器都可以优化所有循环。没有优化的“基准测试”毫无意义。让我猜猜:您测量的是调试版还是未优化版?谢谢!非常有用,尤其是最后一句:)