Вычислить выделенную память std :: string (и использование строк в std :: vector)

Я хочу подсчитать, сколько памяти выделяется при создании и присвоении значений строке.

string s = "";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;
s = "1234567890qwertz";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;

Это вся память, которую занимает моя строка? Начальная / статическая часть, которую я получаю, просто вызывая sizeof (s) (это 40 байт на моей машине) плюс динамическая часть - размер одного символа, умноженный на выделенные заполнители, для обеспечения эффективного изменения размера строк ( на моей машине строка s сначала выделяла блок из 15 байтов до точки, где текст становится слишком длинным, поэтому после второго назначения динамическая часть составляет 31 байт). Кстати, а почему не 16 и 32 байта?

Правильный ли такой способ мышления (статический + динамический для каждой строки - это вся память, которую она занимает)?

Это означает, что если у меня есть std :: vector строк, и я хотел бы вычислить всю память для этого вектора, мне нужно было бы сделать то же самое: я добавляю начальный / статический размер моего вектора, чтобы получить плюс динамическая часть, что означает общую память, занимаемую одной строкой, как я это делаю выше для каждой строки внутри вектора?

vector<string> cache;
// fill cache with strings of dynamic length
int size = sizeof(cache);
for (int i = 0; i < cache.size(); i++)
{
    size += sizeof(cache[i]);
    size += sizeof(cache[i].at(0)) * cache[i].capacity();
}

Итак, чтобы подвести итог, это правильный объем памяти, занятый моим "кешем"?

Изменить: Или мне также нужно принять во внимание, что сам std :: vector также имеет .capacity ()> = .size (), что может означать, что мне действительно нужно сделать это:

для каждого cache.capacity() - мне нужно добавить sizeof(cache[i]) и дополнительно для каждого cache.size() - добавить sizeof(cache[i].at(0)) * cache[i].capacity() ??


person huzzm    schedule 17.01.2019    source источник
comment
Я хочу подсчитать, сколько памяти выделяется, когда я создаю и присваиваю значения строке - Хорошо, я спрошу. Зачем вам эта информация?   -  person PaulMcKenzie    schedule 17.01.2019
comment
sizeof(s) - это размер объекта s, а не самой строки. Если нет какой-либо оптимизации коротких строк (которая хранит содержимое внутри фактического строкового объекта), тогда std::string на самом деле не более чем пара размеров и указатель на фактические строковые данные.   -  person Some programmer dude    schedule 17.01.2019
comment
@Someprogrammerdude Суммируются ли эти 40 или эти пары размеров и указатель на фактические данные, когда я помещаю все больше и больше строковых объектов в std :: vector?   -  person huzzm    schedule 17.01.2019
comment
На ваш побочный вопрос: stackoverflow.com/a/11752722/3552770   -  person LogicStuff    schedule 17.01.2019
comment
Конечно, каждому std::string объекту потребуется место.   -  person Some programmer dude    schedule 17.01.2019
comment
@PaulMcKenzie Я создаю программный кеш данных (просто вектор строк) и хочу знать, как растет моя память по мере того, как я помещаю в него больше элементов (строк). Так что я могу сказать - когда он станет больше 40 КБ, я хочу очистить кеш и так далее ...   -  person huzzm    schedule 17.01.2019
comment
@Someprogrammerdude Значит, правильно делать что-то вроде для каждой строки в векторе: sizeof (s) (который на моей машине равен 40) + [динамически выделяемый размер этой строки]?   -  person huzzm    schedule 17.01.2019
comment
@LogicStuff Ах, да, это имеет смысл. Спасибо. Я забыл об этом.   -  person huzzm    schedule 17.01.2019
comment
Если вас интересует 40 КБ, в этом случае можно не использовать std::vector<std::string>>.   -  person Slava    schedule 17.01.2019
comment
Получите свободную память до и после создания экземпляра строки.   -  person Michael Chourdakis    schedule 17.01.2019
comment
@huzzm да, хотя некоторые строки могут вообще не выделять память.   -  person Bartek Banachewicz    schedule 17.01.2019
comment
@Slava Это может быть правдой, но меня все еще волнует ответ на мой вопрос.   -  person huzzm    schedule 17.01.2019
comment
@BartekBanachewicz О, правда, как же так?   -  person huzzm    schedule 17.01.2019
comment
@huzzm глобальные / статические строки с небольшой оптимизацией строк.   -  person Goswin von Brederlow    schedule 17.01.2019
comment
У класса std::string есть метод size(), который даст вам его размер в байтах.   -  person Nadir    schedule 17.01.2019
comment
@Nadir, точнее, size() возвращает количество допустимых char в строке. Неважно, хранятся ли символы в динамической памяти или во внутреннем буфере SSO. capacity() возвращает фактическое количество выделенных символов, но также не делает различий между динамической памятью и памятью SSO. Вы не можете получить истинный размер байта std::string, не зная внутренних деталей его реализации.   -  person Remy Lebeau    schedule 17.01.2019


Ответы (3)


На этот вопрос будет сложно ответить. Наивно можно было бы подумать, что общий объем потребляемой памяти будет

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector

Но это скорее верхний предел, а не то, что можно фактически потреблять. Например, оптимизация коротких строк позволяет std::string хранить строковые данные в хранилище, которое потребляет сам строковый объект (то, что вы называете статическим размером). Если это так, то фактически потребляемое пространство будет

vector_capacity * sizeof(std::string)

а емкость каждой строки в векторе будет просто тем, сколько места вы занимает, не выделяя лишнего места. Вам нужно будет проверить свою реализацию, чтобы увидеть, использует ли она SSO и длинную строку, которую она будет хранить в строковом объекте, чтобы узнать, использует ли значение емкости внутреннее пространство строк или фактически потребляет дополнительную память. Это приводит к тому, что фактическое занимаемое пространство

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector_where_
                                        the_capcity_is_more_than_the_sso_amount

В тебе расчет sizeof(cache[i].at(0)) не нужен. std::string используйте char и sizeof(char) гарантированно будет 1

person NathanOliver    schedule 17.01.2019
comment
Большое тебе спасибо. Это был тот ответ, который я искал. - person huzzm; 17.01.2019
comment
Подробности реализации сами по себе не нужны; вам просто нужно знать, когда строка выделяет новую память. Это можно сделать, например, с использованием настраиваемого распределителя. - person Bartek Banachewicz; 17.01.2019
comment
Я снова немного отредактировал вопрос, потому что вектор тоже имеет емкость ›= size. Это что-то меняет или емкость 15, в то время как внутри вектора всего 3 строки, означает, что на самом деле это 15 * sizeof (string) + 3 * динамической памяти для каждой отдельной строки (емкость * размер char) < / я> - person huzzm; 17.01.2019
comment
@BartekBanachewicz Хорошо. Спасибо! - person huzzm; 17.01.2019
comment
@huzzm Это так. Я обновил ответ, чтобы использовать vector_capacity * sizeof(std::string) - person NathanOliver; 17.01.2019

Существует простая причина, по которой емкость струны на единицу меньше, чем вы ожидаете, а именно:

s.c_str()

Строка C ++ хранится в блоке памяти, емкость которого указывает общий размер и размер используемого пространства. Но строка C завершается 0. Строка C ++ резервирует один дополнительный байт в конце блока памяти для хранения 0. Таким образом, s.c_str() всегда завершается 0.

Таким образом, память, используемая динамической частью строки, равна емкости + 1.

Что касается общей памяти, потребляемой строкой или вектором строк, Натан Оливер ответил, что я думаю. Но остерегайтесь векторов, содержащих одну и ту же строку несколько раз.

person Goswin von Brederlow    schedule 17.01.2019
comment
Ах да, очень верно. Я забыл про нулевое завершение. Спасибо за дополнительный ответ! - person huzzm; 17.01.2019

Если вы хотите узнать, сколько места занимает ваш std::vector<std::string>, рассчитайте его:

auto netto_memory_use(std::vector<std::string> const& x) noexcept {
    return std::accumulate(
        begin(x),
        end(x),
        sizeof x + sizeof x[0] * x.capacity(),
        [](auto n, auto&& s) {
            if (std::less<void*>()(data(s), &s)
            || std::greater_eq<void*>()(data(s) + s.capacity(), &s + 1))
                return n + s.capacity() + 1;
            return n;
        });
    }

Я использовал std::less<void*> / std::greater_eq<void*>, чтобы воспользоваться преимуществом их определения полного порядка, в отличие от простого использования операторов сравнения.

Аккумулятор проверяет примененную оптимизацию малой строки (SSO) перед добавлением емкости строки. Конечно, все строки с нулевой емкостью могут иметь один и тот же статически назначенный терминатор. Или емкость и / или длина могут быть выделены вместе с символьными данными.
Тем не менее, это должно быть хорошим приближением для используемой памяти, за исключением накладных расходов системы управления памятью.

person Deduplicator    schedule 17.01.2019
comment
Что такое data()? - person Bouncner; 27.11.2019
comment
@Bouncner, std::data() найдено по ADL. - person Deduplicator; 27.11.2019