【问题标题】:vector push_back over std::copy在 std::copy 上的向量 push_back
【发布时间】:2016-04-03 08:33:10
【问题描述】:

我有一个函数,它有一个无序集作为参数。由于我使用的是 openmp,因此我将这个无序集转换为 vector 。我使用 std::copy 进行此转换。

//pseudo code
func( std::unorderedset s1)
begin
    vector v1;
    std::copy(s1.begin,s2.end,std::back_inserter(v1.end());
#openmp scope
    for( i = 0 ; i < v1.size(); i++ )
    {
         //accessing v1(i)
    }
end

但是我觉得 std::copy 是一项昂贵的操作。所以我的想法是,如果我创建一个类变量向量并在更新我的 set 时继续填充这个向量,我可以完全避免这个 std::copy operation 。由于向量的 push_back 操作的时间复杂度被摊销 O(1)。你有什么建议?

【问题讨论】:

  • 另请注意,您可以直接将向量构造为vector v1(s1.begin(), s1.end());,因为构造函数的重载需要两个迭代器
  • @CoryKramer 但它会再次涉及会员副本。我的意思是它会比 std::copy 快吗?
  • 如果在填充 unorderedset 之前不需要使用向量,我认为并行填充它们是没有用的。最好在填充之前正确reserve 向量。您的方法不会这样做,但可以轻松更改为。我很确定 CoryKramer 建议的版本确实在填充之前正确保留。
  • 除了有感觉之外,你能证明你声称std::copy() 很贵的说法吗? std::push_back() 可能是 O(1),但添加 n 元素(比如说,在循环中)是 O(n),所以你在这里描述的推理是有缺陷的。
  • @sameerkarjatkar:将数据从 std::unorderd_set 获取到 std::vector 始终是 O(n) 操作。您可以使用reserve 对其进行改进。请注意,带有back_inserter_iteratorstd::copy 只是一个push_back 循环。自己编写循环并不昂贵。

标签: c++ vector openmp push-back


【解决方案1】:

std::back_insert_iterator 调用std::vector::push_back,所以你的提议没有任何改进。

重要的是您事先知道v1 的大小,因此利用该信息并让std::vector 分配其存储空间只分配一次避免在v1.size() == v1.capacity() 时重新分配std::push_back

这样做:

std::vector<T> v1;
v1.reserve(s1.size());
std::copy(s1.begin(), s2.end(), std::back_inserter(v1));

或者这个:

std::vector<T> v1(s1.size());
std::copy(s1.begin(), s2.end(), v1.begin());

或者,正如@CoryKramer 所建议的,惯用地从一个范围构造v1

std::vector<T> v1(s1.begin(), s1.end());

更新:

所有三个版本都执行s1.size()T 副本数。然而,当在 GCC 上使用 T = int10^7 元素进行测量时,它表明 std::vector::reserve 是最快的方法(是范围构造的两倍,因为 std::distanceForwardIterators 具有 线性复杂性与std::unordered_set::size具有常量)。当处理较少和非常大的对象时,这种差异会减小,但仍然存在。

第二种方式比第一种方式稍慢,因为对元素进行了值初始化。

结论:使用std::vector::reserve

【讨论】:

    【解决方案2】:

    你可以使用这个来提高性能:

    func( std::unorderedset s1)
    begin
        vector v1;
        v1.reserve(s1.size()); // HERE
        std::copy(s1.begin,s2.end,std::back_inserter(v1.end());
    #openmp scope
        for( i = 0 ; i < v1.size(); i++ )
        {
             //accessing v1(i)
        }
    end
    

    但是,复制对象的成本是您必须处理的问题

    【讨论】:

      猜你喜欢
      • 2013-10-26
      • 1970-01-01
      • 2014-05-29
      • 1970-01-01
      • 2022-01-14
      • 2013-03-28
      • 1970-01-01
      • 2021-08-28
      • 2021-08-25
      相关资源
      最近更新 更多