在std :: string的上下文中首字母缩略词SSO的含义

Rae*_*ald 141 c++ string optimization

关于优化和代码风格的C++问题中,在优化副本的上下文中,有几个答案提到了"SSO" std::string.在这种情况下,SSO意味着什么?

显然不是"单点登录"."共享字符串优化",或许?

Dav*_*one 191

背景/概述

对自动变量的操作("来自堆栈",这是您在不调用malloc/的情况下创建的变量new)通常比涉及自由存储("堆",即使用创建的变量)的操作快得多new.但是,自动数组的大小在编译时是固定的,但是来自免费存储的数组的大小不是.此外,堆栈大小有限(通常为几MiB),而免费存储仅受系统内存的限制.

SSO是短/小字符串优化.A std::string通常将字符串存储为指向免费存储("堆")的指针,它提供了与调用时相似的性能特征new char [size].这可以防止非常大的字符串堆栈溢出,但它可能会更慢,尤其是对于复制操作.作为优化,许多实现std::string创建一个小型自动数组,类似于char [20].如果您有一个20个字符或更小的字符串(给定此示例,实际大小不同),它会将其直接存储在该数组中.这样就完全无需拨打电话new,从而加快了工作速度.

编辑:

我并不认为这个答案会如此受欢迎,但是既然如此,让我给出一个更现实的实现,但需要注意的是我从来没有真正阅读任何"疯狂"的SSO实现.

实施细节

至少std::string需要存储以下信息:

  • 尺寸
  • 容量
  • 数据的位置

大小可以存储为std::string::size_type指向结尾的指针或指针.唯一的区别是你是否想要在用户调用时减去两个指针,size或者在用户调用时添加size_type一个指针end.容量也可以以任何方式存储.

你不支付你不使用的东西.

首先,根据我上面概述的内容考虑天真的实现:

class string {
public:
    // all 83 member functions
private:
    std::unique_ptr<char[]> m_data;
    size_type m_size;
    size_type m_capacity;
    std::array<char, 16> m_sso;
};
Run Code Online (Sandbox Code Playgroud)

对于64位系统,这通常意味着std::string每个字符串有24个字节的"开销",另外还有16个用于SSO缓冲区(由于填充要求,此处选择16个而不是20个).如我的简化示例所示,存储这三个数据成员加上本地字符数组是没有意义的.如果m_size <= 16,那么我将把所有数据都放入m_sso,所以我已经知道了容量,我不需要指向数据的指针.如果m_size > 16,那我就不需要了m_sso.在我需要所有这些的地方绝对没有重叠.一个不浪费空间的更智能的解决方案看起来更像这样(未经测试,仅用于示例目的):

class string {
public:
    // all 83 member functions
private:
    size_type m_size;
    union {
        class {
            // This is probably better designed as an array-like class
            std::unique_ptr<char[]> m_data;
            size_type m_capacity;
        } m_large;
        std::array<char, sizeof(m_large)> m_small;
    };
};
Run Code Online (Sandbox Code Playgroud)

我假设大多数实现看起来更像这样.

  • 以下是一些实际实现的很好解释:http://stackoverflow.com/a/28003328/203044 (7认同)
  • 除了使复制更便宜之外,SSO 还有两个好处。第一个是如果您的字符串大小适合小缓冲区大小,则不需要在初始构造时分配。第二个是当一个函数接受一个 `std::string const &amp;` 时,获取数据是一个单一的内存间接,因为数据存储在引用的位置。如果没有小的字符串优化,访问数据将需要两次内存间接访问(首先加载对字符串的引用并读取其内容,然后第二次读取字符串中数据指针的内容)。 (5认同)

Mar*_*som 32

SSO是"小字符串优化"的缩写,这是一种技术,其中小字符串嵌入字符串类的主体中,而不是使用单独分配的缓冲区.


Hug*_*ira 12

正如其他答案所解释的那样,SSO意味着小/短字符串优化.这种优化背后的动机是无可否认的证据,即应用程序通常处理比较长字符串更短的字符串.

正如David Stone 在上面的回答中所解释的,std::string该类使用内部缓冲区来存储内容到达给定长度,这消除了动态分配内存的需要.这使代码更高效,更快捷.

这个其他相关的答案清楚地表明,内部缓冲区的大小取决于std::string实现,这在不同平台之间有所不同(参见下面的基准测试结果).

基准

这是一个小程序,用于对具有相同长度的许多字符串的复制操作进行基准测试.它开始打印时间来复制1000万个长度为1的字符串.然后用长度为2的字符串重复它.它一直持续到长度为50.

#include <string>
#include <iostream>
#include <vector>
#include <chrono>

static const char CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const int ARRAY_SIZE = sizeof(CHARS) - 1;

static const int BENCHMARK_SIZE = 10000000;
static const int MAX_STRING_LENGTH = 50;

using time_point = std::chrono::high_resolution_clock::time_point;

void benchmark(std::vector<std::string>& list) {
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

    // force a copy of each string in the loop iteration
    for (const auto s : list) {
        std::cout << s;
    }

    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
    std::cerr << list[0].length() << ',' << duration << '\n';
}

void addRandomString(std::vector<std::string>& list, const int length) {
    std::string s(length, 0);
    for (int i = 0; i < length; ++i) {
        s[i] = CHARS[rand() % ARRAY_SIZE];
    }
    list.push_back(s);
}

int main() {
    std::cerr << "length,time\n";

    for (int length = 1; length <= MAX_STRING_LENGTH; length++) {
        std::vector<std::string> list;
        for (int i = 0; i < BENCHMARK_SIZE; i++) {
            addRandomString(list, length);
        }
        benchmark(list);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果你想运行这个程序,你应该这样做,./a.out > /dev/null以便不计算打印字符串的时间.重要的数字打印到stderr,因此它们将显示在控制台中.

我用MacBook和Ubuntu机器的输出创建了图表.请注意,当长度达到给定点时,复制字符串的时间会有很大的跳跃.那是字符串不再适合内部缓冲区并且必须使用内存分配的时刻.

还要注意,在linux机器上,当字符串的长度达到16时发生跳转.在macbook上,当长度达到23时跳转.这证实SSO依赖于平台实现.

Ubuntu的 Ubuntu的SSO基准测试

Macbook Pro Macbook Pro上的SSO基准测试