Сборка, которую вы разместили, показывает, что комментарий mjwills, как и ожидалось, правильный.Как отмечает связанная статья, джиттер может быть осторожен в определенных сравнениях, и это одно из них.
Давайте рассмотрим ваш первый фрагмент:
mov rcx,offset mscorlib_ni+0x729e10
rcx - это указатель this«вызова функции-члена.«Указатель this» в этом случае будет адресом предварительно выделенного объекта CLR, чего я точно не знаю.
call clr!InstallCustomModule+0x2320
Теперь мы вызываем некоторую функцию-член для этого объекта;Я не знаю что. ближайшая публичная функция, для которой у вас есть отладочная информация, - это InstallCustomModule, но, очевидно, мы здесь не вызываем InstallCustomModule;мы вызываем функцию, которая находится в 0x2320 байтах от InstallCustomModule.
Было бы интересно посмотреть, что делает код в InstallCustomModule + 0x2320.
В любом случае, мы делаем вызов, ивозвращаемое значение идет в rax.Двигаемся дальше:
mov rcx,qword ptr [rsp+30h]
cmp qword ptr [rcx+8],rax
Похоже, что он извлекает значение a
из this
и сравнивает его с тем, что вернула функция.
Остальная часть кодапросто совершенно обычное: перемещение результата bool сравнения в регистр возврата.
Короче говоря, первый фрагмент эквивалентен:
return ReferenceEquals(SomeConstantObject.SomeUnknownFunction(), this.a);
Очевидно, что здесь прослеживается догадка о том, что константаобъект и неизвестная функция являются специальными помощниками, которые быстро выбирают объекты часто используемых типов, такие как typeof (int).
Второе обоснованное предположение состоит в том, что джиттер сам для себя решает, что шаблон "сравнивает полеtype Type to typeof (что-то) "лучше всего сделать как прямое сравнение ссылок между объектами.
И теперь вы можете сами убедиться, что делает второй фрагмент.Это просто:
return Type.op_Equality(this.a, this.b);
Все, что он делает, это вызывает вспомогательный метод, который сравнивает два типа для равенства значений.Помните, CLR не гарантирует равенство ссылок для всех объектов эквивалентного типа .
Теперь должно быть понятно, почему первый фрагмент быстрее. Джиттер знает гораздо больше о первом фрагменте .Он знает, например, что typeof (int) всегда будет возвращать одну и ту же ссылку, и поэтому вы можете сделать дешевое сравнение ссылок.Он знает, что typeof (int) никогда не бывает нулевым.Он знает точный тип typeof (int) - помните, Type
не запечатан;Вы можете создавать свои собственные Type
объекты.
Во втором фрагменте джиттер не знает ничего, кроме того, что у него есть два операнда типа Type
.Он не знает их типов во время выполнения, он не знает их недействительности;Насколько он знает, вы сами подклассифицировали Type
и создали два экземпляра, которые являются неравными по ссылкам, но равными по стоимости.Он должен вернуться к наиболее консервативной позиции и вызвать вспомогательный метод, который начинает идти вниз по списку: оба они равны нулю?Является ли одно из нулевого, а другой ненулевым?равны ли они?И так далее.
Похоже, отсутствие знаний обходится вам в огромные штрафы ... полсекунды.Я бы не волновался об этом.