Возможно ли частично освободить динамически выделяемую память в системе POSIX?

у меня есть приложение C++, где мне иногда требуется большой буфер типов POD (например, массив из 25 bИллион floats), который нужно держать в памяти сразу в смежном блоке. Эта конкретная организация памяти обусловлена тем, что приложение использует некоторые API C, которые работают с данными. Поэтому другое расположение (например, список меньших кусков памяти, таких как std::deque uses) не представляется возможным.

приложение имеет алгоритм, который запускается на массиве потоковым способом; подумайте что-то вроде этого:

std::vector<float> buf(<very_large_size>);
for (size_t i = 0; i < buf.size(); ++i) do_algorithm(buf[i]);

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

теоретически, поэтому я мог бы освободить эту память, чтобы уменьшить объем памяти моего приложения, когда он пережевывает данные. Однако, делать что-то вроде realloc() (или std::vector<T>::shrink_to_fit()) было бы неэффективно, потому что мое приложение должно было бы потратить свое время на копирование неиспользованных данных в новое место во время перераспределения.

мое приложение работает на POSIX-совместимых операционных системах (например, Linux, OS X). Есть ли интерфейс, с помощью которого я мог бы попросить операционную систему бесплатно только указанную область от передней части блока памяти? Это, казалось бы, самый эффективный подход, так как я мог бы просто уведомить менеджер памяти, который, например, первые 2 ГБ блока памяти могут быть восстановлены, как только я закончу с ним.

3 ответов


возможно ли частично освободить динамически выделяемую память в системе POSIX?

вы не можете сделать это с помощью malloc()/realloc()/free().

однако, вы можете сделать это в полу-портативный способ с помощью mmap() и munmap(). Ключевым моментом является то, что если вы munmap() некоторые страницы malloc() затем можно использовать эту страницу:

  • создать анонимное отображение с помощью mmap();
  • в дальнейшем munmap() для регионов, которые тебе больше не нужно.

проблемы с переносимостью:

  • POSIX не указывает анонимные сопоставления. Некоторые системы обеспечивают MAP_ANONYMOUS или MAP_ANON флаг. Другие системы предоставляют специальный файл устройства, который может быть сопоставлен для этой цели. Linux предоставляет оба.
  • я не думаю, что POSIX гарантирует это, когда вы munmap() страницы malloc() сможет использовать его. Но я думаю, что это будет работать все системы, которые имеют mmap()/unmap().

обновление

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


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

основной момент на этом посту в основном не сказать вам, чтобы сделать то, что вы хотите сделать, потому что ОС не будет излишне держать память вашего приложения в ОЗУ, если это на самом деле не нужно. Это разница между "использование резидентных" и "виртуальной памяти". "Резидент" - это то, что в настоящее время используется и в ОЗУ, "виртуальный" - это всего использование памяти приложения. И пока ваш раздел подкачки достаточно велик," виртуальная " память в значительной степени не является проблемой. [Я предполагаю, что в вашей системе не закончится виртуальное пространство памяти, что верно для 64-битного приложения, пока вы не используете сотни терабайт виртуального пространства!]

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

 for (size_t i = 0; i < buf.size(); ++i) {
    do_algorithm(buf[i]);
    buf.done(i);
 }

на done метод просто проверит, если значение if i (один элемент) находится за концом текущего буфера и освобождает его. [Это должно быть встроено красиво и производить очень мало накладных расходов на среднем цикле-предполагая, что элементы фактически используются в линейном порядке, конечно].

Я был бы очень удивлен, если это принесет вам что-нибудь, если do_algorithm(buf[i]) принимает довольно долгое время (конечно, много секунд, возможно, много минут или даже часов). И, конечно, это поможет, только если у вас есть что-то еще полезное для этой памяти. И даже тогда ОС будет восстанавливать память, которая не используется активно, заменяя ее на диск, Если системе не хватает памяти.

другими словами, если вы выделите 100 ГБ, заполните его, оставьте его сидеть, не касаясь, в конечном итоге все будет на жестком диске, а не в БАРАН.

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

как и все, что касается "настройки" и " производительности улучшения", вам нужно измерить и сравнить эталон, и посмотреть, насколько это помогает.


если вы можете обойтись без услуг std::vector (что в любом случае не даст вам много в этом случае, потому что вы никогда не захотите копировать / return / переместить этого зверя в любом случае), вы можете сделать свою собственную обработку памяти. Задать операционную систему для целых страниц памяти (через mmap) и возвращать их по мере необходимости (используя munmap). Вы можете сказать mmap через свой аргумент кулака и необязательный MAP_FIXED флаг для отображения страницы по определенному адресу (который вы должны обеспечить, чтобы быть не занят, конечно), поэтому вы можете создать область смежной памяти. Если вы выделяете всю память заранее, то это не проблема, и вы можете сделать это с помощью одного mmap и пусть операционная система, выбрать удобное место на карте. В конце концов, это malloc Не внутренне. Для платформ, которые не имеют sys/mman.h, это не трудно вернуться к использованию malloc если вы можете жить с тем, что на этих платформах, вы не вернется память ранний.

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