Словарь C # - как решить ограничение по количеству предметов? - PullRequest
3 голосов
/ 24 октября 2011

Я использую словарь, и мне нужно хранить в нем почти 13 000 000 ключей. К сожалению, после добавления 11 950 000-го ключа я получил исключение «Системе не хватает памяти». Есть ли решение этой проблемы? Мне нужно, чтобы моя программа работала на менее мощных компьютерах, чем на самом деле в будущем ..

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

Любая помощь будет оценена.

Ответы [ 8 ]

9 голосов
/ 24 октября 2011

Купите больше памяти, установите 64-битную версию ОС и перекомпилируйте для 64-битной.Нет, я не шучу.Если вы хотите так много объектов ... в оперативной памяти ... А затем назовите это "особенность".Если новый Android может потребовать 16 ГБ памяти для компиляции ...

Я забыл ... Вы могли бы начать с чтения массива C # объектов, очень больших размеров, в поисках лучшего способа

Вы знаете, сколько 13 миллионов объектов?

Для сравнения 32-разрядное приложение Windows имеет доступ к менее чем 2 ГБ адресного пространства.Таким образом, это 2 миллиарда байтов (дать или взять) ... 2 миллиарда / 13 миллионов = что-то около 150 байтов / объект.Теперь, если мы посмотрим, сколько занимает ссылочный тип ... 150 байтов довольно просто съесть.

Я добавлю кое-что: я посмотрел в своем Magic 8-Ball и он сказал мне: покажи нам свой код .Если вы не сообщите нам, что вы используете для ключа и значений, как мы можем вам помочь?Что вы используете, class или struct или "примитивные" типы?Скажите нам «размер» ваших TKey и TValue.К сожалению, наш хрустальный шар сломался вчера: -)

6 голосов
/ 24 октября 2011

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

Тебе придется пойти с каким-то внешним хранилищем. Я рекомендую купить базу данных и использовать ее для хранения ваших данных. Затем используйте DataSet или аналогичную технологию для загрузки частей данных в память, манипулирования ими, а затем добавьте больше данных из базы данных в DataSet и т. Д.

5 голосов
/ 31 июля 2013

Ну, у меня была почти точно такая же проблема.

Я хотел загрузить около 12,5 миллионов [string, int] s в словарь из базы данных (для всех программирующих "богов" выше, кто неЯ не понимаю, почему, ответ заключается в том, что при работе с базой данных объемом 150 ГБ намного быстрее, если вы можете кэшировать часть одной из таблиц ключей в памяти).

Это досадно выбрасывает из памятиИсключение составляет почти то же самое место - чуть ниже отметки в 12 миллионов, хотя процесс потреблял только около 1,3 ГБ памяти (сокращено до 800 МБ памяти после разумного изменения в методе чтения БД, чтобы не пытаться делать все это наодин раз) - несмотря на работу на I7 с 8 ГБ памяти.

Решение было на самом деле удивительно простым - в Visual Studio (2010) в обозревателе решений щелкните правой кнопкой мыши проект и выберите свойства.На вкладке Build установите Platform Target равным x64 и перестройте.

Он загружается через словарь за несколько секунд, и производительность словаря очень хорошая.

0 голосов
/ 24 октября 2011

Проблема не в объекте Dictionary, а в доступной памяти на вашем сервере.Я провел некоторое исследование, чтобы понять сбои объекта словаря, но он никогда не был неудачным.Ниже приведен код для вашей справки

    private static void TestDictionaryLimit()
    {
        int intCnt = 0;
        Dictionary<long, string> dItems = new Dictionary<long, string>();
        Console.WriteLine("Total number of iterations = {0}", long.MaxValue);
        Console.WriteLine("....");
        for (long lngCnt = 0; lngCnt < long.MaxValue; lngCnt++)
        {
            if (lngCnt < 11950020)
                dItems.Add(lngCnt, lngCnt.ToString());
            else
                break;
            if ((lngCnt % 100000).Equals(0))
                Console.Write(intCnt++);
        }
        Console.WriteLine("Completed..");
        Console.WriteLine("{0} number of items in dictionary", dItems.Count);
    }

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

0 голосов
/ 24 октября 2011

Я думаю, что вам нужен новый подход к вашей обработке.

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

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

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

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

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

0 голосов
/ 24 октября 2011

На самом деле 13000000 предметов довольно много.Если 13000000 распределенных классов - это очень глубокий удар в желудок сборщика мусора!

Также, если вы найдете способ использовать словарь .NET по умолчанию, производительность будет очень плохой, слишком много ключей, количество ключейприближается к числу значений, которые может использовать 31-битный хеш, производительность будет ужасной в любой используемой вами системе, и, конечно, памяти будет слишком много!

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

Вы не можете полагаться на .net hashtable наверняка для этой столь странной и конкретной проблемы.

Учтите, что дерево имеет сложность поискаO (log n), в то время как сложность построения O (n * log n), конечно, строить его будет слишком долго.Затем вы должны создать хеш-таблицу из двоичных деревьев (или наоборот), которая позволит вам использовать обе структуры данных, занимающие меньше памяти.

Затем подумайте о компиляции в 32-битном режиме, а не в 64-битном режиме: 64битовый режим использует больше памяти для указателей.В то же время, наоборот, 32-битное адресное пространство может оказаться недостаточным для вашей проблемы.Мне никогда не приходилось сталкиваться с проблемой, которая могла бы исчерпать 32-битное адресное пространство!

Если и ключи, и значения являются простыми типами значений, я бы предложил вам записать структуру данных в C dll и использовать ее черезC #.

Можно попытаться написать словарь словарей.Допустим, вы можете разбить ваши данные на куски по 500000 элементов, например, между 26 словарями, но занятая память будет очень большой, не думайте, что ваша система справится с этим.

public class MySuperDictionary
{
    private readonly Dictionary<KEY, VALUE>[] dictionaries;

    public MySuperDictionary()
    {
        this.dictionaries = new Dictionary<KEY, VALUE>[373]; // must be a prime number.
        for (int i = 0; i < dictionaries.Length; ++i)
            dictionaries[i] = new Dicionary<KEY, VALUE>(13000000 / dictionaries.Length);
    }

    public void Add(KEY key, VALUE value)
    {
        int bucket = (GetSecondaryHashCode(key) & 0x7FFFFFFF) % dictionaries.Length;
        dictionaries[bucket].Add(key, value);
    }

    public bool Remove(KEY key)
    {
        int bucket = (GetSecondaryHashCode(key) & 0x7FFFFFFF) % dictionaries.Length;
        return dictionaries[bucket].Remove(key);
    }

    public bool TryGetValue(KEY key, out VALUE result)
    {
        int bucket = (GetSecondaryHashCode(key) & 0x7FFFFFFF) % dictionaries.Length;
        return dictionaries[bucket].TryGetValue(key, out result);
    }

    public static int GetSecondaryHashCode(KEY key)
    {
        here you should return an hash code for key possibly using a different hashing algorithm than the algorithm you use in inner dictionaries
    }
}
0 голосов
/ 24 октября 2011

Простое решение - просто используйте простую БД.Наиболее очевидным решением в этом случае, IMHO, является использование SQLite .NET , быстрое, простое и с небольшим объемом памяти.

0 голосов
/ 24 октября 2011

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

...