Ищите технику для загрузки большого количества объектов в IDictionary в .NET - PullRequest
1 голос
/ 19 сентября 2011

Мне нужно загрузить около 6 миллионов объектов в словарь.У меня проблема в том, что я просто добавляю их в словарь, создавая их фрагменты памяти, так как словарь выделяет новые массивы и освобождает существующие.В итоге, таким образом, я мог загрузить только 2 миллиона из них из-за фрагментации свободной памяти.Проблема в том, что я не знаю фактического количества предметов.Все зависит от ввода пользователя.

Мое не очень идеальное решение таково:1. Используйте связанный список для хранения всех объектов после их создания.Я делаю это, так как связанные списки не нуждаются в непрерывном свободном пространстве2. Создайте словарь с нужным размером, поэтому нет необходимости перераспределять внутренние словарные массивы.3. скопировать объекты в словарь.Таким образом, я могу загрузить до 3 миллионов

Любые предложения о том, как я могу улучшить это?Или вам известна бесплатная реализация IDictionary, которая не использует массивы внутри.

Спасибо

ОБНОВЛЕНИЕ: мои ключи представляют собой строки фиксированной длины в зависимости от типа значения.Обычно около 8 символов, но может быть до 20 символов.И общее возможное количество элементов взрывается при увеличении длины ключа.К счастью, текущее максимальное количество предметов составляет 12 миллионов.Значение представляет собой тип класса общим размером примерно 90-120 байт на экземпляр

Это приложение winforms, работающее в 32-битных окнах.И мой типичный хост-компьютер имеет 2 ГБ памяти.В приложении много траты памяти, которая занимает много места.К сожалению, я не могу обратиться к ним сейчас.

Ответы [ 3 ]

2 голосов
/ 19 сентября 2011

Всю проблему фрагментации можно решить с помощью емкости:

var d = new Dictionary<int, string>(expectedCapacity);

expectedCapacity следует рассчитывать пессимистично и с небольшим запасом места.

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

Фрагментация - это проблема только кучи больших объектов, и 6 миллионов пар K, V (~ 6M * 20 = 120 МБ) не должны этого делать.

Но поймите, как растет словарь: когда он полон, он удваивается.Таким образом, при загрузке (чуть более) 8M элементов вы можете получить емкость 16M, с блоками 8M, 4M, 2M и т. Д., Также размещенными на LOH.
Это может вызвать OOM.

Стоит попытаться заранее оценить количество предметов.

1 голос
/ 20 сентября 2011

Поможет ли какое-нибудь разбиение?

Я использовал подход, в котором я вычисляю байтовый хэш, используя XOR GetHashCode() ключа словаря, чтобы разделить словарь на 256 меньших. В основном у вас есть внутренний Dictionary<byte, Dictionary<K, V>>, который содержит значения для внешнего IDictionary<K, V>.

Если вы начали с большого словарного класса, подобного этому:

public class LargeDictionary<K, V> : IDictionary<K, V>
{
    private readonly Dictionary<byte, Dictionary<K, V>> _inner =
            new Dictionary<byte, Dictionary<K, V>>();

    private Dictionary<K, V> GetInner(K key)
    {
        var bs = BitConverter.GetBytes(key.GetHashCode());
        var prekey = (byte)(bs[0] ^ bs[1] ^ bs[2] ^ bs[3]);
        if (!_inner.ContainsKey(prekey))
        {
            _inner.Add(prekey, new Dictionary<K, V>());
        }
        return _inner[prekey];
    }

    /* See below */

}

Сможете ли вы начать с этого и, возможно, перестроить части внутреннего словаря, чтобы восстановить память по мере продвижения?

Вот остальные классы:

    public void Add(K key, V value)
    {
        this.GetInner(key).Add(key, value);
    }

    public bool ContainsKey(K key)
    {
        return this.GetInner(key).ContainsKey(key);
    }

    public ICollection<K> Keys
    {
        get
        {
            var keys = from pk in _inner.Keys
                       from k in _inner[pk].Keys
                       select k;
            return keys.ToList();
        }
    }

    public bool Remove(K key)
    {
        return this.GetInner(key).Remove(key);
    }

    public bool TryGetValue(K key, out V value)
    {
        return this.GetInner(key).TryGetValue(key, out value);
    }

    public ICollection<V> Values
    {
        get
        {
            var values = from pk in _inner.Keys
                         from v in _inner[pk].Values
                         select v;
            return values.ToList();
        }
    }

    public V this[K key]
    {
        get
        {
            return this.GetInner(key)[key];
        }
        set
        {
            this.GetInner(key)[key] = value;
        }
    }

    public void Add(KeyValuePair<K, V> item)
    {
        this.GetInner(item.Key).Add(item.Key, item.Value);
    }

    public void Clear()
    {
        _inner.Clear();
    }

    public bool Contains(KeyValuePair<K, V> item)
    {
        var inner = this.GetInner(item.Key);
        return inner.ContainsKey(item.Key)
            && inner[item.Key].Equals(item.Value);
    }

    public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
    {
        var source = this.ToArray();
        Array.Copy(source, 0, array, arrayIndex, source.Length);
    }

    public int Count
    {
        get
        {
            var counts = from pk in _inner.Keys
                         select _inner[pk].Count;
            return counts.Sum();
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(KeyValuePair<K, V> item)
    {
        return this.GetInner(item.Key).Remove(item.Key);
    }

    public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
    {
        return _inner.Keys.SelectMany(pk => _inner[pk]).GetEnumerator();
    }

    System.Collections.IEnumerator
            System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
0 голосов
/ 19 сентября 2011

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

Будет ли иметь смысл иметь это вне приложения ?может быть, в базе данных (возможно, с использованием формата, такого как SQLite или SQLServer Compact)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...