Оптимизирует ли JIT-компилятор С# проверку на ноль?

В Интернете есть много статей, в которых перечислены оптимизации, сделанные C# JIT перед выполнением фрагмента кода. Например, в этом сообщении на MSDN говорится о:

Сворачивание констант, Распространение констант и копий, Устранение общих подвыражений, Перемещение кода инвариантов цикла, Мертвое хранилище и устранение мертвого кода, Распределение регистров, Встраивание методов, Развертывание циклов (небольшие циклы с небольшими телами).

Мой вопрос: компилятор JIT также имеет дело с бесполезными нулевыми проверками? Я не могу найти источник, рассматривающий этот вопрос.

В той же статье я прочитал:

поскольку спецификация языка C# гарантирует, что любой вызов нулевой ссылки на объект вызывает исключение NullReferenceException, каждый сайт вызова должен гарантировать, что экземпляр не является нулевым. Это делается путем разыменования ссылки на экземпляр; если он равен нулю, он сгенерирует ошибку, которая превратится в это исключение.

Итак, предположим, я пишу такой фрагмент кода:

if (person != null)
{
    Console.WriteLine(person.Name);
}

person.Name снова вызывает вторую нулевую проверку, которая явно бесполезна, и компилятор может ее удалить. Или не?

Я читал, что в Java это уже сделано (некоторые источники между многими здесь, здесь и здесь).

Если это делает и C#, знаете ли вы какой-нибудь источник или документацию, в которой говорится об этом?

Если вместо этого C# этого не делает, знаете почему? Есть ли внутренняя трудность в реализации такой функции для среды .NET, с которой Java JIT не сталкивается?


person Massimiliano Kraus    schedule 06.12.2016    source источник
comment
Только как побочный эффект обычных оптимизаций, таких как устранение мертвого кода. Что должно быть довольно редко. Как правило, оптимизатор джиттера не имеет специальных знаний о коде, выполняющем проверку нулевого указателя, и рассматривает указатели как неустойчивые. Это очень важно для любой виртуальной машины со сборщиком мусора, который произвольно изменяет указатели при сжатии кучи. Примечательно, что сам компилятор C# может устранить проверку, когда вы используете оператор elvis (?.).   -  person Hans Passant    schedule 07.12.2016


Ответы (1)


Null проверьте оптимизацию, выполненную компилятором (Roslyn, а не Jitter) в некоторых случаях, когда это можно сделать полностью.

Например, когда вы используете ? (оператор Элвиса).

IL_0006: stloc.0              // Pop a value from stack into local variable 0
IL_0007: ldloc.0              // Load local variable 0 onto stack
IL_0008: brtrue.s IL_000c     // Branch to target if value is non-zero (true), short form
IL_000a: br.s IL_0013         // Branch to target, short form
IL_000c: ldloc.0              // Load local variable 0 onto stack
IL_000d: call instance void Foo::Bar() // Call method indicated on the stack with arguments

Другой пример — такой код:

new Bar().Foo();

Компилятор генерирует для этой инструкции call, а не callvirt (что означает отсутствие проверки на нулевое значение для this)

В других случаях вы не можете быть уверены, что this не будет нулевым.

Во всяком случае, нулевая проверка действительно очень быстрая.

person Dudi Keleti    schedule 06.12.2016