Сломанный legacy JIT в .NET 4.6
Сегодня я расскажу вам об одной неприятной ситуации, которая связана с JIT в .NET 4.6. Вот несколько фактов:
Если вы поставили .NET Framework 4.6 Preview, то у вас по дефолту стоит RyuJIT.
Если вы поставили Visual Studio 2015 CTP, то она включает .NET Framework 4.6 Preview, а значит у вас по дефолту стоит RyuJIT.
Если вы работаете на Windows 10 Tech Preview, то она также включает.NET Framework 4.6 Preview, а значит у вас по дефолту стоит RyuJIT.
Если вы хотите вернуть старый JIT, то это можно сделать с помощью ключа регистра, переменной среды или app.config-настройки useLegacyJit.
Если включить useLegacyJit в Windows 8 или Windows 8.1, то вы получите большое количество проблем, связанных с компиляцией и запуском приложений.
RyuJIT всё ещё не готов к реальному использованию, некоторые программы могут работать некорректно на нём.
Возможно, я просто что-то не понимаю в этой жизни. Давайте разберёмся в ситуации вместе.Обнаружение проблемыНе так давно я занимался исследованием особенностей различных версий JIT в .NET. В какой-то момент я обратил внимание на то, что ASM-код для 64-битной версии программы не похож на старый JIT-x64. Вместо этого он был похож на результат работы RyuJIT. Я не очень люблю RyuJIT: в нём нет многих оптимизаций, а код зачастую исполняется дольше. Я проверил ключ регистра HKLM\SOFTWARE\Microsoft\.NETFramework\AltJit и переменную среды COMPLUS_AltJit: всё было нормально, RyuJIT должен был выключен. Я проверил версию JIT своим любимым способом: действительно, код обрабатывается с помощью RyuJIT. Тогда я полез в гугл и узнал кое-что новое: при установке .NET Framework 4.6 Preview вы совершенно бесплатно получаете RyuJIT в качестве дефолта (на моём компьютере установка произошла вместе с Visual Studio 2015). При этом существует три способа вернуть всё на место:
Установить переменную среды COMPLUS_useLegacyJit=1
Установить ключ регистра useLegacyJit=1 (REG_WORD) в HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework или HKEY_CURRENT_USER\SOFTWARE\Microsoft\.NETFramework
В app.config-файле прописать волшебные строчки:
И тогда я разозлился. «Да не так уж мне и нужен этот .NET 4.6» — подумал я. И снёс его вместе с Visual Studio 2015. Проверяю: RyuJIT на месте. Я пошёл в установку и удаление программ и снёс всё, что шло вместе с .NET 4.6. Проверяю: RyuJIT на месте. Тогда я совсем разозлился: начал сносить вообще всё, что в своём названии содержало «Microsoft» или ».NET». Проверяю: RyuJIT на месте. Я пошёл на диск C: и начал руками удалять всё, посвящённое .NET, что вообще удалялось. До конца мне .NET снести не удалось, поэтому я прошёлся по системным настройкам и повыключал все выключаемые Windows-компоненты, связанные с .NET. После всех процедур делаю последнюю проверку: RyuJIT на месте.
Воспроизведение проблемы Жить на руинах системы было не очень весело, поставить все .NET-штуки заново оказалось затруднительно, поэтому я начал процесс восстановления системы. Откатился к заводским настройкам, поставил всё программы, настроил рабочее окружение и кинулся проверять работу GC.Collect (): старый добрый Jit-x64 на месте, сборка мусора работает нормально. Я уже предвидел результат, но экспериментатор в моей душе заставил меня поставить Visual Studio 2015 (включая .NET 4.6 и дефолтный RyuJIT). Прогнал все свои тесты, результат не поменялся: моя .NET-утилитка не работала под RyuJIT, а useLegacyJit кидался ошибками на каждый мой шаг.«Может у меня Windows какой-то дефектный» — подумал я. Переходим к следующему этапу: я нашёл в интернете образы чистых Windows 8 и Windows 8.1, поставил их под виртуалкой. На новенькие системы мной была установлена Visual Studio 2015 CTP6 Ultimate (минимальная конфигурация, без всяких дополнительных опций). Проводим эксперимент: собираем консольное x64-приложение с единственным вызовом GC.Collect (), включаем useLegacyJIT, запускаем и видим, как приложение плюётся исключением прямо нам в лицо.
Далее я поставил под виртуалкой Window 10 Tech Preview. Меня ждало ещё одно открытие: там .NET 4.6 встроенный, RyuJIT сразу идёт по дефолту. Порадовало одно: опция useLegacyJit работает нормально, никаких исключений не наблюдается. А вот без неё на дефолтном RyuJIT моя .NET утилитка всё ещё продолжает падать (обязательно разберусь в чём же там дело, увы, это не так просто).
Заключение Мне всё ещё кажется, что я просто что-то не понимаю об этой жизни и что-то не так делаю. Я написал этот пост, т. к. мне кажется, что кто-нибудь ещё также может что-то не понять и наступить на те же грабли. Эксперименты проводились на моём рабочем ноутбуке и под виртуалками: мне этого недостаточно, чтобы объявить, что всё действительно плохо. Буду признателен всем, кто проверит ситуацию у себя на машине и отпишется о результатах. Если кто-то разбирается в ситуации лучше меня, то большая просьба рассказать кто виноват и что делать.Ссылки