Следует ли инициализировать общий словарь .NET с емкостью, равной количеству элементов, которые он будет содержать?

Если у меня есть, скажем, 100 элементов, которые будут храниться в словаре, следует ли инициализировать его таким образом?

var myDictionary = new Dictionary<Key, Value>(100);

Насколько я понимаю, словарь .NET внутренне меняет размер, когда достигает заданной загрузки, и что порог загрузки определяется как отношение емкости.

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

Вероятность коллизий хеширования пропорциональна загрузке словаря. Следовательно, даже если словарь не изменяет размер (и не использует все свои слоты), производительность должна ухудшаться из-за этих конфликтов.

Как лучше всего решить, для какой емкости инициализировать словарь, если вы знаете, сколько элементов будет внутри словаря?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
13
0
4 545
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

@StingyJack: не обязательно. По причинам реализации класс словаря не удваивает свое хранилище. Скорее, пространство выделяется для размещения простого числа элементов, потому что это делает столкновения по модулю гораздо более редкими.

Konrad Rudolph 05.01.2009 22:11

Я согласен, Кент. Я должен был отметить этот вопрос как «академический». Словари являются ключевыми (преднамеренно каламбурными) конструкциями программирования, и мне нравится вдаваться в мелочи о таких повседневных вещах, как эта. Мой основной вопрос: уменьшает ли выделение пространства дополнительный коллизии и повышает производительность?

Drew Noakes 05.01.2009 22:58

Да, в отличие от HashTable, который использует повторное хеширование как метод разрешения коллизий, Dictionary будет использовать цепочку. Так что да, использовать счетчик - это хорошо. Для HashTable вы, вероятно, захотите использовать count * (1/fillfactor)

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

Drew Noakes 05.01.2009 22:49

Это связано со средней длиной цепочки, которая, в свою очередь, связана с количеством столкновений.

Mitch Wheat 06.01.2009 12:49

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

mmx 06.01.2009 13:38

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

Учитывая, что вы указываете конструктору Dictionary начальную емкость k, тогда:

  1. Dictionary зарезервирует объем памяти, необходимый для хранения k элементов;
  2. На производительность QUERY по словарю не влияет, и он не будет ни быстрее, ни медленнее;
  3. Операции ADD не потребуют большего выделения памяти (возможно, дорого) и, следовательно, будут быстрее.

От 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).

Я согласен с документацией :) Тем не менее, я хочу знать, уменьшит ли размер дополнительный количество разрешений коллизий и, следовательно, повысит производительность за счет дополнительных потерь памяти.

Drew Noakes 05.01.2009 22:54

Если вы говорите о выполнении ЗАПРОСОВ против словаря, нет, быстрее не будет. Начальная емкость k зарезервирует объем памяти, необходимый для хранения k элементов. Операции ADD не потребуют большего выделения памяти (возможно, дорого) и, следовательно, будут быстрее.

Jorge Ferreira 06.01.2009 12:24

@smink, я не совсем с тобой согласен. Процесс поиска в словаре смотрится в «ведре» на основе хэш-кода. Несколько записей могут предпочесть эту корзину, но ее получает тот, кто добавлен первым. Другие связаны цепочкой, что означает, что поиск этих других не так эффективен, как первый.

Drew Noakes 06.01.2009 12:54

@smink, кроме того, наличие большего начального размера словаря уменьшит количество хэш-коллизий и, следовательно, уменьшит среднюю длину цепочки, улучшив скорость поиска (хотя потенциально незначительно).

Drew Noakes 06.01.2009 12:55

Я провел быстрый тест, вероятно, не научный, но если я установил размер, потребовалось 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 мс.

Drew Noakes 05.01.2009 22:45

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

Drew Noakes 05.01.2009 22:47

То же самое при использовании System.Diagnostics.Stopwatch в отличие от DateTime.Now

Mitch Wheat 06.01.2009 12:47

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

Хеш-таблица с размером степени 2? Он хорошо работает?

mmx 05.01.2009 22:20

Для меня простые числа звучат лучше, чем степени двойки. Платформа .NET (mscorlib.dll v2.0.0.0) вызывает внутренний метод HashHelpers.GetPrime для нахождения следующего по величине простого числа после «емкости». Он ищет кеш простых чисел и выполняет поиск методом перебора, если его емкость превышает 7199369 :)

Drew Noakes 05.01.2009 22:53
Ответ принят как подходящий

То, чем вы должны инициализировать емкость словаря, зависит от двух факторов: (1) Распределение функции gethashcode и (2) Сколько элементов нужно вставить.

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

Если у вас есть 100 элементов для вставки в словарь, случайным образом распределенная хеш-функция, и вы устанавливаете емкость равной 100, то, когда вы вставляете i-й элемент в хеш-таблицу, у вас будет (i-1) / 100 вероятность того, что i-й элемент элемент будет сталкиваться с другим элементом при вставке. Если вы хотите снизить вероятность столкновения, увеличьте пропускную способность. Увеличение ожидаемой вместимости вдвое снижает вероятность столкновения вдвое.

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

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

rocketsarefast 26.06.2012 18:01

Есть ли требование, что физические хеш-ведра фактически выровнять для указанной емкости? Я бы предположил, что можно бесплатно выбрать подходящее количество сегментов, если оно соответствует «Емкость Dictionary <TKey, TValue> - это количество элементов, которые могут быть добавлены в Dictionary <TKey, TValue> перед изменением размера. . "

user166390 10.08.2012 23:29

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