Почему x86 JIT умнее, чем x64?

Я запускаю очень простую программу

    static void Main(string[] args)
    {
        Console.WriteLine(Get4S());
        Console.WriteLine(Get4());
    }

    private static int Get4S()
    {
        return 4;
    }

    private static int Get4()
    {
        int res = 0;
        for (int i = 0; i < 4; i++)
        {
            res++;
        }
        return res;
    }

когда он работает под x86, он встраивает метод Get4S, а ассемблерный код Get4:

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  xor         eax,eax 
00000005  inc         eax 
00000006  inc         eax 
00000007  inc         eax 
00000008  inc         eax 
00000009  pop         ebp 
0000000a  ret 

НО при работе под x64 мы получаем тот же asm для метода Get4S, но Get4 asm вообще не оптимизирован:

00000000  xor         eax,eax 
00000002  xor         edx,edx 
00000004  inc         eax 
00000006  inc         edx 
00000008  cmp         edx,4 
0000000b  jl          0000000000000004 
0000000d  ret 

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

Почему x64 такой глупый в данном случае?..


person Alex Zhukovskiy    schedule 08.04.2015    source источник
comment
Включены ли jit-оптимизации?   -  person CodesInChaos    schedule 08.04.2015
comment
@CodesInChaos, конечно, вы видите, что код x86 оптимизирован. Это не оптимально, но это развернуло цикл, это невозможно, когда jit-оптимизации подавлены.   -  person Alex Zhukovskiy    schedule 08.04.2015
comment
Не удалось воспроизвести. x64 JIT также оптимизирован для меня до константы 4. Код выглядел так: pastebin.com/UEtXUsLA   -  person harold    schedule 08.04.2015
comment
Я подозреваю, что джиттер x64 не получил много любви/внимания в первые дни .NET, поскольку тогда это было скорее исключением, чем правилом. Поскольку эта тенденция изменилась, некоторые из этих ограничений стали более очевидными, и Microsoft решает их с помощью RyuJIT (blogs.msdn.com/b/dotnet/archive/2013/09/30/)   -  person neil danson    schedule 08.04.2015
comment
@neil В моих тестах старый оптимизатор x64 создавал более быстрый код, чем ryujit.   -  person CodesInChaos    schedule 08.04.2015
comment
@neildanson Я разговаривал с MVP неделю назад, и у него было несколько доказательств того, что x64 JIT намного умнее, чем ruyjit, который также настолько обратно совместим, что имеет даже все ошибки x86 JIT. Например, смотрите первую-инструкцию-starg-bug (DevDiv Bugs 81184). Вот почему я был удивлен таким поведением   -  person Alex Zhukovskiy    schedule 08.04.2015
comment
x64 на самом деле также встраивает Get4 как 4 (не показано в коде, который я разместил ранее, потому что способ, которым я получил этот код, исключает встраивание функции). Так что это намного лучше, чем x86 JIT (как обычно)   -  person harold    schedule 08.04.2015
comment
@harold, в этом случае я не понимаю, почему он не оптимизировал его на моей машине. У вас есть такой же вывод для вашего x86?   -  person Alex Zhukovskiy    schedule 08.04.2015
comment
У меня есть такой же вывод для x86 (этот вывод действительно довольно глупый, он развернул 4 шага и даже не заметил? нет оптимизатора глазка??), Итак, какую процедуру вы следуете, чтобы получить этот вывод?   -  person harold    schedule 08.04.2015
comment
Точки останова на двух возвратах. Первая точка останова теперь работает, но она соблюдается из-за встраивания. Но обрывается секунда. И это странно, но это прерывается при условии i < 4, когда я устанавливаю точку останова при возврате (см. снимок экрана take.ms/HrF9a) . Я также обнаружил, что компиляция x64 намного медленнее: ~15 секунд против 1 секунды для x86.   -  person Alex Zhukovskiy    schedule 08.04.2015
comment
Иногда он путается в расположении точек останова в оптимизированном коде (на самом деле много). Как вообще заставить его сломаться в точке останова?   -  person harold    schedule 08.04.2015
comment
@гарольд извините? Также я имею в виду First breakpoint is not working, but it's expected, небольшая опечатка   -  person Alex Zhukovskiy    schedule 08.04.2015
comment
Ну, вы сняли флажок Подавить JIT-оптимизацию, верно? Если я это сделаю, ваша вторая точка останова тоже не сработает.   -  person harold    schedule 08.04.2015
comment
@harold да, странное поведение ... К сожалению, я не знаю источника этой проблемы и ее решения ... Кажется, это проблема среды.   -  person Alex Zhukovskiy    schedule 08.04.2015
comment
@CodesInChaos Интересно - я не пробовал RyuJit больше года - я не нашел большой разницы в своем коде, так что не стоило хлопот, но идея бесплатного ускорения была хороша :)   -  person neil danson    schedule 08.04.2015


Ответы (1)


Я понял. Это связано с тем, что RyuJIT используется, когда выбрана x64 сборка, даже если выбрана .Net 4.5.2 целевая платформа. Поэтому я исправил это, добавив этот раздел в файл App.Config:

<configuration>
  <runtime>
    <useLegacyJit enabled="1" />
  </runtime>
</configuration>

Эта разметка включает «устаревший» x64 JIT (в кавычках, потому что я думаю, что он намного лучше, чем «блестящий» RyuJIT), и результат ASM в основном методе:

00000000  sub         rsp,28h 
00000004  mov         ecx,4 
00000009  call        000000005EE75870 
0000000e  mov         ecx,4 
00000013  call        000000005EE75870 
00000018  nop 
00000019  add         rsp,28h 
0000001d  ret 

оба метода были рассчитаны во время компиляции и встроены в их значения.

Вывод: при установке .Net 4.6 старый джиттер x64 заменяется на RyuJIT для всех решений до CLR4.0. Таким образом, единственный способ отключить его - это useLegacyJit переключатель или COMPLUS_AltJit переменная среды.

person Alex Zhukovskiy    schedule 12.04.2015
comment
потому что я думаю, что он намного лучше, чем блестящий RyuJIT Закаленный в боях JIT работает лучше, чем неподдерживаемая предварительная версия: новости на 11. - person ta.speot.is; 13.04.2015
comment
@ ta.speot.is Я не говорю, что RyuJIT никогда не должен рождаться, я говорю, что Microsoft говорит всем, что у нас есть новый сверхбыстрый JITter, он вам понравится, но пока это неправда. В будущем - может быть. В любом случае, я не вижу причин, по которым RyuJIT решили основывать на джиттере x86, когда x64 был умнее во всех отношениях (за исключением этого редкого бага с кривым развертыванием цикла). - person Alex Zhukovskiy; 13.04.2015