вопросы по пулу памяти

Мне нужны некоторые пояснения по концепции и реализации пула памяти.

По пулу памяти в вики говорится, что

также называемое выделением блоков фиксированного размера, ... , поскольку эти реализации страдают от фрагментации из-за блоков переменного размера, их может быть невозможно использовать в системе реального времени из-за производительности.

Как происходит «переменный размер блока вызывает фрагментацию»? Как распределение фиксированного размера может решить эту проблему? Это вики-описание кажется мне немного неверным. Я думаю, что фрагментация не может быть предотвращена выделением фиксированного размера или вызвана переменным размером. В контексте пула памяти фрагментации избегают с помощью специально разработанных распределителей памяти для конкретного приложения или уменьшают за счет ограниченного использования предполагаемого блока памяти.

Также с помощью нескольких примеров реализации, например, Пример кода 1 и Пример кода 2, мне кажется, чтобы использовать пул памяти, разработчик должен знать тип данных очень хорошо, затем вырежьте, разделите или организуйте данные в связанные фрагменты памяти (если данные близки к связанному списку) или иерархически связанные фрагменты (если данные более иерархически организованы, например файлы). Кроме того, похоже, что разработчик должен заранее предсказать, сколько памяти ему нужно.

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

Есть ли хорошая библиотека пула памяти для C и C++?

Спасибо за любые комментарии!


person pepero    schedule 19.07.2011    source источник
comment
Я думаю, что основная идея заключается в том, что у вас есть один пул для каждого класса объектов, так что каждый объект требует одинакового пространства. Таким образом, вы можете легко выделять, освобождать и повторно использовать память, потому что вы можете точно повторно использовать освобожденные блоки.   -  person Kerrek SB    schedule 19.07.2011
comment
Привет, Керрек С.Б., спасибо за ваш комментарий. Если один пул для одного типа класса, то будет много-много пулов, или каждый раз, когда разрабатывается один класс, будет проектироваться один пул памяти. Если это правильно, то звучит немного громоздко.   -  person pepero    schedule 19.07.2011
comment
Если реализация пула памяти шаблонна по типу элемента, который ему разрешено выделять, то тот факт, что он громоздкий, скрывается компилятором, генерирующим большую часть необходимого шаблонного кода.   -  person Chad    schedule 19.07.2011
comment
Привет, Чад, тогда как размер пула прогнозируется для определенного типа T в реализации шаблона С++? будет ли это выглядеть так: template‹typename T› std::size_t my_sizeof() { return sizeof(T); }   -  person pepero    schedule 19.07.2011
comment
В Modern C++ Design есть хорошее обсуждение этой темы, и я бы порекомендовал книгу как интересную и за пределами этой темы.   -  person Tom Kerr    schedule 19.07.2011
comment
@pepero: да, хитрость в том, чтобы использовать шаблоны и sizeof, чтобы иметь только одну реализацию, поскольку код управления может быть одинаковым для всех.   -  person R. Martinho Fernandes    schedule 19.07.2011
comment
@ Том Керр, большое спасибо, что указали на это. Кажется, это глава 4 «Распределение малых объектов», и я нахожу ее очень интересной для чтения. Но мне очень любопытно, и Джеймс Джонстон почему также указывает, что это не используется для более крупной цели в его посте. Какая разница, маленький или большой? похоже, это очень эвристическое утверждение, которое я не совсем понимаю.   -  person pepero    schedule 19.07.2011
comment
C++ использует распределитель C по умолчанию, который смещен в сторону больших распределений. Насколько я понимаю, за выделение 4 байт вы заплатите примерно столько же, сколько 64 байта. Если ваше приложение очень интенсивно использует небольшие выделения памяти (например, умные указатели в книге), вы можете увидеть прирост производительности за счет амортизации затрат на производительность, связанных с использованием распределителя C.   -  person Tom Kerr    schedule 19.07.2011
comment
В любом случае, вы, вероятно, не выиграете (и, возможно, пострадаете) от оснащения своих классов распределителями пула. действительно важно, чтобы вы описали свой сценарий и сравнили его. Распределитель по умолчанию уже довольно умен и эффективен, а преимущества использования собственного очень ситуативны. Также попробуйте некоторые существующие распределители, такие как tcmalloc или nedmalloc.   -  person Kerrek SB    schedule 19.07.2011


Ответы (5)


В сценарии, где вы всегда выделяете блоки фиксированного размера, у вас либо достаточно места для еще одного блока, либо его нет. Если у вас есть, блок помещается в доступное пространство, потому что все свободные или используемые места имеют одинаковый размер. Фрагментация не проблема.

В сценарии с блоками переменного размера вы можете получить несколько отдельных свободных блоков разного размера. Запрос на блок размером меньше, чем общая свободная память, может оказаться невозможным для удовлетворения, потому что для него не существует одного непрерывного блока, достаточно большого. Например, представьте, что у вас есть два отдельных свободных блока по 2 КБ, и вам нужно удовлетворить запрос на 3 КБ. Ни одного из этих блоков не хватит для этого, хотя памяти достаточно.

person R. Martinho Fernandes    schedule 19.07.2011
comment
Спасибо, Мартиньо. Если какой размер данных требуется, какой пул создается, это правда, тогда я мог бы понять пул памяти. Конечно, предпочтительнее конкретный пул, а не общий пул для всего. Использование памяти находится под строгим контролем, независимо от того, сколько пулов может быть. - person pepero; 19.07.2011
comment
Да, пулы памяти блоков фиксированного размера обычно специфичны для определенных типов данных. Вы можете написать универсальную реализацию с шаблонами, но в каждом пуле есть объекты только одного типа, следовательно, фиксированный размер. - person R. Martinho Fernandes; 19.07.2011

Переменный размер блока действительно вызывает фрагментацию. Посмотрите на изображение, которое я прикрепляю: введите здесь описание изображения

На изображении (отсюда) показана ситуация, в которой A, B и C выделяет куски памяти, куски переменного размера.

В какой-то момент B освобождает все свои куски памяти, и внезапно возникает фрагментация. Например, если бы C нужно было выделить большой кусок памяти, который по-прежнему помещался бы в доступную память, это было бы невозможно, потому что доступная память разделена на два блока.

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

У пулов памяти, конечно, есть свои недостатки, как вы сами указываете. Так что не стоит думать, что пул памяти — это палочка-выручалочка. Это имеет свою стоимость, и ее имеет смысл платить при определенных обстоятельствах (например, встроенная система с ограниченным объемом памяти, ограничениями в реальном времени и т. д.).

Что касается того, какой пул памяти хорош в С++, я бы сказал, что это зависит. Я использовал один в VxWorks, который был предоставлен ОС; в некотором смысле хороший пул памяти эффективен, когда он тесно интегрирован с ОС. На самом деле, я думаю, каждая RTOS предлагает реализацию пулов памяти.

Если вам нужна общая реализация пула памяти, посмотрите это.

РЕДАКТИРОВАТЬ:

Из вашего последнего комментария мне кажется, что, возможно, вы думаете о пулах памяти как о «решении проблемы фрагментации». К сожалению, это не тот случай. Если хотите, фрагментация есть проявление энтропии на уровне памяти, т. е. она неизбежна. С другой стороны, пулы памяти — это способ управления памятью таким образом, чтобы эффективно уменьшить влияние фрагментации (как я уже сказал и как упоминалось в Википедии, в основном на конкретных системах, таких как системы реального времени). Это обходится дорого, поскольку пул памяти может быть менее эффективным, чем «обычный» метод распределения памяти, поскольку у вас есть минимальный размер блока. Другими словами, энтропия снова появляется под маской.

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

Управление памятью — действительно сложный вопрос, а пулы памяти — это всего лишь метод, который, как и любой другой, улучшает ситуацию по сравнению с другими методами и требует собственных затрат.

person sergio    schedule 19.07.2011
comment
спасибо sergio, если подумать о случае, когда каждый блок памяти был бы одинакового размера, то такой ситуации явно бы не возникло. Как это возможно для каждого фрагмента одинакового размера, если у вас нет только одного типа данных? - person pepero; 19.07.2011
comment
Привет, серджио, я прочитал ссылку на изображение. К сожалению, это типичное введение о внешней фрагментации, подобное тому, что объясняет Мартиньо Фернандесанд в своем посте. Я думаю, что это не проясняет мой вопрос. например, они также могут иметь одинаковый размер, но не быть смежными, и c по-прежнему не может запросить память. - person pepero; 19.07.2011

Пулы памяти как фиксированного, так и переменного размера будут иметь фрагментацию, т. е. между используемыми будут некоторые свободные участки памяти.

Для переменного размера это может вызвать проблемы, поскольку может не оказаться свободного фрагмента, достаточно большого для определенного запрошенного размера.

С другой стороны, для пулов фиксированного размера это не проблема, поскольку можно запрашивать только части предварительно определенного размера. Если есть свободное место, оно гарантированно будет достаточно большим для (кратного) одной порции.

person Ferdinand Beyer    schedule 19.07.2011

Если вы используете систему жесткого реального времени, вам может понадобиться заранее знать, что вы можете выделить память в течение максимально допустимого времени. Это можно «решить» с помощью пулов памяти фиксированного размера.

Однажды я работал над военной системой, где нам нужно было рассчитать максимально возможное количество блоков памяти каждого размера, которое система могла бы когда-либо использовать. Затем эти числа были добавлены к общей сумме, и система была настроена на этот объем памяти.

Безумно дорогой, но работал на оборону.


Когда у вас есть несколько пулов фиксированного размера, вы можете получить вторичную фрагментацию, когда в вашем пуле нет блоков, даже если в каком-то другом пуле достаточно места. Как вы это разделяете?

person Bo Persson    schedule 19.07.2011
comment
это также вопрос, который я хочу задать, после того, как я изучу один пул для одного типа, что произойдет? - person pepero; 19.07.2011

С пулом памяти операции могут работать следующим образом:

  1. Сохраните глобальную переменную, представляющую собой список доступных объектов (изначально пустых).
  2. Чтобы получить новый объект, попробуйте вернуть его из глобального списка доступных. Если его нет, вызовите оператор new, чтобы выделить новый объект в куче. Выделение происходит очень быстро, что важно для некоторых приложений, которые в настоящее время могут тратить много процессорного времени на выделение памяти.
  3. Чтобы освободить объект, просто добавьте его в глобальный список доступных объектов. Вы можете установить ограничение на количество элементов, разрешенных в глобальном списке; если предел достигнут, то объект будет освобожден, а не возвращен в список. Шапка предотвращает появление массовой утечки памяти.

Обратите внимание, что это всегда делается для одного типа данных одного и того же размера; это не работает для больших, и тогда вам, вероятно, нужно использовать кучу, как обычно.

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

person James Johnston    schedule 19.07.2011
comment
Привет, Джеймс, если пул памяти всегда делается для одного типа данных одного и того же размера, то какой тип данных мы должны выбрать для пула памяти, а какой нет? - person pepero; 19.07.2011
comment
Это зависит от типа данных, которые вы пытаетесь сохранить. В нашем примере мы обрабатывали изображения одинакового размера. Итак, это был двумерный массив/матрица, представляющая пиксели изображения. Глобальная переменная представляла собой массив указателей. - person James Johnston; 19.07.2011