Warum wirkt sich der JIT-Auftrag auf die Leistung aus?

Warum wirkt sich die Reihenfolge, in der C # -Methoden in .NET 4.0 just-in-time kompiliert werden, darauf aus, wie schnell sie ausgeführt werden? Betrachten Sie beispielsweise zwei gleichwertige Methoden:

<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>

Der einzige Unterschied besteht in der Einführung einer lokalen Variablen, die sich auf den generierten Assemblycode und die Schleifenleistung auswirkt. Warum das so ist, ist aFrage an sich.

Möglicherweise ist es noch seltsamer, dass die Reihenfolge, in der die Methoden aufgerufen werden, auf x86 (aber nicht auf x64) einen Einfluss von etwa 20% auf die Leistung hat. Rufen Sie die Methoden wie folgt auf ...

<code>static void Main()
{
    SingleLineTest();
    MultiLineTest();
}
</code>

...undSingleLineTest ist schneller. (Kompilieren Sie mit der x86-Release-Konfiguration und stellen Sie dabei sicher, dass die Einstellung "Code optimieren" aktiviert ist, und führen Sie den Test von außerhalb von VS2010 aus.) Kehren Sie jedoch die Reihenfolge um.

<code>static void Main()
{
    MultiLineTest();
    SingleLineTest();
}
</code>

... und beide Methoden nehmen die gleiche Zeit in Anspruch (fast, aber nicht ganz so lange wieMultiLineTest Vor). (Wenn Sie diesen Test ausführen, ist es hilfreich, einige zusätzliche Aufrufe hinzuzufügenSingleLineTest undMultiLineTest zusätzliche Proben zu bekommen. Wie viele und welche Reihenfolge spielt keine Rolle, außer welche Methode zuerst aufgerufen wird.)

Um zu demonstrieren, dass die JIT-Reihenfolge wichtig ist, verlassen Sie das ProgrammMultiLineTest Erstens, aber KraftSingleLineTest zuerst JITed werden ...

<code>static void Main()
{
    RuntimeHelpers.PrepareMethod(typeof(Program).GetMethod("SingleLineTest").MethodHandle);
    MultiLineTest();
    SingleLineTest();
}
</code>

Jetzt,SingleLineTest ist wieder schneller.

Wenn Sie in VS2010 "JIT-Optimierung bei Modullast unterdrücken" deaktivieren, können Sie einen Haltepunkt einfügenSingleLineTest und stellen Sie sicher, dass der Assemblycode in der Schleife unabhängig von der JIT-Reihenfolge identisch ist. Der Assemblycode zu Beginn der Methode ist jedoch unterschiedlich. Aber wie wichtig das ist, wenn der Großteil der Zeit in der Schleife verbracht wird, ist verwirrend.

A Beispielprojekt, das dieses Verhalten demonstriert ist auf Github.

Es ist nicht klar, wie sich dieses Verhalten auf reale Anwendungen auswirkt. Eine Sorge ist, dass es die Leistungsoptimierung volatil machen kann, abhängig davon, welche Bestellmethoden zuerst aufgerufen werden. Probleme dieser Art sind mit einem Profiler nur schwer zu erkennen. Sobald Sie die Hotspots gefunden und ihre Algorithmen optimiert haben, ist es schwer zu erraten, ob eine zusätzliche Beschleunigung durch JIT-Methoden möglich ist.

Aktualisieren: Siehe auch dieMicrosoft Connect-Eintrag für diese Ausgabe.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage