vector <T> :: push_back用于预定义的构造函数?

use*_*286 5 c++ performance vector

我在这里问我的看法是否真实。我本来以为定义vector<T> v(size_t someSize, T init_value)将调用诸如之类的函数vector<T>::reserve,而不是vector<T>::push_back。我在这里找到了与此相关的讨论:std :: vector push_back是瓶颈,但这在思想上略有不同。

运行一些实验,我注意到一直都在vector<T> v(size_t someSize, T init_value)打电话::push_back。这是真的?我有以下使用uftracehttps://github.com/namhyung/uftrace)的报告。

     Avg total   Min total   Max total  Function
==========  ==========  ==========  ====================================
858.323 ms  858.323 ms  858.323 ms  main
618.245 ms  618.245 ms  618.245 ms  sortKaway
234.795 ms  234.795 ms  234.795 ms  std::sort
 72.752 us   72.752 us   72.752 us  std::vector::_M_fill_initialize
 65.788 us   49.551 us   82.026 us  std::vector::vector
 20.292 us   11.387 us   68.629 us  std::vector::_M_emplace_back_aux
 18.722 us   17.263 us   20.181 us  std::equal
 18.472 us   18.472 us   18.472 us  std::vector::~vector
 17.891 us   10.002 us  102.079 us  std::vector::push_back // push_back?!
Run Code Online (Sandbox Code Playgroud)

是否vector<T>::reserve还呼吁vector<t>::push_back最终?有更快的版本vector吗?


以上是原始帖子。经过一番评论,我测试了一个简单的版本,并意识到我完全是错误的。

#include <vector>
#include <functional>
#include <queue>
#include <cassert>
using namespace std; // for the time being

int main () {
    vector<int> v(10, 0);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

实际上,这会导致以下结果,而不涉及std::vector<T>::push_back

  # Function Call Graph for 'main' (session: 9ce7f6bb33885ff7)
  =============== BACKTRACE ===============
   backtrace #0: hit 1, time  12.710 us
     [0] main (0x4009c6)

  ========== FUNCTION CALL GRAPH ==========
    12.710 us : (1) main
     0.591 us :  +-(1) std::allocator::allocator
     0.096 us :  | (1) __gnu_cxx::new_allocator::new_allocator
              :  | 
     6.880 us :  +-(1) std::vector::vector
     4.338 us :  |  +-(1) std::_Vector_base::_Vector_base
     0.680 us :  |  |  +-(1) std::_Vector_base::_Vector_impl::_Vector_impl
     0.445 us :  |  |  | (1) std::allocator::allocator
     0.095 us :  |  |  | (1) __gnu_cxx::new_allocator::new_allocator
              :  |  |  | 
     3.294 us :  |  |  +-(1) std::_Vector_base::_M_create_storage
     3.073 us :  |  |    (1) std::_Vector_base::_M_allocate
     2.849 us :  |  |    (1) std::allocator_traits::allocate
     2.623 us :  |  |    (1) __gnu_cxx::new_allocator::allocate
     0.095 us :  |  |     +-(1) __gnu_cxx::new_allocator::max_size
              :  |  |     | 
     1.867 us :  |  |     +-(1) operator new
              :  |  | 
     2.183 us :  |  +-(1) std::vector::_M_fill_initialize
     0.095 us :  |     +-(1) std::_Vector_base::_M_get_Tp_allocator
              :  |     | 
     1.660 us :  |     +-(1) std::__uninitialized_fill_n_a
     1.441 us :  |       (1) std::uninitialized_fill_n
     1.215 us :  |       (1) std::__uninitialized_fill_n::__uninit_fill_n
     0.988 us :  |       (1) std::fill_n
     0.445 us :  |        +-(1) std::__niter_base
     0.096 us :  |        | (1) std::_Iter_base::_S_base
              :  |        | 
     0.133 us :  |        +-(1) std::__fill_n_a
Run Code Online (Sandbox Code Playgroud)

对困惑感到抱歉。是的,库实现可以像我们期望的那样工作,push_back如果使用构建,则不涉及initial size

小智 0

唷,看来你回答了你自己的问题!一时间我感到无比的困惑。只是假设,我可以想象一些vector's使用reserveand填充构造函数的模糊情况push_backs,但绝对不是像在 GNU 标准库中随 GCC 一起找到的高质量实现那样。我想说,假设,一个模糊的向量实现有可能以这种方式实现,但实际上完全不可能有任何像样的实现。

相反,这几乎是二十年前的事了,但我尝试实现我的版本,std::vector希望能够匹配它的性能。这不仅仅是一些愚蠢的练习,而是由于我们有一个软件开发工具包并希望为其使用一些基本的 C++ 容器这一事实而产生的诱惑,但它的目标是允许人们使用不同的方式为我们的软件编写插件编译器(以及不同的标准库实现)与我们使用的不同。因此,我们无法std::vector在这些上下文中安全地使用,因为我们的版本可能与插件编写者的版本不匹配。我们被迫不情愿地为 SDK 推出我们自己的容器。

相反,我发现它std::vector以难以匹配的方式非常高效,特别是对于具有简单的 ctor 和 dtor 的普通旧数据类型。这又是十多年前的事了,但我发现vector<int>在 MSVC 5 或 6(忘记了哪一个)中使用填充构造函数实际上转换为与memset我的幼稚版本使用的方式相同的反汇编,只是循环遍历事物并使用新的放置无论他们是否是 POD,他们都没有。范围向量还有效地转化memcpy为 POD 的超快速度。这正是让我很难击败 Vector 的原因,至少在当时是这样。如果不深入研究类型特征和特殊外壳 POD,我无法真正匹配 POD 的 Vector 性能。我可以将其与 UDT 相匹配,但我们的大多数性能关键型代码倾向于使用 POD。

因此,今天流行的矢量实现很可能与我进行这些测试时一样高效,甚至更高,而且我想以某种方式保证您的矢量实现很可能非常快。我期望它做的最后一件事是使用push_backs.