Dlaczego zamówienie JIT wpływa na wydajność?
Dlaczego kolejność, w jakiej metody C # w .NET 4.0 są kompilowane na czas, wpływa na szybkość ich wykonywania? Na przykład rozważ dwie równoważne metody:
<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>
Jedyną różnicą jest wprowadzenie zmiennej lokalnej, która wpływa na wygenerowany kod zespołu i wydajność pętli. Dlaczego tak jestpytanie samo w sobie.
Być może nawet dziwniejsze jest to, że na x86 (ale nie na x64) kolejność wywoływania metod ma około 20% wpływ na wydajność. Wywołaj takie metody ...
<code>static void Main() { SingleLineTest(); MultiLineTest(); } </code>
...iSingleLineTest
jest szybszy. (Skompiluj za pomocą konfiguracji wydania x86, upewniając się, że ustawienie „Optymalizuj kod” jest włączone i uruchom test z zewnątrz VS2010.) Ale odwróć kolejność ...
<code>static void Main() { MultiLineTest(); SingleLineTest(); } </code>
... i obie metody zajmują ten sam czas (prawie, ale nie całkiem, tak długo jakMultiLineTest
przed). (Podczas uruchamiania tego testu warto dodać dodatkowe połączenia doSingleLineTest
iMultiLineTest
uzyskać dodatkowe próbki. Ile i jaka kolejność nie ma znaczenia, z wyjątkiem tego, która metoda nazywa się najpierw.)
Wreszcie, aby pokazać, że kolejność JIT jest ważna, odejdźMultiLineTest
po pierwsze, ale siłaSingleLineTest
być najpierw JITem ...
<code>static void Main() { RuntimeHelpers.PrepareMethod(typeof(Program).GetMethod("SingleLineTest").MethodHandle); MultiLineTest(); SingleLineTest(); } </code>
Teraz,SingleLineTest
jest jeszcze szybszy.
Jeśli wyłączysz „Pomiń optymalizację JIT na obciążeniu modułu” w VS2010, możesz umieścić punkt przerwania wSingleLineTest
i zobacz, że kod zespołu w pętli jest taki sam bez względu na kolejność JIT; jednakże kod zespołu na początku metody jest różny. Ale to, jak to ma znaczenie, gdy większość czasu spędza w pętli, jest kłopotliwe.
A przykładowy projekt demonstrujący to zachowanie jest na githubie.
Nie jest jasne, jak to zachowanie wpływa na rzeczywiste aplikacje. Jedną z obaw jest to, że może sprawić, że dostrajanie wydajności stanie się niestabilne, w zależności od tego, jakie metody kolejności zostaną wywołane. Problemy tego rodzaju byłyby trudne do wykrycia za pomocą profilera. Po znalezieniu hotspotów i zoptymalizowaniu ich algorytmów, trudno byłoby się domyślić bez większego zgadywania i sprawdzić, czy wczesne przyspieszenie jest możliwe dzięki metodom JIT.
Aktualizacja: Zobacz takżeWpis Microsoft Connect dla tego problemu.