Если у меня есть, скажем, 100 элементов, которые будут храниться в словаре, следует ли инициализировать его таким образом?
var myDictionary = new Dictionary<Key, Value>(100);
Насколько я понимаю, словарь .NET внутренне меняет размер, когда достигает заданной загрузки, и что порог загрузки определяется как отношение емкости.
Это предполагает, что если бы в приведенный выше словарь было добавлено 100 элементов, то он сам изменил бы размер при добавлении одного из элементов. Я бы хотел избежать изменения размера словаря, так как он снижает производительность и расходует память.
Вероятность коллизий хеширования пропорциональна загрузке словаря. Следовательно, даже если словарь не изменяет размер (и не использует все свои слоты), производительность должна ухудшаться из-за этих конфликтов.
Как лучше всего решить, для какой емкости инициализировать словарь, если вы знаете, сколько элементов будет внутри словаря?





Я думаю, вы слишком усложняете дело. Если вы знаете, сколько элементов будет в вашем словаре, то обязательно укажите это при построении. Это поможет словарю выделить необходимое пространство во внутренних структурах данных, чтобы избежать перераспределения и перетасовки данных.
Я согласен, Кент. Я должен был отметить этот вопрос как «академический». Словари являются ключевыми (преднамеренно каламбурными) конструкциями программирования, и мне нравится вдаваться в мелочи о таких повседневных вещах, как эта. Мой основной вопрос: уменьшает ли выделение пространства дополнительный коллизии и повышает производительность?
Да, в отличие от HashTable, который использует повторное хеширование как метод разрешения коллизий, Dictionary будет использовать цепочку. Так что да, использовать счетчик - это хорошо. Для HashTable вы, вероятно, захотите использовать count * (1/fillfactor)
Интересно отметить различие между перефразированием и связыванием. Спасибо. В любом случае, тем не менее, все еще происходит какое-то разрешение конфликтов, которое будет иметь влияние некоторый на производительность. Вы хотите сказать, что при цепочке это меньше?
Это связано со средней длиной цепочки, которая, в свою очередь, связана с количеством столкновений.
Нет, я не говорю, что меньше. По-разному. Но когда вы используете цепочку, пространство хранения, используемое ссылками, не учитывается в самой хеш-таблице, что снижает потребность в дополнительном пространстве, если имеет место коллизия.
Указание начальной емкости для конструктора Dictionary увеличивает производительность, поскольку будет меньше изменений размеров внутренних структур, которые хранят значения словаря во время операций ADD.
Учитывая, что вы указываете конструктору Dictionary начальную емкость k, тогда:
Dictionary зарезервирует объем памяти, необходимый для хранения k элементов;От MSDN:
The capacity of a Dictionary(TKey, TValue) is the number of elements that can be added to the Dictionary(TKey, TValue) before resizing is necessary. As elements are added to a Dictionary(TKey, TValue), the capacity is automatically increased as required by reallocating the internal array.
If the size of the collection can be estimated, specifying the initial capacity eliminates the need to perform a number of resizing operations while adding elements to the Dictionary(TKey, TValue).
Я согласен с документацией :) Тем не менее, я хочу знать, уменьшит ли размер дополнительный количество разрешений коллизий и, следовательно, повысит производительность за счет дополнительных потерь памяти.
Если вы говорите о выполнении ЗАПРОСОВ против словаря, нет, быстрее не будет. Начальная емкость k зарезервирует объем памяти, необходимый для хранения k элементов. Операции ADD не потребуют большего выделения памяти (возможно, дорого) и, следовательно, будут быстрее.
@smink, я не совсем с тобой согласен. Процесс поиска в словаре смотрится в «ведре» на основе хэш-кода. Несколько записей могут предпочесть эту корзину, но ее получает тот, кто добавлен первым. Другие связаны цепочкой, что означает, что поиск этих других не так эффективен, как первый.
@smink, кроме того, наличие большего начального размера словаря уменьшит количество хэш-коллизий и, следовательно, уменьшит среднюю длину цепочки, улучшив скорость поиска (хотя потенциально незначительно).
Я провел быстрый тест, вероятно, не научный, но если я установил размер, потребовалось 1,2207780 секунд, чтобы добавить один миллион элементов, и 1,5024960 секунд, чтобы добавить, если я не указал размер словаря ... мне это кажется незначительным .
Вот мой тестовый код, может быть, кто-то сможет провести более строгий тест, но я сомневаюсь, что это имеет значение.
static void Main(string[] args)
{
DateTime start1 = DateTime.Now;
var dict1 = new Dictionary<string, string>(1000000);
for (int i = 0; i < 1000000; i++)
dict1.Add(i.ToString(), i.ToString());
DateTime stop1 = DateTime.Now;
DateTime start2 = DateTime.Now;
var dict2 = new Dictionary<string, string>();
for (int i = 0; i < 1000000; i++)
dict2.Add(i.ToString(), i.ToString());
DateTime stop2 = DateTime.Now;
Console.WriteLine("Time with size initialized: " + (stop1.Subtract(start1)) + "\nTime without size initialized: " + (stop2.Subtract(start2)));
Console.ReadLine();
}
Интересно. Для справки в будущем вам следует использовать класс System.Diagnostics.Stopwatch при измерении такого времени. DateTime.Now даст вам разрешение только 15 мс, но секундомер дает разрешение примерно 0,01 мс.
Я хочу знать, будет ли быстрее указывать размер, скажем, 2 000 000, и добавлять 1 000 000 из-за уменьшения нагрузки и, следовательно, уменьшения количества цепочек.
То же самое при использовании System.Diagnostics.Stopwatch в отличие от DateTime.Now
Первоначальный размер - это всего лишь предположение. Например, большинство хеш-таблиц предпочитают, чтобы размеры были простыми числами или степенью двойки.
Хеш-таблица с размером степени 2? Он хорошо работает?
Для меня простые числа звучат лучше, чем степени двойки. Платформа .NET (mscorlib.dll v2.0.0.0) вызывает внутренний метод HashHelpers.GetPrime для нахождения следующего по величине простого числа после «емкости». Он ищет кеш простых чисел и выполняет поиск методом перебора, если его емкость превышает 7199369 :)
То, чем вы должны инициализировать емкость словаря, зависит от двух факторов: (1) Распределение функции gethashcode и (2) Сколько элементов нужно вставить.
Ваша хеш-функция должна быть либо случайным образом распределена, либо она должна быть специально сформулирована для вашего набора входных данных. Допустим, первое, но если вас интересует второе, поищите идеальные хеш-функции.
Если у вас есть 100 элементов для вставки в словарь, случайным образом распределенная хеш-функция, и вы устанавливаете емкость равной 100, то, когда вы вставляете i-й элемент в хеш-таблицу, у вас будет (i-1) / 100 вероятность того, что i-й элемент элемент будет сталкиваться с другим элементом при вставке. Если вы хотите снизить вероятность столкновения, увеличьте пропускную способность. Увеличение ожидаемой вместимости вдвое снижает вероятность столкновения вдвое.
Кроме того, если вы знаете, как часто вы собираетесь обращаться к каждому элементу в словаре, вы можете вставлять элементы в порядке убывания частоты, поскольку элементы, которые вы вставляете первыми, в среднем будут доступны быстрее.
вау, вставляя часто используемые элементы перед редко используемыми элементами, чтобы повысить производительность. Никогда об этом не думал.
Есть ли требование, что физические хеш-ведра фактически выровнять для указанной емкости? Я бы предположил, что можно бесплатно выбрать подходящее количество сегментов, если оно соответствует «Емкость Dictionary <TKey, TValue> - это количество элементов, которые могут быть добавлены в Dictionary <TKey, TValue> перед изменением размера. . "
@StingyJack: не обязательно. По причинам реализации класс словаря не удваивает свое хранилище. Скорее, пространство выделяется для размещения простого числа элементов, потому что это делает столкновения по модулю гораздо более редкими.