【发布时间】:2018-04-19 01:28:17
【问题描述】:
我来这里是想问问我的看法是否真实。
我原本以为定义vector<T> v(size_t someSize, T init_value) 会调用vector<T>::reserve 之类的函数,而不是vector<T>::push_back。我在这里找到了一些与此相关的讨论:std::vector push_back is bottleneck,但这与它的想法略有不同。
运行一些实验,我注意到vector<T> v(size_t someSize, T init_value) 一直调用::push_back。这是真的?我有以下使用uftrace(https://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?!
vector<T>::reserve 最终也会调用vector<t>::push_back 吗? vector 有更快的版本吗?
以上为原帖。经过一些cmet之后,我测试了一个简单的版本,然后意识到我完全错了。
#include <vector>
#include <functional>
#include <queue>
#include <cassert>
using namespace std; // for the time being
int main () {
vector<int> v(10, 0);
return 0;
}
这实际上导致如下,不涉及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
很抱歉给您带来了困惑。是的,库实现如我们预期的那样工作,如果使用initial size 构建,它不涉及push_back。
【问题讨论】:
-
省略号 ("...") 在 c++ 中有特定的含义。在你的问题中特别是在代码块中使用它时要小心,因为它可能会导致混淆。
-
reserve的一个常见用例是预留一次,然后再预留几次push_back。这两个功能并不相互排斥,实际上经常一起使用。 -
@FrançoisAndrieux 你说得对,我应该多加注意
-
虽然标准对构造函数重载的工作方式没有太多限制,但我真的怀疑任何好的实现都会在不先保留大小的情况下做到这一点......你使用的是什么编译器/标准库?你的构建标志是什么?
-
你能提供你的实验代码吗?它是否在做任何其他可能导致
std::vector::push_back被调用的事情?我可以在 gcc 标准库源代码中看到您所指的构造函数调用std::vector::_M_fill_initialize,但我不确定它为什么会调用push_back。
标签: c++ performance vector