¿Por qué el orden en que los métodos de C# en .NET 4.0 están compilados justo a tiempo afecta la rapidez con que se ejecutan? Por ejemplo, consideremos dos métodos equivalentes:¿Por qué el orden de JIT afecta el rendimiento?
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);
}
La única diferencia es la introducción de una variable local, que afecta el código ensamblador generado y el rendimiento de bucle. Por qué ese es el caso es un question in its own right.
Posiblemente aún más extraño es que en x86 (pero no x64), el orden en que se invocan los métodos tiene un impacto de alrededor del 20% en el rendimiento. Invocar los métodos como este ...
static void Main()
{
SingleLineTest();
MultiLineTest();
}
... SingleLineTest
y es más rápido. (Compilar utilizando la configuración de lanzamiento 86, asegurando que "Optimizar código" opción está habilitada, y ejecute la prueba de VS2010 exterior.) Sin embargo, invertir el orden ...
static void Main()
{
MultiLineTest();
SingleLineTest();
}
... y ambos métodos tienen el mismo tiempo (casi, pero no del todo, siempre y cuando MultiLineTest
antes). (Al ejecutar esta prueba, es útil agregar algunas llamadas adicionales al SingleLineTest
y MultiLineTest
para obtener muestras adicionales. Cuantos y qué orden no importa, excepto por qué método se llama primero.)
Por último, para demostrar que para JIT es importante, dejar MultiLineTest
principio, pero la fuerza que se SingleLineTest
JITed primera ...
static void Main()
{
RuntimeHelpers.PrepareMethod(typeof(Program).GetMethod("SingleLineTest").MethodHandle);
MultiLineTest();
SingleLineTest();
}
Ahora, SingleLineTest
es más rápido de nuevo.
Si desactiva "Suprimir la optimización de JIT en la carga del módulo" en VS2010, puede poner un punto de interrupción en SingleLineTest
y ver que el código de ensamblaje en el bucle sea el mismo independientemente del orden de JIT; sin embargo, el código de ensamblaje al comienzo del método varía. Pero cómo esto importa cuando se pasa la mayor parte del tiempo en el circuito es desconcertante.
A sample project demonstrating this behavior está en github.
No está claro cómo este comportamiento afecta a las aplicaciones del mundo real. Una preocupación es que puede hacer que el ajuste del rendimiento sea volátil, dependiendo de la orden en que los métodos se llamen primero. Los problemas de este tipo serían difíciles de detectar con un generador de perfiles. Una vez que encontraste los puntos de acceso y optimizaste sus algoritmos, sería difícil saberlo sin mucha conjetura y comprobar si es posible una aceleración adicional mediante métodos JITing.
Actualización: Consulte también el Microsoft Connect entry para este problema.
"... tiene alrededor de un 20% de impacto en el rendimiento" tengo alrededor de 8% –
¿Es la alineación (de las instrucciones reales) la misma? O estar JITted siguiendo el otro método, o el cambio menor en el preámbulo, realmente podría atornillar con la alineación. –
@lukas En dos computadoras, cada una con Intel Core i5s, obtuve un promedio de 1412ms y 1490ms para 'SingleLineTest' y 1773ms y 1792ms para' MultiLineTest'. Esto funciona con aceleraciones de 26% y 20%. Para cada computadora, la desviación estándar para la aceleración llegó al 2%. Hubiera esperado ver alguna diferencia entre las máquinas, pero el 8% es sorprendente. –