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

时间:2013-11-09 12:43:31

标签: c++ optimization c++11 containers stdvector

除了标准将其定义为连续的事实之外,为什么std :: vector是连续的?

如果空间不足,则需要重新分配新块并将旧块复制到新块,然后再继续。

如果不连续怎么办?当存储填满时,它只会分配一个新块并保留旧块。通过迭代器访问时,它会做简单的>,<检查索引所在的块并返回它。这样,每次空间用完时都不需要复制数组。

这会真的有用吗?或者我错过了什么?

5 个答案:

答案 0 :(得分:23)

如果std::vector不能保证连续性,那么就会发明一个新的容器。

连续性保证使得与期望连续数组的现有代码交互操作变得更容易,并且由于它是缓存友好的,因此也提供了非常好的性能。 (因此,在中间插入/删除对于中等大小来说非常快。)

在扩展上复制数组的成本非常低廉 - 如果你一次向一个向量追加一百万个元素,每个元素平均会被复制一次。

答案 1 :(得分:13)

标准C ++库也定义了一个非连续的类似数组的容器:std::deque<T>std::deque<T>上的迭代比迭代std::vector<T>要慢得多。如果操作相当简单,它可能会慢5倍:这是我在比较累积整数序列时得到的实际时间。这是您为非连续表示支付的费用!

这种相当陡峭的减速的原因是gcc知道如何在std::vector<int>而不是std::deque<int>上对循环进行矢量化。即使没有矢量化,迭代也会慢约30%。也就是说,std::vector<T>重新分配的相当小的成本实际上并不重要!

答案 2 :(得分:10)

这有几个原因:

首先,由于两个因素,对连续容器的迭代要比非连续容器快得多:第一个是分支预测 - 处理器不需要丢弃它的管道每次读完一个子容器,管道重置次数越少意味着代码越快。第二个是完全缓存一个连续的内存块比一堆各种小块容易得多,这使得你的阵列更有可能被整体缓存。

其次,有很多C ++代码必须与C代码进行交互,并且许多代码在接收数组/缓冲区时会期望连续的内存空间,因为这是最少的数据结构实现独立的方式来做到这一点。当您与不断期望缓冲区/数组的代码进行交互时,将std::deque转换为数组的开销与std::vector到数组的实际瞬时传递(可能是基本上只是给出一个指向内部数组的指针。)

所有这一切都证明存在一个连续的容器。正如其他人所说,当你不需要快速迭代或内存连续时,你总是可以使用std::deque

答案 3 :(得分:9)

通过使std::vector连续,它可以被视为一个数组。但是,它也可以调整大小。它的大小可以在运行时定义,而不是编译时。此外,矢量可用于为需要缓冲区的函数分配内存。这样做的好处是当vector超出范围时,内存将被释放。例如,使用ReadFile时,可以使用向量来创建缓冲区。:

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

请注意data是C ++ 11中的新增功能。在较旧的代码中,您可能会看到等效的&(buffer.at(0))&(buffer[0]),它返回第一个元素的地址。

std::deque更适合您所描述的内容。

答案 4 :(得分:3)

作为其他答案的补充(它们非常完整),有一种情况是你确实喜欢向量不连续:当你需要同时调整向量的大小时。这就是英特尔线程构建模块提供tbb :: concurrent_vector的原因,这或多或少就是你所说的你想要的

“当存储填满时,它只会分配一个新块并保留旧块。当通过迭代器访问时,它会做简单的&gt;,&lt;检查索引所在的块并返回它“。

然后,tbb :: concurrent_vector和std :: vector之间的比较将使您更好地理解连续内存的优点(速度)和缺点(不能同时增长std :: vector)。我希望tbb :: concurrent_vector比std :: deque更好地优化,这就是为什么tbb :: concurrent_vector vs std :: vector是一个更公平的比较。