Словарь C# - как решить ограничение по количеству предметов?

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

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

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

8 ответов

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

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

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

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

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

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

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

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

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

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

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

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

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

Проблема не в объекте 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);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

Если и ключи, и значения являются простыми типами значений, я бы посоветовал вам написать структуру данных на языке Cll и использовать ее через 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
    }
}

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

Другие вопросы по тегам