向量的小字符串优化?

34

我知道几乎所有的STL实现都有一个“小字符串”优化,即如果sizeof(characters) <= sizeof(pointers),则字符串会将实际字符数据存储在用于指针的内存中,而不是存储通常的起始、结束和容量3个指针。现在我有很多元素大小<= sizeof(pointer)的小向量,但需要动态调整大小并且可能会变得相当大。 但是,向量的中位数(而不是平均数)大小只有4-12个字节。因此,对向量进行“小字符串”优化对我来说非常有用。是否存在这样的优化?

我在考虑通过简单地将向量转换为字符串(即提供一个向量接口到字符串),来自行进行这项操作。这样做好吗?


你的问题不是很清楚。此外,你所说的“向量”接口指的是什么?你是在谈论一个特殊的svector类来保存小字符串吗? - dirkgently
不,我的意思是一个包含任意值的字符串,而不是char类型 - 就像一个向量。将向量接口应用于字符串意味着包装字符串对象并公开向量兼容接口,添加缺失的函数,如push_back。 - BuschnicK
难道使用分配器不是更好的选择吗?因为vector还需要知道它是处于“小型”还是“大型”模式,所以你甚至无法获得3个指针大小的内存。 - UncleBens
唯一执行您所提到的“空间优化”的标准库实现是Dinkumware的标准库,这是随Microsoft的Visual Studio一起提供的库。 - Billy ONeal
我从未尝试过,但如果有人这样做 template<class T> using small_vector = std::basic_string<T>;,是否可以得到非常接近小向量优化的东西?(至少对于pod类应该有效)。 - alfC
4个回答

28
Boost 1.58刚刚发布,其中的Container库有一个基于LLVM SmallVectorsmall_vector类。同时还有一个static_vector,它不能超出最初给定的大小。这两个容器都是头文件库。
Facebook的folly库也有一些很棒的容器。
它有一个small_vector,可以通过模板参数进行配置,以像boost的staticsmall向量一样工作。它还可以配置为使用小整数类型来进行内部大小记录,考虑到他们是Facebook,这并不令人惊讶:)
正在进行跨平台的工作,因此Windows / MSVC支持应该会在某一天实现...

17
你可以从LLVM中借用SmallVector的实现(仅头文件,位于LLVM\include\llvm\ADT)。

6
不再是仅头文件。 - user1804599
1
标题在这里:https://github.com/llvm-mirror/llvm/blob/master/include/llvm/ADT/SmallVector.h - Nick

3

数年前已经讨论过(那个帖子中的一些名字可能看起来有点熟悉:-)),但我不知道是否有现成的实现。 我不认为我会尝试将std::string适应这个任务。对于std::basic_string上的确切要求没有很好地进行说明,但标准非常清楚,它仅适用于行为类似于char的东西。对于实质上不同的类型,它可能仍然可以工作,但很难说会发生什么--它从未被设计为,并且可能没有测试过许多除小整数以外的类型。

完全符合规范的std::vector实现需要大量工作。但是,从头开始实现std::vector的可用子集(甚至包括小向量优化)通常不会特别困难。如果包括小向量优化,则我相当确定无法满足所有std::vector的要求。

特别是,在向量对象中存储实际数据的情况下交换或移动向量意味着您需要交换/移动实际数据项,而std::vector的要求则是基于其仅存储指向数据的指针,因此通常可以通过操作指针而不必实际触摸数据项本身来交换或移动其内容。因此,即使在操作数据项本身将会抛出异常的情况下,也需要能够这样做。因此,小向量优化将阻止满足这些要求。

另一方面,如上所述,std::string的要求之一是它只能存储不会引发异常的项。因此,如果std::string是一个可行的选项,实现自己的类似于vector的容器可能也不需要过多担心这些细节。


  1. 有一种情况,即使在真正的std::vector中,您也必须交换/移动实际数据项:如果两个向量使用不同的分配器,则必须通过该向量的分配器为目标中的对象分配空间。

15
从零开始正确地实现 std::vector 是出奇地困难。当然,如果你选择忽略异常安全性,那么它会变得简单得多,但这样就不再是一个有意义(或有用)的 vector 实现了。 - jalf
1
@jalf:的确,这不是一件简单的事情——并且有一个要求可能无法满足。即使T(T const&)和(或)swap(T,T)抛出异常,交换两个向量也不能抛出异常,除非分配器的复制构造函数抛出异常。我看不到在使用内置缓冲区时满足该要求的任何方法。 - Jerry Coffin
1
@Jerry:没错,我也认为这是做不到的。但至少这只是一个小问题,你可以在没有nothrow保证的情况下生存。 - jalf
关于向量最困难的部分之一是选择扩展内部数组的策略。 - phuclv
@jalf:关于对象大小的问题:你不能将std::vector包装在一个带有std::array<T,shortvector元素数量>的联合体中吗?这样你就可以使用容器的方法,并且“只需要”考虑从一个容器转换到另一个容器。 - Eike
显示剩余2条评论

2
如果T是POD类型,为什么不使用basic_string而使用vector?

4
请注意,您需要为POD类型编写一个std::char_traits特化(或等效的traits类)。需求并不是非常严格,除了需要指定一个特殊的“eof”值外,但我认为这可能有点繁琐。 - Steve Jessop
1
此外,不能保证 basic_string<MyPodType> 将使用小字符串优化,并且没有可移植的方法来指定元素数量,即使它确实使用了。 - Arne Vogel
1
你为什么建议误用字符串? - xaxxon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接