В С ++ 11 shrink_to_fit
был введен для дополнения некоторых контейнеров STL (например, std::vector
, std::deque
, std::string
).
Синопсис, его основной функцией является запрос контейнера, который связан с уменьшить его способность соответствовать его размеру. Тем не менее, это запрос не является обязательным, и реализация контейнера может свободно оптимизировать иначе и оставить вектор с емкостью, превышающей его размер.
Кроме того, в предыдущем вопросе SO OP не рекомендовалось использовать shrink_to_fit
чтобы уменьшить возможности его std::vector
к его размеру. Причины не делать этого приведены ниже:
shrink_to_fit
ничего не делает или это дает вам проблемы локальности кэша, и это O (n)
выполнить (так как вы должны скопировать каждый элемент в их новый, меньший дом).
Обычно дешевле оставить слабину в памяти. @Massa
Может ли кто-нибудь так любезно ответить на следующие вопросы:
std::vector
).shrink_to_fit
в конце концов? Имеют ли место аргументы в цитате?
Мера, и вы будете знать. Вы ограничены в памяти? Можете ли вы определить правильный размер заранее? Будет эффективнее reserve
чем это сокращаться после факта. В целом, я склонен согласиться с тем, что большинство применений, вероятно, хорошо подходят для слабины.
Если да, то как правильно уменьшить емкость контейнера STL до его размера (по крайней мере, для std :: vector).
Комментарий относится не только к shrink_to_fit
, но к любому другому способу сокращения. Учитывая, что вы не можете realloc
на месте, это включает в себя получение другого куска памяти и копирование туда независимо от того, какой механизм вы используете для сжатия.
И если есть лучший способ уменьшить контейнер, в чем причина существования shrink_to_fit?
Запрос не является обязательным, но альтернативы не имеют лучших гарантий. Вопрос в том, сокращение имеет смысл, если это так, то имеет смысл предоставить shring_to_fit
операция, которая может использовать тот факт, что объекты переехал на новое место. То есть если тип T
имеет noexcept(true)
Переместите конструктор, он выделит новую память и переместит элементы.
Хотя вы можете достичь того же самого внешне, этот интерфейс упрощает работу. Эквивалентно shrink_to_fit
в C ++ 03 было бы:
std::vector<T>(current).swap(current);
Но проблема с этим подходом состоит в том, что когда копия делается во временную, она не знает, что current
собирается быть заменен, нет ничего, что говорит библиотеке, что это Можно переместить удерживаемые объекты. Обратите внимание, что с помощью std::move(current)
не достигнет желаемого эффекта, как это было бы переехать весь буфер, поддерживая тот же capacity()
,
Реализация этого извне была бы немного более громоздкой:
{
std::vector<T> copy;
if (noexcept(T(std::move(declval<T>())))) {
copy.assign(std::make_move_iterator(current.begin()),
std::make_move_iterator(current.end()));
} else {
copy.assign(current.begin(), current.end());
}
copy.swap(current);
}
Предполагая, что я правильно выполнил условие if … это, вероятно, не то, что вы хотите писать каждый раз, когда хотите эту операцию.
- Будут ли аргументы верны?
Поскольку аргументы изначально мои, не против, если я буду защищать их один за другим:
Или shrink_to_fit
ничего не делает (…)
Как уже упоминалось, стандарт гласит (много раз, но в случае vector
это раздел 23.3.7.3 …), который запрос не является обязательным, чтобы обеспечить широту реализации для оптимизации. Это означает, что реализация Можно определять shrink_to_fit
как неоперативный.
(…) или это дает вам проблемы с локальностью кэша
В том случае, если shrink_to_fit
является не реализован как неактивный, вы должны выделить новый базовый контейнер с емкостью size()
скопировать (или, в лучшем случае, переместить) построить все ваши N = size()
новые элементы из старых, уничтожить все старые (в случае перемещения это должно быть оптимизировано, но возможно, что это снова включает цикл над старым контейнером), а затем уничтожить старый контейнер как таковой. Это сделано, в libstdc++-4.9
Точно так же, как описал Дэвид Родригес,
_Tp(__make_move_if_noexcept_iterator(__c.begin()),
__make_move_if_noexcept_iterator(__c.end()),
__c.get_allocator()).swap(__c);
И в libc++-3.5
по функции в __alloc_traits
это делает примерно то же самое.
Ох, и реализация абсолютно не может полагаться на realloc
(даже если он использует malloc
внутри ::operator new
для его выделения памяти), потому что realloc
, если он не может сжаться на месте, либо оставит память в одиночку (безоперационный случай), либо сделает побитовое копирование (и упустит возможность перенастройки указателей и т. д., которые дали бы надлежащие конструкторы копирования / перемещения C ++).
Конечно, можно написать сокращающийся распределитель памяти и использовать его в конструкторе его векторов.
В простом случае, когда векторы больше, чем строки кэша, все это движение оказывает давление на кэш.
и это O (N)
Если n = size()
Я думаю, это было установлено выше, что, по крайней мере, вы должны сделать один n
размерное распределение, n
копировать или перемещать конструкции, n
разрушения, и один old_capacity
размер освобождения.
обычно дешевле просто оставить слабину в памяти
Очевидно, если вы действительно не нуждаетесь в свободной памяти (в этом случае может быть разумнее сохранить ваши данные на диск и перезагрузить их позже по требованию …)
- Если да, то как правильно уменьшить емкость контейнера STL до его размера (по крайней мере, для std :: vector).
Правильный путь еще shrink_to_fit
… вам просто не нужно полагаться на это или очень хорошо знать свою реализацию!
- И если есть лучший способ уменьшить контейнер, в чем причина существования shrink_to_fit?
Нет лучшего способа, но причина существования shrink_to_fit
УТВЕРЖДАЕТ, что иногда ваша программа может чувствовать нехватку памяти, и это один из способов лечения. Не очень хороший способ, но все же.
НТН!
- Если да, то как правильно уменьшить емкость контейнера STL до его размера (по крайней мере, для std :: vector).
«Трюк обмена» обрезает вектор до требуемого размера (из более эффективного SQL):
vector<Person>(persons).swap(persons);
Особенно полезно, когда вектор пуст, чтобы освободить всю память:
vector<Person>().swap(persons);
Векторы постоянно отключали код обнаружения утечки памяти моего тестировщика из-за сохраняющегося распределения неиспользуемого пространства, и это прекрасно их разобрало.
Это тот пример, когда меня действительно не волнует эффективность времени выполнения (размер или скорость), но меня интересует точное использование памяти.
- И если есть лучший способ уменьшить контейнер, в чем причина существования shrink_to_fit?
Я действительно не знаю, в чем смысл предоставления функции, которая на законных основаниях не может делать абсолютно ничего.
Я обрадовался, когда увидел, что это было введено, и отчаялся, когда обнаружил, что на него нельзя положиться.
Возможно, мы увидим Maybe_sort () в следующей версии.