C# Исполнение словаря: Строка по умолчанию GetHashCode() Comparer's GetHashCode() выделяет память с нарушением правил, тем самым снижая производительность?

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

Но именно эту неудачу я вижу в профиле своего приложения, использующего System.Collections.Generic.Dictionary

В очень узком цикле я нахожу следующее в результатах своего профайлера:

.

  • [3,47%] TryGetValue(TKey, TValue&) (...словарь)
    • [3,47%] FindEntry(TKey) (...словарь)
      • [3.47%] GetHashCode(string) (System.CultureAwareComparer)
        • [3.46%] GetHashCodeOfString(String, CompareOptions) (System.Globalization.CompareInfo)
          • [3,39%] [Уборка мусора]
          • [0.01%] [Thread Suspend]

Это весь учет поддеревьев из профайлера.

Я не опытный эксперт в этой специфической работе, так что я мог бы неправильно прочитать эти чайные листья. Но мне кажется, что GetHashCodeOfString "должен быть" выделяет память и приглашает сборщика мусора прервать мою программу в середине этого цикла, я хочу REALLY TUNED AND TIGHT, и это составляет ошеломляющую большую часть стоимости этого цикла.

В качестве дополнительной информации вот еще одно доказательство того, что этот код выделяет память

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

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

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

Конкретные вопросы, которые у меня есть:

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

EDIT: Crud, моя вина, но это не со свойствами компаратора по умолчанию, у нас есть настройка игнорирования кейса. Не уверен, повлияет ли это на результаты, но так как ignoreCase повлияет на равенство, то он должен иметь некоторое влияние на хэш.

UPDATE: Пробежал еще один тест, используя порядковый сравнитель (все еще с IgnoreCase), и переписал исходные результаты вывода на 100% стоимость = TryGetValue, чтобы было больше яблок к яблокам

Original:

  • 100% TryGetValue.
    • 100% FindEntry
      • 99.5% КультураПрограммное обеспечениеКомпаратор.GetHashCode
        • 99.5% СравнитеInfo.GetHashCodeOfString
          • 95,86% [Сбор мусора]
          • 3,31% [Резьба приостановлена]
      • 0,5% CultureAwareComparer.Equals
        • 0.5% Сравните
          • 0,5% [сбор мусора]

Порядок:

  • 100% TryGetValue
    • 100% FindEntry
      • 47.22% КультураПрограммаСравнитель.Равноценные
        • 47,22% [Сбор мусора]

Также оказалось, что в TryGetValue резко сократилось общее время, затрачиваемое на сбор мусора. Я не удостоверился в том, что все остальное равно, но это составило 46 секунд из 10 минутного стресс-теста в первом прогоне, а в орнидальном - 252 миллисекунды. Считайте, что анекдотическая, а не ожидаемая относительная стоимость.

Кажется, что вся стоимость хэша, которая раньше составляла 99+% от стоимости, теперь настолько "бесплатна", что она даже не появляется в профилировщике, который, как мне кажется, работает в режиме дискретизации.

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

Я все еще не могу объяснить себе, почему стоимость GC так сильно влияет на результат первого профиля, но из приведенных ниже комментариев я полагаю, что она НЕ выделяет управляемую кучу памяти, а потому что она медленная, то это, как правило, функция, которая "случайным образом" GCed другими действиями на других потоках, так как этот процесс действительно использует режим сервера gc.

Может быть, это указывает на то, что этот узкий цикл имеет тенденцию быть параллельным с кодом allocation-happy где-то в другом месте.

16
задан rice 30 August 2011 в 22:42
поделиться