В настоящее время я работаю над проектом по обработке медицинских изображений, для которого требуется огромное количество памяти. Могу ли я что-нибудь сделать, чтобы избежать фрагментации кучи и ускорить доступ к данным изображения, которые уже были загружены в память?
Приложение написано на C++ и работает в Windows XP.
Обновлено: Приложение выполняет некоторую предварительную обработку данных изображения, такую как переформатирование, вычисление справочных таблиц, извлечение интересующих фрагментов изображений ... Приложению требуется около 2 ГБ ОЗУ во время обработки, из которых около 1,5 ГБ может использоваться для данные изображения.
В этом вопросе нужна некоторая ясность. Что такое «огромный» объем памяти? Какой язык (как отмечалось ранее). Где вас беспокоит фрагментация - в уже загруженных данных изображения, или вы беспокоитесь, потому что вам нужно манипулировать данными?
Этот вопрос обсуждался здесь. Возможно, вы найдете там полезную информацию.
Вы можете написать свой собственный распределитель, но это жесткий.


Не обладая дополнительной информацией о проблеме (например, о языке), вы можете сделать одно - избежать оттока выделения, повторно используя выделения, а не выделять, работать и освобождать. Распределитель, такой как dlmalloc, обрабатывает фрагментацию лучше, чем кучи Win32.
Догадываясь здесь, вы имели в виду избегать фрагментации, а не избегать дефрагментации. Также предполагаю, что вы работаете с неуправляемым языком (вероятно, c или C++). Я бы посоветовал вам выделить большие куски памяти, а затем обслуживать выделение кучи из выделенных блоков памяти. Этот пул памяти, поскольку содержит большие блоки памяти, менее подвержен фрагментации. Подводя итог, вы должны реализовать собственный распределитель памяти.
См. Некоторые общие идеи по этому здесь.
Я предполагаю, что вы используете что-то неуправляемое, потому что на управляемых платформах система (сборщик мусора) заботится о фрагментации.
Для C / C++ вы можете использовать другой распределитель, отличный от стандартного. (уже были некоторые темы о распределителях в stackowerflow).
Также вы можете создать собственное хранилище данных. Например, в проекте, над которым я сейчас работаю, у нас есть настраиваемое хранилище (пул) для растровых изображений (мы храним их в большом непрерывном блоке памяти), потому что у нас их много, и мы отслеживаем кучу фрагментация и дефрагментация, когда фрагментация слишком велика.
Фрагментация не зависит от сборки мусора. Это происходит, когда долгоживущие объекты разбросаны по куче, потому что их распределение смешано с распределением короткоживущих объектов. Каким образом распределяется недолговечный материал, не имеет значения.
Хороший сборщик мусора позаботится о фрагментации, перемещая объекты и обновляя ссылки.
Я собирался возразить по тем или иным строкам, но я уступил. Если у вас также есть доступ ко всей памяти через GC-plus-plus, это может быть. Я тебе рассказывал, как дойти до школы за три мили ...
Возможно, вам потребуется реализовать ручное управление памятью. Являются ли данные изображения долгоживущими? Если нет, то вы можете использовать шаблон, используемый веб-сервером apache: выделить большие объемы памяти и поместить их в пулы памяти. Передайте эти пулы в качестве последнего аргумента функций, чтобы они могли использовать пул для удовлетворения потребности в выделении временной памяти. После завершения цепочки вызовов вся память в пуле больше не должна использоваться, поэтому вы можете очистить область памяти и использовать ее снова. Распределения выполняются быстро, поскольку они означают только добавление значения к указателю. Освобождение выполняется очень быстро, поскольку вы сразу освобождаете очень большие блоки памяти.
Если ваше приложение является многопоточным, вам может потребоваться сохранить пул в локальном хранилище потока, чтобы избежать накладных расходов на межпотоковую связь.
Если вы обрабатываете медицинские изображения, вполне вероятно, что вы выделяете большие блоки за раз (512x512, изображения размером 2 байта на пиксель). Фрагментация вас укусит, если вы выделите меньшие объекты между в качестве буферов изображений.
Написание специального распределителя не обязательно сложно для этого конкретного случая использования. Вы можете использовать стандартный распределитель C++ для вашего объекта Image, но для пиксельного буфера вы можете использовать настраиваемое распределение, которое полностью управляется в вашем объекте Image. Вот краткий и грязный план:
Это всего лишь одна простая идея с множеством возможностей для вариаций. Основная уловка - избежать освобождения и перераспределения буферов пикселей изображения.
Привет, Джефф, ты когда-нибудь работал над обработкой медицинских изображений?
Время от времени :) Возможно, вы захотите проверить vtk или даже osirix для справочного материала ... На самом деле, идея выше - это просто своего рода стандартный способ обработки пользовательского выделения одинакового размера в C++, хотя ...
На это есть ответы, но трудно говорить в общих чертах, не зная деталей проблемы.
Я предполагаю 32-битную Windows XP.
Старайтесь избегать необходимости в сотнях МБ непрерывной памяти, если вам не повезет, несколько случайных dll загрузятся в неудобных местах через доступное адресное пространство, быстро сократив очень большие области непрерывной памяти. В зависимости от того, какие API вам нужны, этого может быть довольно сложно предотвратить. Это может быть довольно удивительно, как простое выделение пары блоков памяти по 400 МБ в дополнение к некоторому «нормальному» использованию памяти может оставить вам некуда выделить последний «маленький» блок размером 40 МБ.
С другой стороны, заранее выделяйте блоки разумного размера за раз. Примерно 10 МБ или около того - это хороший компромиссный размер блока. Если вам удастся разделить данные на блоки такого размера, вы сможете достаточно эффективно заполнить адресное пространство.
Если вам по-прежнему не хватает адресного пространства, вам нужно будет иметь возможность вставлять и выводить блоки страниц на основе какого-то алгоритма кеширования. Выбор правильных блоков для вывода на страницу будет во многом зависеть от вашего алгоритма обработки и потребует тщательного анализа.
Другое решение - выбор места для публикации. Вы можете просто записать их во временные файлы. Вы также можете изучить API расширений окон для адресов Microsoft. В любом случае вам нужно быть осторожным в дизайне вашего приложения, чтобы убрать все указатели, указывающие на то, что должно быть выгружено, иначе произойдут действительно плохие вещи (tm).
Удачи!
Если вы собираетесь выполнять операции с большой матрицей изображения, вы можете рассмотреть метод, называемый «мозаичным». Обычно идея состоит в том, чтобы загрузить изображение в память так, чтобы один и тот же непрерывный блок байтов содержал не пиксели в одной строке, а квадрат в 2D-пространстве. Обоснование этого состоит в том, что вы будете выполнять больше операций, расположенных ближе друг к другу в 2D, а не на одной строке сканирования.
Это не уменьшит использование памяти, но может иметь огромное влияние на подкачку страниц и производительность.
Здесь вы столкнетесь с ограничением диапазона виртуальных адресов, которое с 32-битной Windows дает вам не более 2 ГБ. Вы также должны знать, что при использовании графического API, такого как DirectX или OpenGL, будут использоваться обширные части этих 2 ГБ для буфера кадра, текстур и подобных данных.
1,5-2 ГБ для приложения 32b достичь довольно сложно. Самый элегантный способ сделать это - использовать 64-битную ОС и 64-битное приложение. Даже с ОС 64b и приложением 32b это может быть в некоторой степени жизнеспособным, если вы используете LARGE_ADDRESS_AWARE.
Однако, поскольку вам нужно хранить данные изображения, вы также можете обойти это, используя Отображение файлов как хранилище памяти - это можно сделать таким образом, чтобы у вас была выделенная и доступная память, но не используя никаких виртуальных адресов вообще.
Если вы можете изолировать именно те места, где вы, вероятно, разместите большие блоки, вы можете (в Windows) напрямую вызвать VirtualAlloc вместо того, чтобы проходить через диспетчер памяти. Это позволит избежать фрагментации в обычном диспетчере памяти.
Это простое решение, и оно не требует использования специального диспетчера памяти.
Я делаю ставку на C++, но это правда: на этот вопрос нельзя ответить, пока не известна основная «парадигма» ...