Почему реализация libc ++ std :: string занимает 3-кратную память как libstdc ++?

Рассмотрим следующую тестовую программу:

#include  #include  #include  int main() { std::cout << sizeof(std::string("hi")) << " "; std::string a[10]; std::cout << sizeof(a) << " "; std::vector v(10); std::cout << sizeof(v) + sizeof(std::string) * v.capacity() << "\n"; } 

Вывод для libstdc++ и libc++ соответственно:

 8 80 104 24 240 264 

Как вы можете видеть, libc++ занимает в 3 раза больше памяти для простой программы. Как различается реализация, которая вызывает дисбаланс в памяти? Нужно ли меня беспокоиться и как мне это решить?

Вот короткая программа, которая поможет вам изучить использование памяти типа std::string : stack и heap.

 #include  #include  #include  #include  std::size_t allocated = 0; void* operator new (size_t sz) { void* p = std::malloc(sz); allocated += sz; return p; } void operator delete(void* p) noexcept { return std::free(p); } int main() { allocated = 0; std::string s("hi"); std::printf("stack space = %zu, heap space = %zu, capacity = %zu\n", sizeof(s), allocated, s.capacity()); } 

Используя http://melpon.org/wandbox/, легко получить вывод для разных комбинаций компилятора / lib, например:

gcc 4.9.1:

 stack space = 8, heap space = 27, capacity = 2 

gcc 5.0.0:

 stack space = 32, heap space = 0, capacity = 15 

лязг / Libc ++:

 stack space = 24, heap space = 0, capacity = 22 

VS-2015:

 stack space = 32, heap space = 0, capacity = 15 

(последняя строка – http://webcompiler.cloudapp.net )

Вышеупомянутый вывод также показывает capacity , которая является мерой количества char может содержать строка, прежде чем она должна будет выделить новый, более крупный буфер из кучи. Для реализаций gcc-5.0, libc ++ и VS-2015 это мера буфера коротких строк . То есть буфер размера, выделенный в стеке, содержит короткие строки, что позволяет избежать более дорогого распределения кучи.

Похоже, что реализация libc ++ имеет наименьшее (использование стека) реализации коротких строк и все же содержит самый большой из буферов коротких строк. И если подсчитать общее использование памяти (стек + куча), libc ++ имеет наименьшее общее использование памяти для этой 2-символьной строки среди всех 4 этих реализаций.

Следует отметить, что все эти измерения были выполнены на 64-битных платформах. На 32-разрядном уровне использование стека libc ++ будет уменьшаться до 12, а буфер с небольшой строкой – до 10. Я не знаю поведения других реализаций на 32-битных платформах, но вы можете использовать приведенный выше код, чтобы узнать ,

Вы не должны беспокоиться, разработчики стандартных библиотек знают, что они делают.

Используя последний код из соединительной линии GCC subversion, libstdc ++ дает следующие цифры:

 32 320 344 

Это связано с тем, что с нескольких недель назад я переключил стандартную реализацию std::string для использования оптимизации небольших строк (с пространством для 15 символов) вместо реализации с копией на запись, с которой вы тестировали.

Описание: Похоже, что libstdc++ использует один char* . Фактически, он выделяет больше памяти.

Таким образом, вы не должны беспокоиться о том, что реализация libc++ Clang неэффективна в памяти.

Из документации libstdc ++ (в разделе Подробное описание ):

 A string looks like this: [_Rep] _M_length [basic_string] _M_capacity _M_dataplus _M_refcount _M_p ----------------> unnamed array of char_type 

Где _M_p указывает на первый символ в строке, и вы передаете его указателю на _Rep и вычитаете 1, чтобы получить указатель на заголовок.

Такой подход имеет огромное преимущество в том, что для строкового объекта требуется только одно распределение. Все уродство ограничено одной парой встроенных функций, каждая из которых компилируется в одну команду добавления: _Rep :: _ M_data () и string :: _ M_rep (); и функция распределения, которая получает блок необработанных байтов и с достаточным количеством места и создает объект _Rep спереди.

Причина, по которой вы хотите, чтобы _M_data указывала на массив символов, а не на _Rep, так что отладчик может видеть содержимое строки. (Вероятно, мы должны добавить не-встроенный элемент, чтобы использовать _Rep для отладчика, чтобы пользователи могли проверять фактическую длину строки.)

Таким образом, он просто выглядит как один char* но это вводит в заблуждение с точки зрения использования памяти.

Ранее libstdc++ основном использовал этот макет:

  struct _Rep_base { size_type _M_length; size_type _M_capacity; _Atomic_word _M_refcount; }; 

Это ближе к результатам libc++ .

libc++ использует «оптимизацию коротких строк». Точная компоновка зависит от того, определена ли _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT . Если он определен, указатель данных будет выравниваться по слову, если строка короткая. Подробнее см. В исходном коде .

Оптимизация коротких строк исключает распределение кучи, поэтому она также выглядит более дорогостоящей, чем реализация libstdc++ если вы рассматриваете только части, которые выделяются в стеке. sizeof(std::string) показывает только использование стека, а не общее использование памяти (стек + куча).

Я не проверял фактические реализации в исходном коде, но я помню, как это проверял, когда я работал над своей строковой библиотекой C ++. Обычная реализация строки в 24 байта. Если длина строки меньше или равна 16 байтам, вместо malloc’ing из кучи она копирует строку во внутренний буфер размером 16 байт. В противном случае он сохраняет и сохраняет адрес памяти и т. Д. Эта небольшая буферизация фактически помогает с точки зрения производительности рабочего времени.

Для некоторых компиляторов есть возможность отключить внутренний буфер.

  • Извлечение пар слов с помощью String.split ()
  • Как преобразовать шестнадцатеричную строку в строку java
  • Замените пробел на период в Bash
  • Преобразование std :: __ cxx11 :: string в std :: string
  • Преобразование String для двойного использования в Java
  • Как удалить пустые строки из форматированной строки?
  • Альтернатива нескольким String.Replaces
  • Строковые литералы соответствуют перегрузке bool вместо std :: string
  • StringBuilder vs Конкатенация строк в toString () в Java
  • Форматирование строки в столбцы
  • Как разбирать строку в nullable int
  • Interesting Posts

    У меня есть Tessellated Sphere OpenGL, и я хочу вырезать в ней цилиндрическое отверстие

    Размещение массива-new требует неопределенных накладных расходов в буфере?

    Mac OS X: как изменить цветную метку файлов с терминала

    разница между и на Android

    Получить поля модели в Django

    Поместите курсор в конец текста в EditText

    Что делать 1. # INF00, -1. # IND00 и -1. # IND означает?

    Являются ли a, & a, a, a , & a и & a одинаковыми указателями?

    NodeJS / express: код состояния кэша и 304

    Публикация ECMA-404 повлияла на достоверность текстов JSON, таких как «2» или «привет»?

    Смешивание области видимости в C #

    Обнаружение мертвого кода в устаревшем проекте C / C ++

    Http Переадресация на https Иногда приводит к «эта страница содержит другие ресурсы, которые не являются безопасными»

    pathPattern для соответствия расширению файла не работает, если существует какой-либо период в другом месте имени файла?

    Как передать ArrayList параметру метода varargs?

    Давайте будем гением компьютера.