2009-04-12 2 views
12

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

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

ответ

14

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

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

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

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

+0

Windows может иметь 3 ГБ виртуального пространства с/LARGEADDRESSAWARE –

+0

Флаг позволяет процессу использовать до 4 ГБ *, если ОС может предоставить его *. Как правило, Windows по-прежнему настроена, чтобы дать всего 2 ГБ каждому процессу. Это тоже можно изменить, рискуя нестабильностью драйверов, чтобы дать вам 3 ГБ, да. С PAE вы можете получить еще больше. Но 64-битная версия, вероятно, лучше. – jalf

+1

Имхо, третий вариант - самый важный. Помимо управления памятью, он также позволяет осуществлять параллельную обработку. – xtofl

4

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

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

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

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

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

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

4

Вы проверили, не потеряли ли вы память?

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

+0

Да, я проверил наличие утечек, их нет. – KPexEA

0

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

2

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

+0

Это не так просто в использовании 3 ГБ. Вы должны убедиться, что все ваши арифметические операции с указателем безопасны, иначе вы столкнетесь, когда использование памяти станет высоким. См. Http://blogs.msdn.com/oldnewthing/archive/2004/08/12/213468.aspx для получения дополнительной информации. – eran

3

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

1

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

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

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

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

1

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

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

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

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

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

1

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

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

+0

Я распределяю точки и полисы с помощью кучного менеджера, поэтому они занимают столько места, сколько необходимо, и почти не слышали, так как моя куча выделена (в данном случае) 1 мб кусков и долей запросов от каждого блока – KPexEA

+0

Другой причиной может быть «память» фрагментация "(т. е. память доступна в небольших кусках, но когда вы запрашиваете« 1 Мб », смежный кусок 1 МБ недоступен. Использует ли XML-анализатор« кучный менеджер »? Может быть, XML-парсер использует стандартное распределение памяти и вызывает фрагментация? –

0

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

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

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

1

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

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

1

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

0

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

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

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

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

0

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

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

0

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

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

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

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

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

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