.NET 4.0 Dictionary‹TKey,TValue›: ключ типа значения в рамке для бесполезной проверки «нулевого значения». Влияет ли это на производительность?

Например:

.method private hidebysig instance void Insert(!TKey key, !TValue 'value', bool add) cil managed
{
    .maxstack 3
    .locals init (
        [0] int32 num,
        [1] int32 num2,
        [2] int32 num3,
        [3] int32 num4)
    L_0000: ldarg.1 
    L_0001: box !TKey
    L_0006: brtrue.s L_000e
    L_0008: ldc.i4.5 
    L_0009: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument)

Это из внутреннего метода Add Dictionary‹int,Object› в .NET 4.0. Хотя дженерики широко рекламируются как помогающие избежать упаковки типов значений, почему этот системный компонент выполняет эту неэффективную проверку каждой операции для ключа типа значения? Если я правильно понимаю, это не только снижает производительность, но и всегда возвращает true (тип значения в штучной упаковке никогда не будет нулевой ссылкой)

редактировать: Резюме ответа Марка на этот конкретный вопрос: причина, по которой это важно, заключается в том, что эта реализация Dictionary‹K,V› решила запретить использование «нулевых» экземпляров Nullable‹T› в качестве ключей. Поскольку инструкция поля MSIL специально обрабатывает тип значения Nullable‹T›, проверка не обязательно бесполезна для всех типов значений.


person Glenn Slayden    schedule 03.12.2010    source источник
comment
Всегда проверяйте сгенерированный x86 asm, прежде чем говорить о том, что код делает ненужные вещи. Джиттер часто оптимизирует бесполезный код. И сделать это, запустив программу выпуска без отладчика, а затем подключившись к работающей программе.   -  person CodesInChaos    schedule 03.12.2010


Ответы (1)


Nullable<T> — это struct / тип значения и может быть null (в зависимости от вашего определения null, но, конечно, он может упаковывать в null). И не все TKey имеют тип значения (string, пожалуй, самый распространенный TKey).

Здесь требуется, чтобы ключ не был нулевым; так что это нужно проверить.

На самом деле бокс не так плох, как думают люди; даже в коробке, это будет gen-0 собранный. Он может использовать особый случай с помощью обобщений (как это делает EqualityComparer<T> — с помощью нескольких различных подклассов), но это кажется излишним.

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

person Marc Gravell    schedule 03.12.2010
comment
Спасибо за ответ Марк. Я не уверен, почему Nullable‹T› здесь уместен. Вопрос касается ключа типа значения, который не может быть нулевым. Ваша точка зрения о gen-0 хорошо принята, но я приложил все усилия, чтобы быть как можно более свободным от распределения в моих вычислительных циклах. Я предполагаю, что автор универсального типа мог бы помочь JITter, кэшируя результат typeof(TKey).IsValueType в конструкторе и пропуская проверку нуля. - person Glenn Slayden; 03.12.2010
comment
@Glenn - это актуально, потому что нужно проверять на ноль. И в соответствии с существующим значением словаря ключ int? может заставить его выбрать исключение. Конечно, с предложенной вами поправкой это просто checkForNull = !type.IsValueType || Nullable.GetUnderlyingType(type) != null; - person Marc Gravell; 03.12.2010
comment
Я не думаю, что потребуется дополнительная проверка для Nullable. Разве Nullable‹T› не является просто ссылочным типом (самим по себе), так что typeof(Nullable‹T›).IsValueType всегда равен false? - person Glenn Slayden; 03.12.2010
comment
@Гленн - нет; Nullable<T> это struct - person Marc Gravell; 03.12.2010
comment
Вау, интересно, так это означает, что Nullable‹T› имеет специальную обработку во время выполнения по отношению к инструкции box? - person Glenn Slayden; 03.12.2010
comment
@Гленн; да, именно так. Также компилятор (сопоставление нулевых проверок с HasValue, когда это возможно, и т. д.) - person Marc Gravell; 03.12.2010
comment
@Glenn и другие любопытные побочные эффекты этого см. Мой ответ с наибольшим количеством голосов на странице моего профиля пользователя. - person Marc Gravell; 03.12.2010
comment
Последнее замечание: я вижу, что статический Nullable‹T›.Equals(object, object) блокирует объект, а статический Nullable.Equals(Nullable‹T›, Nullable‹T›) — нет! Я думаю, что буду держаться подальше от них и просто рассчитываю на '== null', чтобы получить специальную обработку обнаружения нуля в универсальном универсальном типе с гибким значением. - person Glenn Slayden; 04.12.2010
comment
@Glenn первый - это просто object.Equals, отображаемый через наследование. == работает корректно, через поднятую поддержку оператора. - person Marc Gravell; 04.12.2010