Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

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

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

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

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

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

Звучит так, будто вы делаете txt для двоичной беседы, так зачем вам нужно иметь все данные в памяти?.

Не можете ли вы просто прочитать примитив из txt (xml), а затем сохранить в binarystream?

Ответ 12

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

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

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

Ответ 13

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

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

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

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

Ответ 14

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

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

Ответ 15

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

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

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

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

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