为什么 Rust 字符串没有短字符串优化 (SSO)?

Jan*_*tke 6 string rust

我查看了 的源代码String,发现它是根据 实现的Vec,它没有任何形式的小对象优化:

pub struct String {
    vec: Vec<u8>,
}
Run Code Online (Sandbox Code Playgroud)

来自 C++,其中每个主要标准库都使用短字符串优化 (SSO) std::string,这是非常令人惊讶的。字符串的许多用例都涉及非常短的字符串,例如:

  1. 如果您正在编写编译器,您将拥有关键字和标记的字符串,例如"==", "pub","delete"
  2. 如果您要对枚举进行字符串化,则所有常量名称通常都足够短以适合 SSO 缓冲区
  3. 如果您使用格式字符串打印内容,则格式字符串很少会太长以至于不适合 SSO
  4. 如果您使用键和值解析配置文件,则键和值通常都很短,例如setting: enabled
  5. 如果您要存储正则表达式,它们通常也适合 SSO
  6. 如果您要存储字典,则几乎可以对整个字典进行 SSO,因为例如英语单词相当短

鉴于此,默认情况下不使用任何 SSO 的理由是什么String?是否可以追溯添加该功能?是否有任何分析数据可以证明 SSO 是否有帮助?

C++ 中 SSO 的注释

SSO 是通过重用容器的内存来完成的std::string,否则容器将存储指针、大小和容量来存储:

  • 内部字符串的大小(可以只是一个字节)
  • 容器中的字符串数据(通常最大长度约为 20 字节)

也有可能只重用容量,并且有一个指向字符串对象内部的指针。所有这些通常都是通过 , 完成的union,并且在 Rust 中也是可能的。

Cha*_*man 3

SSO 并不总是成功 - 它以牺牲长字符串为代价来优化短字符串。Rust 更喜欢具有一致的性能特征,尤其是在标准库中,并让外部板条箱处理其他情况。这样,标准库的用户就不会感到悲观,如果需要,他们可以使用外部板条箱,并且仍然享受针对其特定用例的优化。

此外,Rust 中的 SSO 可能比 C++ 中的 SSO 更昂贵:虽然我不知道 C++ 中的标准库是否实际使用此功能,但 C++ 具有移动构造函数 - 因此,可以有一个指向数据的指针,无论数据存储在堆或堆栈。这样,访问数据就可以无分支了。然而,Rust 无法做到这一点,因为当对象移动时,指向内联存储的指针必须更新 - 但在 Rust 中,移动始终是简单的 memcpy。

此外,虽然 Rust 可以定义String具有 SSO,但现在这是不可能的,因为它保证缓冲区将始终存储在堆上,并且String::into_bytes()(返回Vec<u8>)保证不复制数据

  • 并非每个实现都对无分支代码使用内部指针:libstdc++ 有一个内部指针,但 libc++ 在 SSO 中使用分支。哪一个更好是有争议的,因为内部指针可以为您节省一个分支,但是这个分支是可以预测的,并且内部指针占用了大量本可以用于字符串数据的空间(可能是 8 个字节)。 (2认同)