Por que o pedido do JIT afeta o desempenho?
Por que a ordem em que os métodos C # no .NET 4.0 são compilados just-in-time afetam a rapidez com que são executados? Por exemplo, considere dois métodos equivalentes:
<code>public static void SingleLineTest() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); int count = 0; for (uint i = 0; i < 1000000000; ++i) { count += i % 16 == 0 ? 1 : 0; } stopwatch.Stop(); Console.WriteLine("Single-line test --> Count: {0}, Time: {1}", count, stopwatch.ElapsedMilliseconds); } public static void MultiLineTest() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); int count = 0; for (uint i = 0; i < 1000000000; ++i) { var isMultipleOf16 = i % 16 == 0; count += isMultipleOf16 ? 1 : 0; } stopwatch.Stop(); Console.WriteLine("Multi-line test --> Count: {0}, Time: {1}", count, stopwatch.ElapsedMilliseconds); } </code>
A única diferença é a introdução de uma variável local, que afeta o código de montagem gerado e o desempenho do loop. Por que esse é o caso é umpergunta em seu próprio direito.
Possivelmente ainda mais estranho é que em x86 (mas não em x64), a ordem em que os métodos são invocados tem um impacto de cerca de 20% no desempenho. Invoque os métodos assim ...
<code>static void Main() { SingleLineTest(); MultiLineTest(); } </code>
...eSingleLineTest
é mais rápido. (Compile usando a configuração de Liberação x86, assegurando que a configuração "Otimizar código" esteja ativada e execute o teste fora do VS2010.) Mas inverta a ordem ...
<code>static void Main() { MultiLineTest(); SingleLineTest(); } </code>
... e ambos os métodos tomam o mesmo tempo (quase, mas não tanto, contanto queMultiLineTest
antes). (Ao executar este teste, é útil adicionar algumas chamadas adicionais paraSingleLineTest
eMultiLineTest
para obter amostras adicionais. Quantos e qual ordem não importa, exceto para qual método é chamado primeiro.)
Finalmente, para demonstrar que a ordem JIT é importante, deixeMultiLineTest
primeiro, mas forçaSingleLineTest
ser JITed primeiro ...
<code>static void Main() { RuntimeHelpers.PrepareMethod(typeof(Program).GetMethod("SingleLineTest").MethodHandle); MultiLineTest(); SingleLineTest(); } </code>
Agora,SingleLineTest
é mais rápido novamente.
Se você desligar "Suprimir otimização JIT no carregamento do módulo" no VS2010, você pode colocar um ponto de interrupçãoSingleLineTest
e ver que o código de montagem no loop é o mesmo, independentemente da ordem JIT; no entanto, o código de assembly no início do método varia. Mas como isso é importante quando a maior parte do tempo é gasto no circuito é desconcertante.
A projeto de amostra demonstrando esse comportamento está no github.
Não está claro como esse comportamento afeta os aplicativos do mundo real. Uma preocupação é que ela pode tornar o ajuste de desempenho volátil, dependendo dos métodos de pedido que foram chamados pela primeira vez. Problemas desse tipo seriam difíceis de detectar com um profiler. Uma vez que você encontrou os hotspots e otimizou seus algoritmos, seria difícil saber sem muita suposição e verificar se a aceleração adicional é possível pelos métodos JITing antecipadamente.
Atualizar: Veja também oEntrada do Microsoft Connect para este problema.