Перейти к содержанию

Последнее обновление:2026-10-12

Тест производительности SSO в C++ string

Сегодня хочу задать вам два вопроса:

1.Есть ли разница в производительности у приведённого ниже кода?

std::string s1("hello world hel");
std::string s2("hello world helo");

2.Чему равен sizeof(std::string)?

Давайте вместе разберём эти два вопроса.

Поскольку ответы на них различаются для g++(libstdc++) и clang(libc++), разберём их по отдельности.

1.g++(libstdc++)

За исходный код g++ возьмём g++-10; подробный код можно посмотреть по этому адресу на github — здесь приведу лишь наиболее ключевые фрагменты.

https://github.com/gcc-mirror/gcc/blob/devel/omp/gcc-10/libstdc%2B%2B-v3/include/bits/basic_string.h#L171

template<typename _CharT, typename _Traits, typename _Alloc>
class basic_string {
  private:
    struct _Alloc_hider : allocator_type {
        pointer _M_p; // The actual data.
    };
      enum { __min_cap = 15 / sizeof(value_type) };
    _Alloc_hider    _M_dataplus;             //  8 bytes
    size_type __size_;                     //  8 bytes

    union {
            _CharT           _M_local_buf[_S_local_capacity + 1]; // 16 bytes 
            size_type        _M_allocated_capacity;                             // 8 bytes
    }; // 16 bytes
}; // 32 bytes

В комментариях к приведённому коду подробно перечислены байты, занимаемые string (обратите внимание: value_type здесь считается для char, то есть sizeof(value_type) = 1).

sizeof(string) = 8 + 8 + 16 = 32.

Из них 8 байт на ptr, 8 байт на size и 16 байт на union.

С точки зрения производительности здесь реализована SSO: локальный буфер хранит максимум 15 символов, а при превышении 15 символов происходит выделение в heap; я написал benchmark, чтобы сравнить производительность.

  • Содержимое короткой строки для теста: "hello world hel", всего 15 символов.
  • Содержимое длинной строки для теста: "hello world helo", всего 16 символов.
std::string s1("hello world hel");
std::string s2("hello world helo");

Видно, что разница в производительности огромна — всего один символ настолько меняет производительность std::string!

По сути, дело всё в том же SSO local buffer.

Тест производительности: https://quick-bench.com/q/s7pvDB_SwQyDCcZk50odAaYIzMc

gcc short long

2.clang++(libc++)

Аналогично возьмём llvm-14; если хотите посмотреть полный код — откройте адрес на github ниже, здесь я приведу ключевые фрагменты.

https://github.com/llvm/llvm-project/blob/release/14.x/libcxx/include/string#L765

template<class _CharT, class _Traits, class _Allocator>
class basic_string {
  private:
    struct __long {
        size_type __cap_;               // 8 bytes
        size_type __size_;              // 8 bytes
        pointer   __data_;              // 8 bytes
    };

        enum {__min_cap = (sizeof(__long) - 1)/sizeof(value_type) > 2 ?
                      (sizeof(__long) - 1)/sizeof(value_type) : 2};

    struct __short {
        union {
            unsigned char __size_;      // 1 bytes
            value_type __lx;            // 1 bytes 
        };                              // 1 bytes
        value_type __data_[__min_cap];  // 23 bytes
    };                                  // 24 bytes

        union __ulx {
            __long __lx;        // 24 bytes
            __short __lxx;  // 24 bytes
        }; // 24 bytes
}

В комментариях к приведённому коду подробно перечислены байты, занимаемые string (обратите внимание: value_type здесь считается для char, то есть sizeof(value_type) = 1).

sizeof(string) равен размеру union; этот union занимает 24 байта, поэтому итог равен 24.

Что касается SSO, локальный буфер хранит не более 22 символов, а при превышении 22 символов происходит выделение в heap.

Поэтому, когда мы применили на clang тот же тестовый код, что и для gcc, результат получился несколько иным: производительность для long-строки почти не отличается от производительности для short-строки. Причина в том, что тестовые сценарии gcc укладывались в 22 символа — все они были короткими.

Тест производительности: https://quick-bench.com/q/nmLMqQZ31cmWBbbIFr2nHN2JL1k

llvm short long no

Когда же мы тестируем по-настоящему длинные и короткие строки, производительность меняется заметно, и тренд совпадает с gcc.

  • Содержимое короткой строки для теста: "hello world hello worl", всего 22 символа.
  • Содержимое длинной строки для теста: "hello world hello world", всего 23 символа.

Тест производительности: https://quick-bench.com/q/ujtMrtotEi6RrpfMDJ1UcSLFIJE

llvm short long

Похожие темы

Комментарии