Как избежать нехватки памяти в приложении с высоким потреблением памяти? C / C ++ - PullRequest
13 голосов
/ 12 апреля 2009

Я написал конвертер, который принимает XML-файлы openstreetmap и преобразует их в двоичный формат рендеринга, который обычно составляет около 10% от исходного размера. Размер входного файла обычно составляет 3 ГБ и больше. Входные файлы не загружаются в память все сразу, но передаются в виде точек и полисов, затем запускается bsp и файл выводится. В последнее время на больших файлах ему не хватает памяти и он умирает (у рассматриваемого есть 14 миллионов точек и 1 миллион полигонов). Обычно моя программа использует от 1 до 1,2 ГБ оперативной памяти, когда это происходит. Я пытался увеличить виртуальную память с 2 до 8 ГБ (на XP), но это изменение не произвело никакого эффекта. Кроме того, поскольку этот код является открытым исходным кодом, я бы хотел, чтобы он работал независимо от доступного RAM (хотя и медленнее), он работает на Windows, Linux и Mac.

Какие методы я могу использовать, чтобы избежать переполнения памяти? Обрабатывать данные в меньших подмножествах и затем объединять окончательные результаты? Используете мой тип обработчика виртуальной памяти? Есть другие идеи?

Ответы [ 15 ]

16 голосов
/ 12 апреля 2009

Во-первых, в 32-битной системе вы всегда будете ограничены 4 ГБ памяти независимо от настроек файла подкачки. (Из них только 2 ГБ будут доступны вашему процессу в Windows. В Linux у вас обычно будет доступно около 3 ГБ) *

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

Во-вторых, может помочь выделение меньшего объема памяти за раз. Зачастую легче найти 4 блока по 256 МБ свободной памяти, чем один блок по 1 ГБ.

В-третьих, разделите проблему. Не обрабатывайте весь набор данных одновременно, но попробуйте одновременно загружать и обрабатывать только небольшой раздел.

4 голосов
/ 12 апреля 2009

Вы проверили, чтобы убедиться, что у вас нет утечки памяти?

Поскольку ваша программа переносима на Linux, я советую запустить ее под Valgrind.

4 голосов
/ 12 апреля 2009

Звучит так, как будто вы уже используете SAX подход к обработке XML (загрузка XML по мере необходимости, а не сразу).

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

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

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

В худшем случае вы можете попытаться использовать что-то вроде VirtualAlloc , если вы работаете в системе Windows. Если вы работаете в 32-битной системе, вы можете попробовать использовать что-то вроде Расширение физического адреса (PAE) .

Вы также можете рассмотреть вопрос о введении ограничений на ввод для вашей программы, а также установить другое ограничение для 32-разрядных и 64-разрядных систем.

3 голосов
/ 12 апреля 2009

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

2 голосов
/ 12 апреля 2009

Предполагая, что вы используете Windows XP, если у вас чуть больше предела памяти, и вы не хотите или у вас нет времени на переработку кода, как предложено выше, вы можете добавить ключ / 3GB в свой boot.ini. файла, а затем нужно просто установить переключатель компоновщика, чтобы получить дополнительный 1 ГБ памяти.

1 голос
/ 12 апреля 2009

Вам необходимо передавать как вывод, так и ввод. Если ваш выходной формат не ориентирован на поток, подумайте о втором проходе. Например, если выходной файл начинается с проверки суммы / размера данных, оставьте пробел при первом проходе, а затем выполните поиск / запись в этот пробел.

1 голос
/ 12 апреля 2009

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

Если вы не хотите тратить время на оптимизацию использования памяти, почему бы не попробовать Conservative Garbage Collector ? Это замена плагина для malloc () / new и free (). На самом деле, free () не работает, поэтому вы можете просто удалить эти вызовы из вашей программы. Если вместо этого вы вручную оптимизируете свою программу и управляете пулом памяти, как предлагалось ранее, то в конечном итоге вы сделаете большую часть работы, которую CGC уже выполняет для вас.

1 голос
/ 12 апреля 2009

Как вы распределяете память на очки? Вы распределяете баллы по одному за раз (например, pt = новая точка). Затем, в зависимости от размера точки, некоторая память может быть потрачена впустую. Например, в Windows память выделяется в количестве, кратном 16 байтам, поэтому даже если вы попросите попытаться выделить 1 байт, ОС фактически выделит 16 байт.

В этом случае может помочь использование распределителя памяти. Вы можете сделать быструю проверку, используя STL allocator. (перегрузите новый оператор для класса Point и используйте распределитель STL для выделения памяти вместо «malloc» или оператора по умолчанию new).

1 голос
/ 12 апреля 2009

В 32-битной XP максимальное адресное пространство программы составляет 2 ГБ. Затем происходит фрагментация из-за загрузки DLL и драйверов в ваше адресное пространство. Наконец, у вас есть проблема фрагментации вашей кучи.

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

Наконец, вы можете разделить ваш BSP по процессам. Сложно и мучительно, и, честно говоря, просто поместить его на диск было бы проще, но теоретически вы могли бы получить более высокую производительность, если бы группа процессов обменивалась информацией, если бы вы могли сохранять все резидентным (и при условии, что вы можете быть умнее, чем память, чем ОС может обрабатывать буферизацию файлов ... что является большим, если). Каждому процессу потребовалось бы гораздо меньше памяти и, следовательно, он не должен работать до предела адресного пространства 2 ГБ. Конечно, вы будете прожигать RAM / swap намного быстрее.

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

Мальчик, разве 64-битные вычисления не звучат намного лучше, чем другие варианты?

1 голос
/ 12 апреля 2009

Вы должны понимать, что виртуальная память отличается от «ОЗУ» тем, что объем используемой виртуальной памяти - это общий объем, который вы зарезервировали, в то время как реальная память (в Windows она называется «Рабочий набор») - это память, которая вы на самом деле изменили или заблокированы.

Как кто-то еще указал, на 32-битных платформах Windows ограничение виртуальной памяти составляет 2 гигабайта, если вы не установите специальный флаг для 3 гигабайт и можете гарантировать, что все указатели как в вашем коде, так и в любых используемых вами библиотеках используются только указатели без знака.

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

Я врезался в 32-битную стену в Windows, но у меня нет опыта работы с этими ограничениями в Linux, поэтому я только говорил о стороне Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...