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

StackOverflow https://stackoverflow.com/questions/741834

  •  09-09-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

Это звучит так, как будто вы уже делаете САКСОФОН основанный подход к обработке XML (загрузка XML по ходу работы, а не всего сразу).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Боже, разве 64-разрядные вычисления не звучат намного приятнее, чем другие варианты?

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

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

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

Если вы не хотите тратить время на оптимизацию использования памяти, почему бы не попробовать Консервативный Сборщик мусора?Это подключаемый модуль, заменяющий malloc() /новый и бесплатный().Фактически, free() - это неоперабельная функция, поэтому вы можете просто удалить эти вызовы из своей программы.Если вместо этого вы вручную оптимизируете свою программу и управляете пулом памяти, как предлагалось ранее, вы в конечном итоге будете выполнять большую часть работы, которую CGC уже выполняет за вас.

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

Звучит так, как будто вы переводите текст в двоичный формат, так зачем вам нужно хранить все данные в памяти?.
Разве вы не можете просто прочитать примитив из txt (xml), а затем сохранить в binarystream?

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

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

Теперь это звучит просто, не так ли?(Рад, что мне не нужно этого делать :) )

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

Вот несколько вещей, которые вы можете сделать, чтобы помочь в этой ситуации:

  • Если вы работаете в Windows, используйте Карты файлов (пример кода).Это предоставит доступ к файлу через один указатель буфера, как если бы вы читали весь файл в памяти, только на самом деле этого не делая.Последние версии ядра Linux имеют аналогичный механизм.
  • Если вы можете, а похоже, что вы могли бы, сканируйте файл последовательно и избегайте создания DOM в памяти.Это значительно сократит время загрузки, а также требования к памяти.
  • Используйте Объединенную память!Вероятно, у вас будет много крошечных объектов, таких как узлы, точки и еще много чего.Используйте объединенную память, чтобы помочь (я предполагаю, что вы используете неуправляемый язык.Поиск объединенного выделения и пулов памяти).
  • Если вы используете управляемый язык, по крайней мере, переведите эту конкретную часть на неуправляемый язык и возьмите под контроль память и чтение файлов.Управляемые языки имеют нетривиальные накладные расходы как с точки зрения объема памяти, так и с точки зрения производительности.(Да, я знаю, что это помечено как "C ++" ...)
  • Попытайтесь разработать алгоритм на месте, при котором вы одновременно считываете и обрабатываете только минимальный объем данных, чтобы снизить ваши требования к памяти.

Наконец, позвольте мне отметить, что сложные задачи требуют комплексных мер.Если вы считаете, что можете позволить себе 64-разрядную машину с 8 ГБ оперативной памяти, тогда просто используйте алгоритм "чтение файла в память, обработка данных, запись выходных данных", даже если на завершение работы уйдет день.

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

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

Это старый вопрос, но, поскольку я недавно сделал то же самое ....

Простого ответа не существует.В идеальном мире вы бы использовали машину с огромным адресным пространством (т. Е. 64-разрядную версию) и огромным объемом физической памяти.Одного только огромного адресного пространства недостаточно, иначе оно просто будет барахлить.В этом случае проанализируйте XML-файл в базе данных и с помощью соответствующих запросов извлеките то, что вам нужно.Вполне вероятно, что это то, что делает сама OSM (я полагаю, что в мире около 330 ГБ).

На самом деле я все еще использую XP 32bit по соображениям целесообразности.

Это компромисс между пространством и скоростью.Вы можете делать практически все, что угодно, в любом объеме памяти, при условии, что вам все равно, сколько времени это займет.Используя структуры STL, вы можете анализировать все, что захотите, но вскоре у вас закончится память.Вы можете определить свои собственные распределители, которые меняются местами, но опять же, это будет неэффективно, потому что карты, векторы, наборы и т.д. На самом деле Не знают, что вы делаете.

Единственный способ, который я нашел, чтобы заставить все это работать на небольшом пространстве на 32-разрядной машине, состоял в том, чтобы очень тщательно продумать, что я делаю и что когда необходимо, и разбить задачу на куски.Экономичный объем памяти (никогда не использует более ~ 100 МБ), но не очень быстрый, но тогда это не имеет значения - как часто приходится анализировать XML-данные?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top