С# фрагментированный массив

Мне нужно выделить очень большие массивы простых структур (1 ГБ ОЗУ). После нескольких выделений/освобождений память фрагментируется и возникает исключение OutOfMemory.

Это под 32 бит. Я бы предпочел не использовать 64-битную версию из-за снижения производительности — одно и то же приложение работает на 30% медленнее в 64-битном режиме.

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


person Meh    schedule 03.11.2010    source источник
comment
Странно, это действительно неправильный вопрос. Он должен был спросить, почему 64-битная программа работает на 30% медленнее? Ваш 32-битный процесс будет намного медленнее после того, как вы разделите массивы.   -  person Hans Passant    schedule 03.11.2010
comment
Первоначально приложение было 64-битным, но после прочтения некоторых статей предполагается, что 64-битный JIT не так хорош в оптимизации кода, как 32-битный (stackoverflow.com/questions/14432/64bit-net-performance-tuning), я преобразовал его в 32-битный и обнаружил, что это истинный. Даже если я потеряю скорость здесь, она все равно запустится быстрее, чем 64-битная версия - я использую IronPython + C# + C++/CLI, а запуск 64-битной версии занимает на 10 секунд больше (из-за IronPython)   -  person Meh    schedule 03.11.2010


Ответы (3)


Джош Уильямс представил в своем блоге класс BigArray<T>, используя фрагментированный массив:

BigArray<T>, обход ограничения размера массива в 2 ГБ

Вы найдете больше полезной информации в этом связанном вопросе:

2-мерные массивы C# огромного размера

Простое специальное исправление может состоять в том, чтобы включить переключатель 3 ГБ для вашего приложения. Это позволит вашему приложению использовать более 2 ГБ на процесс в 32-разрядной версии Windows. Однако имейте в виду, что максимальный размер объекта, разрешенный средой CLR, по-прежнему составляет 2 ГБ. Переключатель можно включить с помощью действия после сборки для вашего основного исполняемого файла:

call "$(DevEnvDir)..\tools\vsvars32.bat"
editbin.exe /LARGEADDRESSAWARE "$(TargetPath)"
person Dirk Vollmar    schedule 03.11.2010
comment
Я уже использовал трюк /LARGEADDRESSAWARE, и он мне очень помог - person Meh; 03.11.2010

При создании массива .Net пытается найти непрерывную часть памяти для вашего массива. Поскольку общий лимит памяти для 32-битного приложения составляет 2 ГБ, вы можете видеть, что будет сложно найти такой блок после нескольких выделений.

  1. Вы можете попробовать использовать что-то вроде LinkedList<T>, чтобы избежать необходимости непрерывного распределения, или реструктурировать свой код, чтобы сделать эти фрагменты меньше (хотя вы не будете в полной безопасности, это также не произойдет с массивом 500 МБ).

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

  3. Если вы можете использовать IEnumerable вместо IList для передачи ваших данных в остальную часть вашей программы, вы сможете свернуть этот список, используя метод SelectMany LINQ.

  4. И, наконец, вы можете просто реализовать интерфейс IList в пользовательском классе, а под капотом использовать несколько меньших массивов.

person Groo    schedule 03.11.2010

Подойдет ли вам LinkedList? http://msdn.microsoft.com/en-us/library/he2s3bh7.aspx

person Mike Ruhlin    schedule 03.11.2010
comment
Слишком расточительно хранить каждый элемент (8 байт) в отдельном узле. - person Meh; 03.11.2010