C # огромный размер 2-димных массивов - PullRequest
6 голосов
/ 07 апреля 2010

Мне нужно объявить квадратные матрицы в C # WinForms с более чем 20000 элементов подряд. Я читал о 2 ГБ .Net ограничение размера объекта в 32-битной, а также тот же случай в 64-битной ОС. Итак, как я понял, единственный ответ - это использование небезопасного кода или отдельной библиотеки, созданной с помощью компилятора C ++.

Проблема для меня стоит, потому что ushort [20000,20000] меньше 2 ГБ, но на самом деле я не могу выделить даже 700 МБ памяти. Мой лимит составляет 650 МБ, и я не понимаю, почему - у меня 32-битный WinXP с 3 ГБ памяти. Я попытался использовать Marshal.AllocHGlobal (700 << 20), но он выдает OutOfMemoryException, GC.GetTotalMemory возвращает 4,5 МБ, прежде чем пытаться выделить память. </p>

Я обнаружил только то, что многие говорят, что используют небезопасный код, но я не могу найти пример того, как объявить 2-мерный массив в куче (любой стек не может хранить такой большой объем данных) и как работать с ним с помощью указателей. Это чистый код C ++ внутри небезопасных {} скобок?

PS. Пожалуйста, не спрашивайте, ПОЧЕМУ мне нужны такие огромные массивы ... но если вы хотите - мне нужно проанализировать тексты (например, книги) и найти много индексов. Так что ответ - матрицы отношений между словами

Редактировать: Может ли кто-нибудь предоставить небольшой пример работы с матрицами с использованием указателей в небезопасном коде . Я знаю, что под 32 битами невозможно выделить больше места, но я потратил много времени на поиск такого примера и НИЧЕГО не нашел

Ответы [ 6 ]

5 голосов
/ 07 апреля 2010

Зачем требовать огромный двумерный массив? Вы можете смоделировать это, например, с помощью зубчатого массива - ushort[][] - почти так быстро, и вы не достигнете того же предела одного объекта. Конечно, вам все еще понадобится buckets-o-RAM, поэтому подразумевается x64 ...

        ushort[][] arr = new ushort[size][];
        for(int i = 0 ; i < size ; i++) {
            arr[i] = new ushort[size];
        }

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

4 голосов
/ 07 апреля 2010

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

Обратите внимание, что ограничение виртуального адресного пространства в 32-битной Windows не имеет никакого отношения к объему физической памяти вашего компьютера, в вашем случае 3Gb. Вместо этого ограничение вызвано количеством битов, которые процессор использует для адресации памяти. Неудивительно, что 32-битная Windows использует 32 бита для доступа к каждому адресу памяти, что дает общее адресуемое пространство памяти 4 Гбайт. По умолчанию Windows сохраняет 2 ГБ для себя и дает 2 ГБ текущему процессу, так что вы можете понять, почему CLR не найдет ничего похожего на распределение 2 ГБ. С помощью некоторых хитростей вы можете изменить распределение ОС / пользователей таким образом, чтобы Windows оставляла себе только 1 ГБ и предоставляла работающему процессу 3 Гбит, что может помочь. Однако в 64-битных окнах адресная память, назначаемая каждому процессу, увеличивается до 8 терабайт, поэтому здесь CLR почти наверняка сможет использовать полные 2 Гбит выделения для массивов.

2 голосов
/ 12 апреля 2010

Я так счастлив! :) Недавно я обшарил предметную проблему - попытался решить ее с помощью базы данных, но обнаружил, что этот путь далеко не идеален. Матрица [20000,20000] была реализована в виде единой таблицы. Даже при правильно настроенных индексах время, необходимое только для создания более 400 миллионов записей, составляет около 1 часа на моем ПК. Это не критично для меня. Затем я запустил алгоритм для работы с этой матрицей (требуется два раза, чтобы объединить одну и ту же таблицу!), И после того, как он проработал более получаса, он не сделал ни одного шага. После этого я понял, что единственный способ - найти способ работать с такой матрицей только в памяти и снова вернуться к C #.

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

Как было сказано в моем первом посте, можно использовать 2-мерные массивы только для 650MB под 32-битным WinXP. Результаты после использования Win7 и 64-битной компиляции также были печальными - менее 700MB.

Я использовал JAGGED ARRAYS [] [] вместо одного 2-мерного массива [,], и результаты вы можете увидеть ниже:

Скомпилировано в режиме Release как 32-битное приложение - WinXP 32bit 3GB физ. памятная записка - 1,45 ГБ Скомпилировано в режиме Release как 64-битное приложение - Win7 64-битная 2 ГБ под виртуальной машиной - 7.5 ГБ

- К этому посту прилагаются источники приложения, которые я использовал для тестирования. Я не могу найти здесь, как прикрепить исходные файлы, поэтому просто опишите часть дизайна и поместите здесь ручной код. Создать приложение WinForms. Поставьте на форму такие контролы с именами по умолчанию: 1 кнопка, 1 numericUpDown и 1 список В файле .cs добавьте следующий код и запустите.

private void button1_Click(object sender, EventArgs e)
        {
            //Log(string.Format("Memory used before collection: {0}", GC.GetTotalMemory(false)));
            GC.Collect();
            //Log(string.Format("Memory used after collection: {0}", GC.GetTotalMemory(true)));
            listBox1.Items.Clear();
            if (string.IsNullOrEmpty(numericUpDown1.Text )) {
                Log("Enter integer value");
            }else{
                int val = (int) numericUpDown1.Value;
                Log(TryAllocate(val));
            }
        }

        /// <summary>
        /// Memory Test method
        /// </summary>
        /// <param name="rowLen">in MB</param>
        private IEnumerable<string> TryAllocate(int rowLen) {
            var r = new List<string>();
            r.Add ( string.Format("Allocating using jagged array with overall size (MB) = {0}", ((long)rowLen*rowLen*Marshal.SizeOf(typeof(int))) >> 20) );
            try {
                var ar = new int[rowLen][];
                for (int i = 0; i < ar.Length; i++) {
                    try {
                        ar[i] = new int[rowLen];
                    }
                    catch (Exception e) {
                        r.Add ( string.Format("Unable to allocate memory on step {0}. Allocated {1} MB", i
                            , ((long)rowLen*i*Marshal.SizeOf(typeof(int))) >> 20 ));
                        break;
                    }
                }
                r.Add("Memory was successfully allocated");
            }
            catch (Exception e) {
                r.Add(e.Message + e.StackTrace);
            }
            return r;
        }

        #region Logging

        private void Log(string s) {
            listBox1.Items.Add(s);
        }

        private void Log(IEnumerable<string> s)
        {
            if (s != null) {
                foreach (var ss in s) {
                    listBox1.Items.Add ( ss );
                }
            }
        }

        #endregion

Проблема решена для меня. Ребята, заранее спасибо!

0 голосов
/ 07 апреля 2010

Для OutOfMemoryException прочитайте эту ветку (особенно nobugz и ответ Брайана Расмуссена):
Microsoft Visual C # 2008 Сокращение количества загружаемых библиотек

0 голосов
/ 07 апреля 2010

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

Редизайн также является выбором номер один в этом замечательном сообщении в блоге:

BigArray, преодолевая ограничение размера массива 2 ГБ

Варианты, предлагаемые в этой статье:

0 голосов
/ 07 апреля 2010

Если разреженный массив не применяется, вероятно, лучше просто сделать это в C / C ++ с API-интерфейсами платформы, относящимися к файлу отображения памяти: http://en.wikipedia.org/wiki/Memory-mapped_file

...