Фон
мой предыдущий вопрос около boost.pool
побудил меня детально изучить boost.pool, и теперь у меня есть дополнительный вопрос, чтобы завершить мое понимание.
прелюдия
Эта ссылка утверждает следующее о шаблоне пула объектов:
Шаблон пула объектов — это шаблон программного креативного проектирования, который
использует набор инициализированных объектов, готовых к использованию, а не
выделять и уничтожать их по требованию.
Из того, что я могу сказать, boost.pool
(упрощенно) реализует шаблон пула объектов с помощью выделения памяти и управления, в основном на основе размера element_type
и возвращает простой указатель на выделенный объект:
element_type * malloc();
void free(element_type * p);
Простой пример повышения также показывает, что нет необходимости явно free
приобретенный элемент:
X * const t = p.malloc();
... // Do something with t; don't take the time to free() it.
Вопрос
Я понимаю, что выделенная память будет безопасно освобождена при уничтожении объекта пула, но как пул узнает, когда блок памяти, полученный клиентом, был освобожден обратно в пул и может использоваться повторно, если его интерфейс возвращает прямой указатель в element_type
еще звонок free()
все еще не требуется? Т.е. как пул повышения может повторно использовать эту память, если нет уверенности в том, что она еще не используется? И если он не использует эту память повторно, считается ли это тем же шаблоном, который описан в вики-справке?
Как пул буста может повторно использовать эту память, если не уверен, что
память еще не используется?
Не может На самом деле это не будет повторно использовать эту память. Это только гарантия того, что у вас не будет утечек когда бассейн разрушен.
И если он не использует эту память повторно, считается ли это тем же шаблоном, который описан в вики-справке?
статья Вы связаны говорит: Объединение объектов может значительно повысить производительность в ситуациях, когда высока стоимость инициализации экземпляра класса.
Хотя из Boost pool вступление: Пулы обычно используются, когда есть много размещения и освобождения небольших объектов.
Так что нет, они не по одной схеме. Один предназначен для повторного использования объектов, которые дорого конструировать (потоки, ресурсы opengl и т. д.). Другой предназначен для управления множеством мелких объектов, что дает вам больше контроля, чем дает стандартный распределитель.
Как вы указали, есть два способа использования пулов:
Пример для второго случая: представьте, что у вас есть класс графа, где каждый узел хранит своих соседей, используя указатели. Теперь вы должны сделать глубокую копию вашего графика. Вы выделите кучу новых узлов и скопируете данные из старых в новые, но теперь вам нужно инициализировать указатели соседей, поэтому вам нужна карта от старых указателей до новых указателей:
std::map<node*,node*> old_ptr_to_new_ptr;
Это хороший пример использования распределителей пула (я не буду вдаваться в подробности о том, как использовать распределители пула с контейнерами std): множество небольших объектов (узлов карты), которые будут удалены все вместе.
Библиотека Boost Pool обеспечивает STL распределители которые более эффективны при распределении объектов одного типа (в отличие от std::allocator
который просто использует new
а также delete
). Это то, что Страуструп или Александреску назвали бы распределитель мелких объектов.
Как и любой пользовательский класс-распределитель, он работает с четырьмя отдельными функциями: выделять, освобождать, конструировать и уничтожать. Я думаю, что их имена говорят сами за себя (если вы не путаетесь в распределении или строительстве). Чтобы получить новый объект из пула, вы сначала вызываете allocate(1)
чтобы получить указатель, а затем вы звоните construct( ptr, value )
по этому указателю ptr
чтобы получить его как копию value
(или переехал). И вы делаете наоборот, когда вы хотите удалить этот объект. Это механизм, который все контейнеры STL используют скрытно для выделения-конструирования-уничтожения-освобождения своих объектов.
Вы не должны доверять статье в Википедии, на которую вы ссылались (и не в целом), она очень плохо сформулирована, использует очень расплывчатый и неточный язык и принимает несколько узкий взгляд на шаблон пула объектов. И, кстати, цитирование Википедии бесполезно, вы не знаете, кто это написал, нет причин доверять этому, всегда идите к источнику.
Шаблон, описанный в вики (и особенно в исходной статье), имеет цель, совершенно отличную от той, которую пытаются выполнить распределители пула поддержки. Как описано в вики, акцент делается на повторное использование объектов без их реального уничтожения (например, токарно-бассейн хороший пример, потому что было бы дорого создавать и уничтожать потоки часто, а исходная статья заинтересована в объединении поставщиков услуг базы данных по аналогичным причинам). В распределителях повышающего пула акцент делается на том, чтобы избежать вызова кучи (freestore) для выделения множества небольших объектов, задача, которую куча не может выполнить очень эффективно, и она станет фрагментированной. Возможно, вместо этого его следует называть «распределителями небольших объектов», чтобы избежать путаницы.
как пул узнает, когда блок памяти, полученный клиентом, был освобожден обратно в пул и может использоваться повторно, если его интерфейс возвращает прямой указатель на element_type, но при этом вызов free () по-прежнему не требуется?
Я не думаю, что это может. Я считаю, что история идет так. Вы можете просто выделить кучу объектов из пула, никогда не освобождая их, и это все еще безопасно в том смысле, что когда вы уничтожаете пул-распределитель, вся его память сбрасывается вместе с ним, включая все объекты, которые вы оставшись в бассейне. Это то, что они подразумевают под словом «не требуется освобождать объекты», просто, что ваше приложение не утечет память после истечения срока службы объекта пула-распределителя, если вы забудете освободить все объекты из пула.
Но если вы не скажете пулу-распределителю освободить объекты, которые вам больше не нужны (и, следовательно, могут быть повторно использованы), он не сможет повторно использовать эти слоты памяти, это просто невозможно (учитывая, что распределители не • доставить любой специальный смарт-указатель, который сможет отслеживать выделенные объекты).
Как пул форсирования может повторно использовать эту память, если не может быть уверенности, что она еще не используется?
Если нельзя быть уверенным, что память все еще не используется, то он не сможет повторно использовать память. Любой фрагмент кода, который может привести к такой безрассудной вещи, как «предположить, что объект больше не нужен, не будучи уверенным», будет бесполезным фрагментом кода, поскольку он, очевидно, будет иметь неопределенное поведение, и ни один программист не сможет его использовать когда-либо.
И если он не использует эту память повторно, считается ли это тем же шаблоном, который описан в вики-справке?
Нет, он не реализует то, что объясняется в вики. Вам придется привыкнуть к тому факту, что терминология иногда сталкивается неудачно. Чаще всего ссылаются на то, что реализует бустер-пул как «пул памяти» или как «распределитель небольших объектов». Эта структура оптимизирована для небольших объектов, которые довольно дешево создавать и копировать. Поскольку куча (freestore) предназначена для больших блоков памяти и имеет тенденцию плохо справляться с попытками найти место для небольших объектов, использование ее для этой цели не является хорошей идеей и может привести к фрагментации кучи. Таким образом, пул-распределители по существу заменяют кучу чем-то, что более эффективно при распределении множества небольших объектов одного типа. Он не использует объекты повторно, но может повторно использовать освобожденную память, как это делает куча. И он обычно выделяет свою память (которую он выделяет) из кучи как большой непрерывный блок (например, с std::vector
). Есть много других преимуществ производительности при использовании распределителя небольших объектов, когда это уместно. Но то, что реализует Boost Pool, на самом деле сильно отличается от того, что описано в вики. Вот описание того, для чего хороши пул-распределители:
Хорошее место для использования пула — в ситуациях, когда множество (несмежных) небольших объектов может быть выделено в куче, или если распределение и освобождение объектов одного размера происходит многократно.