简体   繁体   English

为什么std :: vector是连续的?

[英]Why is std::vector contiguous?

Besides the fact that the standard defines it to be contiguous, why is std::vector contiguous? 除了标准将其定义为连续的事实之外,为什么std :: vector是连续的?

If it runs out of space, it needs to reallocate a new block and copy the old block to the new one before continuing. 如果空间不足,则需要重新分配新块并将旧块复制到新块,然后再继续。

What if it wasn't contiguous? 如果它不连续怎么办? When the storage fills up, it would just allocate a new block and keep the old block. 当存储填满时,它只会分配一个新块并保留旧块。 When accessing through an iterator, it would do simple >, < checks to see which block the index is in and return it. 当通过迭代器访问时,它会执行简单的>,<检查以查看索引所在的块并将其返回。 This way it doesnt need to copy the array every time it runs out of space. 这样,每次空间不足时都不需要复制数组。

Would this really work/be better? 这会真的有用吗? or am i missing something? 还是我错过了什么?

If std::vector didn't guarantee contiguousness, a new container would be invented which did. 如果std::vector不能保证连续性,那么就会发明一个新的容器。

The contiguity guarantee makes it easier to inter-operate with existing code that expects a contiguous array, and also gives very good performance because it is cache-friendly. 连续性保证使得更容易与期望连续阵列的现有代码进行互操作,并且由于它是缓存友好的,因此也提供了非常好的性能。 (Inserting/deleting in the middle is in practice very fast for moderate sizes because of this.) (因此,在中间插入/删除对于中等大小来说非常快。)

Copying the array on expansion is surprisingly cheap - if you append to a vector a million elements one at a time, each element will have been copied on average around once. 在扩展上复制数组是非常便宜的 - 如果你一次向一个向量追加一百万个元素,那么每个元素平均会被复制一次。

The standard C++ library defines a non-contiguous array-like container, too: std::deque<T> . 标准C ++库也定义了一个非连续的类似数组的容器: std::deque<T> Iteration over a std::deque<T> is much slower than iterating over a std::vector<T> . std::deque<T>迭代比在std::vector<T>迭代要慢得多。 If the operation is fairly trivial, it may be something like 5 times slower: these are actual times I get when comparing accumulating a sequence of integers. 如果操作相当简单,它可能会慢5倍:这是我在比较累积整数序列时得到的实际时间。 This is the cost you are paying for a non-contiguous representation! 这是您为非连续表示支付的费用!

The reason for this fairly steep slowdown is that gcc knows how to vectorize the loop over a std::vector<int> but not for a std::deque<int> . 这种相当陡峭的减速的原因是gcc知道如何在std::vector<int>上矢量化循环,而不是std::deque<int> Even without vectorization the iteration is about 30% slower. 即使没有矢量化,迭代也会慢约30%。 That is, the fairly small cost of std::vector<T> 's re-allocations actually don't really matter that much! 也就是说, std::vector<T>重新分配的相当小的成本实际上并不重要!

There are a few reasons for this: 这有几个原因:

First, iteration over a contiguous container is a lot faster than over a non-contiguous one due to two factors: the first is branch prediction - the processor doesn't need to throw away its pipeline every time you finish reading one of the sub-containers, and less pipeline resets means faster code. 首先,由于两个因素,对连续容器的迭代要比非连续容器快得多:第一个是分支预测 - 处理器不需要在每次完成读取其中一个子系统时丢弃其管道容器和更少的管道重置意味着更快的代码。 The second is that it's much easier to fully cache a contiguous block of memory than a bunch of assorted small blocks, making it much more likely that your array is cached on its entirety. 第二个是完全缓存连续的内存块要比一堆各种小块容易得多,这使得你的阵列更有可能完全缓存。

Second, there's a lot of C++ code being written out there that has to interact with C code, and a lot of that code will expect a contiguous space of memory when receiving an array/buffer, because that's the least data structure implementation-dependent way to do it. 其次,有很多C ++代码必须与C代码进行交互,并且许多代码在接收数组/缓冲区时会期望连续的内存空间,因为这是与数据结构最不依赖的实现方式去做吧。 When you're interacting with code that expects buffers/arrays constantly, the overhead of converting your std::deque into an array takes its toll compared to the practically instantaneous passage of an std::vector to an array (which can be basically just giving a pointer to the internal array). 当你不断地与需要缓冲区/数组的代码进行交互时,将std::deque转换为数组的开销与std::vector到数组的实际瞬时通道相比(这基本上只是给出一个指向内部数组的指针)。

All of this justifies the existence of a contiguous container. 所有这些都证明存在一个连续的容器。 As others have said, when you don't need either fast iteration or contiguousness of memory, you can always use an std::deque . 正如其他人所说,当你不需要快速迭代或内存连续时,你总是可以使用std::deque

By making std::vector contiguous, it can be treated much like an array. 通过使std::vector连续,它可以像数组一样对待。 However, it's also resizable. 但是,它也可以调整大小。 Its size is definable at runtime, rather than compile time. 它的大小可以在运行时定义,而不是编译时。 Also, a vector can be used to allocate memory for functions that require a buffer. 此外,矢量可用于为需要缓冲区的函数分配内存。 The advantage of this is the memory will be free'd by the vector when it goes out of scope. 这样做的好处是当vector超出范围时, vector将释放内存。 For example, when using ReadFile a vector can be used to create a buffer.: 例如,使用ReadFile时,可以使用向量创建缓冲区:

unsigned int bytesRead = 0;
std::vector<char> buffer(fileSize);
// open file, etc.
ReadFile(hFileIn, buffer.data(), buffer.size(), &bytesRead, nullptr);

Note that data is new in C++11. 请注意, data是C ++ 11中的新增功能。 In older code you will probably seen an equivalent &(buffer.at(0)) or &(buffer[0]) which returns the address of the first element. 在旧代码中,您可能会看到等效的&(buffer.at(0))&(buffer[0]) ,它返回第一个元素的地址。

A std::deque would be a better fit for what you're describing. std::deque会更适合你所描述的内容。

As a complement to the other answers (they are quite complete), there is one situation when you do prefer vectors to not be contiguous: when you need to resize a vector concurrently. 作为其他答案的补充(它们非常完整),有一种情况,当你更喜欢向量不是连续的时候:当你需要同时调整向量的大小时。 That is why Intel Thread Building Block provides tbb::concurrent_vector, which is more or less what you said you would expect 这就是为什么英特尔®线程构建模块提供了tbb :: concurrent_vector,这或多或少就是你所说的

"When the storage fills up, it would just allocate a new block and keep the old block. When accessing through an iterator, it would do simple >, < checks to see which block the index is in and return it." “当存储填满时,它只会分配一个新块并保留旧块。当通过迭代器访问时,它会做简单的>,<检查索引所在的块并返回它。”

Then, a comparison between tbb::concurrent_vector and std::vector would give you a better understanding of the advantages (speed) and disadvantages (cannot grow std::vector concurrently) of contiguous memory. 然后,tbb :: concurrent_vector和std :: vector之间的比较将使您更好地理解连续内存的优点(速度)和缺点(不能同时增长std :: vector)。 I expect tbb::concurrent_vector to be better optimized than std::deque and that is why tbb::concurrent_vector vs std::vector is a more fair comparison. 我希望tbb :: concurrent_vector比std :: deque更好地优化,这就是为什么tbb :: concurrent_vector vs std :: vector是一个更公平的比较。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM