- PVSM.RU - https://www.pvsm.ru -

Сломанный legacy JIT в .NET 4.6 Preview

Сломанный legacy JIT в .NET 4.6 Preview - 1Сегодня я расскажу вам об одной неприятной ситуации, которая связана с 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: в нём нет многих оптимизаций, а код зачастую исполняется дольше. Я проверил ключ регистра HKLMSOFTWAREMicrosoft.NETFrameworkAltJit и переменную среды COMPLUS_AltJit: всё было нормально, RyuJIT должен был выключен. Я проверил версию JIT своим любимым способом [1]: действительно, код обрабатывается с помощью RyuJIT. Тогда я полез в гугл и узнал кое-что новое [2]: при установке .NET Framework 4.6 Preview вы совершенно бесплатно получаете RyuJIT в качестве дефолта (на моём компьютере установка произошла вместе с Visual Studio 2015). При этом существует три способа вернуть всё на место:

  1. Установить переменную среды COMPLUS_useLegacyJit=1
  2. Установить ключ регистра useLegacyJit=1 (REG_WORD) в HKEY_LOCAL_MACHINESOFTWAREMicrosoft.NETFramework или HKEY_CURRENT_USERSOFTWAREMicrosoft.NETFramework
  3. В app.config-файле прописать волшебные строчки:
    <configuration>
      <runtime>
       <useLegacyJit enabled="1">
      </runtime>
    </configuration>
    

Ок, давайте попробуем. Действительно, способ рабочий, он возвращает старый JIT на место, но вот только при этом портит разные полезные штуки. Несколько примеров:

  1. Самое воспроизводимое: вызов GC.Collect() падает с исключением (чтобы всё было правильно не забудьте поставить Platform Target: x64, Target framework: .NET 4+).
    Сломанный legacy JIT в .NET 4.6 Preview - 2
  2. csc2 плюётся страшными кодами выхода.
    Сломанный legacy JIT в .NET 4.6 Preview - 3
  3. Постоянно вылетающий mscorlib recursive resource lookup bug.
    Сломанный legacy JIT в .NET 4.6 Preview - 4
  4. Многие приложения падают на старте (например, dotMemory, Paint.NET).
    Сломанный legacy JIT в .NET 4.6 Preview - 5
    Сломанный legacy JIT в .NET 4.6 Preview - 6
  5. А некоторые кидают исключения во время работы (например, GitExtensions).
    Сломанный legacy JIT в .NET 4.6 Preview - 7

В остальном при большом желании работать можно, но постоянные ошибки очень мешают. Пригорюнился я, да и выключил useLegacyJit. «Будем жить с новым RyuJIT, что поделать-то» — сказал я себе.

Но на этом мои проблемы не закончились. На днях я решил воспользоваться одной из .NET-утилит в проекте на своей работе. А она упала. Падение отлично воспроизводилось. Как порядочный разработчик, я пошёл и завёл баг в JIRA. Вскоре тикет был закрыт со статусом Cannot Reproduce: у всех всё работало, кроме меня. «Хм…» — подумал я. Пошёл в реестр, включил useLegacyJit, снова запустил утилиту — всё отработало на отличненько. Расследование бага всё ещё ведётся, но факт остаётся фактом: RyuJIT портил нормальную работу нашей замечательной утилитки.

И тогда я разозлился. «Да не так уж мне и нужен этот .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 утилитка всё ещё продолжает падать (обязательно разберусь в чём же там дело, увы, это не так просто).

Заключение

Мне всё ещё кажется, что я просто что-то не понимаю об этой жизни и что-то не так делаю. Я написал этот пост, т. к. мне кажется, что кто-нибудь ещё также может что-то не понять и наступить на те же грабли. Эксперименты проводились на моём рабочем ноутбуке и под виртуалками: мне этого недостаточно, чтобы объявить, что всё действительно плохо. Буду признателен всем, кто проверит ситуацию у себя на машине и отпишется о результатах. Если кто-то разбирается в ситуации лучше меня, то большая просьба рассказать кто виноват и что делать.

Ссылки

Автор: DreamWalker

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/c-2/88645

Ссылки в тексте:

[1] своим любимым способом: http://aakinshin.net/ru/blog/dotnet/jit-version-determining-in-runtime/

[2] кое-что новое: https://github.com/Microsoft/dotnet/blob/master/docs/testing-with-ryujit.md

[3] Microsoft .NET Framework 4.6 Preview (Web Installer): http://www.microsoft.com/en-us/download/details.aspx?id=44928

[4] Visual Studio 2015 CTPs: https://www.visualstudio.com/en-us/downloads/visual-studio-2015-ctp-vs.aspx

[5] Источник: http://habrahabr.ru/post/255567/