2015-09-16 6 views
9

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

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

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

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

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

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

+0

Вы выделяете всю память сразу в начале? Если да: это необходимо? Если нет: вы попробовали ringbuffers? – Gombat

+1

Да, круговой буфер - это то, что вам кажется нужным. –

+0

@ Gombat: Я должен был поместить эту деталь в OP, но более ранний шаг в моей цепочке обработки требует, чтобы весь буфер был в памяти (как непрерывный блок, так как он передается C API) сразу. Иначе да, круговой буфер определенно будет правильным выбором. Алгоритм, на который я ссылаюсь в вопросе, - это заключение к конвейеру нескольких этапов обработки; как только это будет сделано, мне больше не нужны данные. –

ответ

4

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

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

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

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

Вопросы портативность являются:

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

Update

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

5

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

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

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

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

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

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

Другими словами, если вы выделили 100 ГБ, залейте его, оставьте его сидящим без касания, оно в конечном итоге ВСЕ будет на жестком диске, а не в ОЗУ.

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

Как и все, что касается «настройки» и «улучшения производительности», вам необходимо измерить и сравнить контрольный показатель и посмотреть, насколько он помогает.

2

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

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

+0

Нет необходимости в '' 'MAP_FIXED'' 'здесь, поскольку вы можете создать целую область, используя единственный' '' mmap() '' 'call, а затем' '' unmap() '' 'его части. '' 'MAP_FIXED''' указан в POSIX, но нет портативного способа определить для него правильный адрес. Таким образом, '' 'MAP_FIXED''' добавляет проблемы с переносимостью и не решает существующие проблемы (вам все равно нужны несанкционированные анонимные сопоставления). – gavv

+0

@ g-v Да, я думаю, что я обратился к этому в разделе «Если вы выделите всю память вверх, ...». – 5gon12eder

+0

Да, я вижу. Просто уточнение, связанное с переносимостью. – gavv

Смежные вопросы