#c #string #stl
#c #строка #stl
Вопрос:
Я расследую случай, когда потребляется намного больше памяти, чем необходимо. Если я присваиваю строку an std::vector
, она внезапно резервирует больше памяти кучи, чем необходимо, хотя размер строки уже известен:
Вот к чему я это сводил:
#include <vector>
#include <iostream>
#include <new>
void* operator new(size_t size) {
void * p = malloc(size);
std::cout << "talloc " << size << " @ " << p;
return p;
}
void operator delete(void* p) {
std::cout << "t free " << p;
free(p);
}
int main() {
{
std::cout << std::endl << "1. Create first string: ";
auto s1 = std::string{"String with 20 chars"};
std::cout << std::endl << "2. Create longer string: ";
auto s2 = std::string{"String with 25 characters"};
std::cout << std::endl << "3. Copy construct: ";
auto s3 = s2;
std::cout << std::endl << "4. Copy assign: ";
s1 = s3;
std::cout << std::endl << "5. Leaving scope: ";
}
std::cout << std::endl;
}
Результат:
1. Create first string: alloc 21 @ 0x56047f176280
2. Create longer string: alloc 26 @ 0x56047f1762a0
3. Copy construct: alloc 26 @ 0x56047f1762d0
4. Copy assign: alloc 41 @ 0x56047f176300 free 0x56047f176280
5. Leaving scope: free 0x56047f1762d0 free 0x56047f1762a0 free 0x56047f176300
Я бы ожидал, что строка 4 будет такой же, как строка 3.
Почему как libstdc (этот результат), так и libc (32/48 байт) выделяют больше памяти для назначения копирования, чем для построения копии?В обоих случаях новый размер известен. Я не вижу, как один из них, скорее всего, потребует дополнительной памяти в будущем.
Комментарии:
1. Виртуальная память дешевая (в большинстве случаев бесплатная). Выделение памяти вам ничего не стоит, пока кто-то не использует эту память. Например, Linux не поддерживает выделение физической памяти до того, как кто-то записывает. Итак, выделение 128 ГБ ничего не стоит, если вы записываете только в 1 МБ.
2. Исправьте свой вопрос.
3. @JesperJuhl: Конечно, но это другой масштаб. Если каждая строка будет стоить вам 41 вместо 26 байт, это пространство будет выделено, поскольку оно намного меньше страницы. В моем случае (база данных в памяти) это привело к потере 300 МБ на наборе данных в 1 ГБ.
4. @Yakk-AdamNevraumont — Не могли бы вы уточнить?
Ответ №1:
Я отследил это в реализации basic_string libstdc . operator=(const basic_stringamp;)
вызовы this->assign
, какие вызовы _M_assign
, какие вызовы _M_create
с новой (минимальной) и старой пропускной способностью. Здесь исходная емкость удваивается. Ссылка на политику экспоненциального роста включена в качестве комментария.
Во-первых, я не понял, как это связано с моим примером. В конце концов, я просто хочу заменить значение.
Политика роста для operator=(const basic_stringamp;)
имеет больше смысла, когда вместо отдельного назначения вы рассматриваете этот код:
std::string s;
for (auto i = 100; i < 110; i) s = s std::to_string(i);
Здесь установка емкости так, чтобы она была «просто подходящей», нарушила бы требование линейного роста.
Чтобы решить проблему потребления памяти, я теперь вызываю shrink_to_fit()
целевую строку.