可以 std::vector 使用小缓冲区优化吗?

Posted

技术标签:

【中文标题】可以 std::vector 使用小缓冲区优化吗?【英文标题】:May std::vector make use of small buffer optimization? 【发布时间】:2012-01-01 17:17:33 【问题描述】:

我今天和我的同事想知道是否可以实现 std::vector 以利用小缓冲区优化。通过查看 C++11 草案,我阅读了 23.3.1p8

表达式 a.swap(b),对于标准容器类型而非数组的容器 a 和 b,应交换 a 和 b 的值,而不对单个容器元素调用任何移动、复制或交换操作。

起初这似乎禁止了小缓冲区优化,但在 as-if 规则下,我们仍然可以对非类类型进行小缓冲区优化(因为我们无法观察到正在完成的复制)。接下来的文字似乎更难“愚弄”

在交换之前引用一个容器中的元素的每个迭代器都应该在交换之后引用另一个容器中的相同元素。

这是否足以阻止为 std::vector 实现小缓冲区优化?是否有任何其他障碍,或者最终有可能在 SBO 中使用 std::vector?

【问题讨论】:

string 是否有相同的swap 子句? llvm libc++ 项目页面提到使用 SBO,这表明 (1) litb 引用的规则不适用于字符串,或者 (2) 有某种方法可以将 SBO 与不考虑这些规则的字符串,或者 (3) libc++ 作者在阅读标准的这一部分时会感到失望。 llvm libc++ 项目页面指的是字符串,而不是向量。我相信 Johannes 指出的交换/迭代器评论确实禁止向量的 SBO。 21.4.1/p6 特别允许 string::swap 使迭代器无效。 相关:***.com/q/2178281/103167 【参考方案1】:

23.2.1 / p10 / b6:

除非另有说明...

没有swap() 函数使任何引用被交换容器元素的引用、指针或迭代器无效。 ...

vector 没有“另外指定”。因此,vector 的 SBO 是非法的。

string 不受此规则的约束,因为它在 21.4.1/p6 中“另有说明”:

引用 a 元素的引用、指针和迭代器 basic_string 序列可能会因以下用途而失效 basic_string 对象:

作为任何标准库函数的参数,将非常量 basic_string 的引用作为参数。^234

234) 例如,作为非成员函数 swap() 的参数 (21.4.8.8)、operator>>() (21.4.8.9) 和 getline() (21.4.8.9),或作为 basic_string::swap() 的参数

【讨论】:

离题了,但我最近在看这篇文章,试图为move找到类似的保证。看起来很明显,但是需要move 将有效的迭代器传输到目标容器吗? 不,我不相信它是(但我的记忆是错误的)。如果你有迭代器或临时引用,你可能有点太棘手了。 :-) 但是,我很乐意看到有其他建议的激励代码。 但是move 不仅适用于临时人员,它对于您不想复制的持久结构也很有用。激励代码只是在局部变量中构建结构,然后将其移动到更永久的容器中。 (当然,它并不总是被移动,或者我只是在那里构建它开始。)【参考方案2】:

除了迭代器失效的问题,还有一个避免小缓冲区优化的安全参数。

如果写入超出std::vector,则会出现堆损坏,这很难预测什么会被覆盖,也很难利用任意代码执行。

如果缓冲区被嵌入到局部变量中,溢出会破坏堆栈,攻击者可能会获得对返回地址的控制,这会更有用(例如,return-to-libc 攻击)。

【讨论】:

相关性存疑:虽然攻击者可能更容易利用堆栈溢出,但从开发人员的角度来看,这两种情况无论如何都应该被视为完全不可取的。堆损坏并不是真的“更好”,它只是不同 - 不可接受。 @Leushenko:ASLR 并没有修复任何错误,但它仍然被认为是非常理想的深度安全防御。同上不可执行的堆栈。

以上是关于可以 std::vector 使用小缓冲区优化吗?的主要内容,如果未能解决你的问题,请参考以下文章

std :: deque(双端队列)真的是随机访问和恒定时间插入吗?

std::string,std::vector,std::accumulate注意事项

std::vector emplace_back 可以从向量本身的元素复制构造吗?

C++ std::vector 可以同时处理来自多线程的 push_back 吗?

使用std::vector优化点云动画显示一例

如何在 C++17 中将 std::string 转换为 std::vector<std::byte>?