1

您能解释一下为什么使用以下代码(未优化)的计算时间存在如此大的差异。我怀疑 RVO 与移动构造,但我不太确定。

一般来说,遇到这种情况时的最佳做法是什么?初始化非 POD 数据时,循环中的自动声明是否被视为不好的做法?

在循环内使用 auto :

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 user 0.00s system 97% cpu 0.177 total

循环外的向量实例:

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 user 0.00s system 99% cpu 0.325 total

4

2 回答 2

2

我怀疑 RVO 与移动构造,但我不太确定。

是的,几乎可以肯定这就是正在发生的事情。第一种情况是从函数的返回值中移动初始化一个变量:在这种情况下,可以通过使函数在适当的位置初始化它来省略移动。第二种情况从返回值移动赋值;任务不能省略。我相信 GCC 即使在优化级别为零时也会执行省略,除非您明确禁用它。

在最后一种情况下(带有-O3,现在已从问题中删除)编译器可能会注意到循环没有副作用,并将其完全删除。

volatile通过声明向量并进行优化编译,您可能(或可能不会)获得更有用的基准。这将迫使编译器在每次迭代时实际创建/分配它,即使它认为它知道得更好。

初始化非 POD 数据时,循环中的自动声明是否被视为不好的做法?

不; 如果有的话,在所需的最窄范围内声明事物被认为是更好的做法。因此,如果只在循环中需要它,请在循环中声明它。在某些情况下,您可以通过在循环外声明一个复杂对象来避免在每次迭代中重新创建它,从而获得更好的性能;但只有在您确定性能优势 (a) 存在并且 (b) 值得失去局部性时才这样做。

于 2013-06-12T12:03:50.267 回答
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是负责管理资源的人。

但是你在这里做得非常漂亮的是,遵循一般规则通常是有意义的

声明变量尽可能接近它们的用途。

看到这个讨论

于 2013-08-21T08:27:44.340 回答