Как избежать фрагментации кучи? - PullRequest
13 голосов
/ 30 сентября 2008

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

Приложение написано на C ++ и работает в Windows XP.

РЕДАКТИРОВАТЬ: Приложение выполняет некоторую предварительную обработку данных изображения, например переформатирование, вычисление справочных таблиц, извлечение представляющих интерес подизображений ... При обработке приложению требуется около 2 ГБ ОЗУ из которых около 1,5 ГБ можно использовать для данных изображения.

Ответы [ 9 ]

15 голосов
/ 30 сентября 2008

Если вы выполняете обработку медицинских изображений, вполне вероятно, что вы выделяете большие блоки за один раз (512x512, 2 байта на пиксель изображения). Фрагментация укусит вас, если вы распределите меньшие объекты между выделениями буферов изображений.

Написание собственного распределителя не обязательно сложно для этого конкретного варианта использования. Вы можете использовать стандартный распределитель C ++ для вашего объекта Image, но для пиксельного буфера вы можете использовать пользовательское распределение, которое все управляется внутри вашего объекта Image. Вот быстрый и грязный план:

  • Используйте статический массив структур, каждая структура имеет:
    • Твердый кусок памяти, который может содержать N изображений - фрагментация поможет контролировать фрагментацию - попробуйте начальное N, равное 5 или около того
    • Параллельный массив bools, указывающий, используется ли соответствующее изображение
  • Чтобы выделить, найдите в массиве пустой буфер и установите его флаг
    • Если ничего не найдено, добавьте новую структуру в конец массива
  • Для освобождения найдите соответствующий буфер в массиве (-ах) и очистите логический флаг

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

5 голосов
/ 30 сентября 2008

Если вы собираетесь выполнять операции с матрицей большого изображения, вы можете рассмотреть методику, называемую «мозаика». Идея, как правило, заключается в загрузке изображения в память, чтобы один и тот же непрерывный блок байтов не содержал пиксели в одной строке, а имел квадрат в двухмерном пространстве. Основанием для этого является то, что вы будете выполнять больше операций, которые расположены ближе друг к другу в 2D, а не на одной строке сканирования.

Это не приведет к сокращению использования памяти, но может оказать огромное влияние на обмен страниц и производительность.

5 голосов
/ 30 сентября 2008

Есть ответы, но сложно быть общим, не зная деталей проблемы.

Я предполагаю, что 32-битная Windows XP.

Старайтесь не тратить 100 МБ непрерывной памяти, если вам не повезло, несколько случайных dll загрузятся в неудобных точках через доступное адресное пространство, быстро сокращая очень большие области непрерывной памяти. В зависимости от того, какие API вам нужны, это может быть довольно сложно предотвратить. Может быть весьма удивительно, как простое выделение пары 400 МБ блоков памяти в дополнение к некоторому «нормальному» использованию памяти может оставить вас некуда выделить окончательный «маленький» 40 МБ блок.

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

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

Выбор места для вывода страниц - другое решение. Вы можете решить просто записать их во временные файлы. Вы также можете изучить API Microsoft Window Addressing Extentions. В любом случае вам нужно быть осторожным в дизайне своего приложения, чтобы очистить любые указатели, указывающие на что-то, что должно быть выгружено, иначе произойдут действительно плохие вещи (tm).

Удачи!

2 голосов
/ 05 ноября 2008

Здесь вы столкнетесь с ограничением диапазона виртуальных адресов, которое при 32-битной Windows дает максимум 2 ГБ. Вы также должны знать, что использование графического API, такого как DirectX или OpenGL, будет использовать обширные части этих 2 ГБ для буфера кадров, текстур и подобных данных.

1,5-2 ГБ для 32-битных приложений довольно сложно достичь. Самый элегантный способ сделать это - использовать 64-битную ОС и 64-битное приложение. Даже с 64-битной ОС и 32-битным приложением это может быть несколько жизнеспособным, если вы используете LARGE_ADDRESS_AWARE.

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

2 голосов
/ 30 сентября 2008

Без гораздо большей информации о проблеме (например, о языке) вы можете избежать одной проблемы с распределением ресурсов путем повторного использования, а не распределения, работы и освобождения. Распределитель, такой как dlmalloc , обрабатывает фрагментацию лучше, чем кучи Win32.

1 голос
/ 30 сентября 2008

Возможно, вам потребуется реализовать ручное управление памятью. Являются ли данные изображения долгоживущими? Если нет, то вы можете использовать шаблон, используемый веб-сервером apache: выделять большие объемы памяти и помещать их в пулы памяти. Передайте эти пулы в качестве последнего аргумента в функциях, чтобы они могли использовать пул для удовлетворения необходимости выделения временной памяти. После завершения цепочки вызовов вся память в пуле больше не должна использоваться, поэтому вы можете очистить область памяти и использовать ее снова. Распределение быстрое, так как оно означает только добавление значения к указателю. Распределение памяти происходит очень быстро, поскольку вы сразу освобождаете очень большие блоки памяти.

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

1 голос
/ 30 сентября 2008

Я предполагаю, что вы используете что-то неуправляемое, потому что на управляемых платформах система (сборщик мусора) заботится о фрагментации.

Для C / C ++ вы можете использовать другой распределитель, отличный от используемого по умолчанию. (было несколько потоков о распределителях в stackowerflow).

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

1 голос
/ 30 сентября 2008

Угадайте, что вы имели в виду избегайте фрагментации , а не избегайте дефрагментации . Также предполагаю, что вы работаете с неуправляемым языком (вероятно, c или C ++). Я хотел бы предложить, чтобы вы выделяли большие куски памяти, а затем обслуживали выделения кучи из выделенных блоков памяти. Этот пул памяти, поскольку содержит большие блоки памяти, в меньшей степени подвержен фрагментации. Подводя итог, вы должны реализовать пользовательский распределитель памяти.

См. Некоторые общие идеи по этому вопросу здесь .

0 голосов
/ 17 февраля 2010

Если вы можете изолировать именно те места, где вы можете выделить большие блоки, вы можете (в Windows) напрямую вызывать VirtualAlloc вместо того, чтобы обращаться к диспетчеру памяти. Это позволит избежать фрагментации в обычном диспетчере памяти.

Это простое решение, не требующее использования собственного менеджера памяти.

...