2017-08-29 35 views
10

Kod:Jaki jest powód tak różnych czasów trwania samych bloków kodu?

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     const int iterCount = 999999999; 

     var sum1 = 0; 
     var sum2 = 0; 

     using (new Dis()) 
     { 
      var sw = DateTime.Now; 
      for (var i = 0; i < iterCount; i++) 
       sum1 += i; 
      Console.WriteLine(sum1); 
      Console.WriteLine(DateTime.Now - sw); 
     } 

     using (new Dis()) 
     { 
      var sw = DateTime.Now; 
      for (var i = 0; i < iterCount; i++) 
       sum2 += i; 
      Console.WriteLine(sum2); 
      Console.WriteLine(DateTime.Now - sw); 
     } 

     Console.ReadLine(); 
    } 

    private class Dis : IDisposable 
    { 
     public void Dispose(){} 
    } 
} 

Dwa identyczne bloki w identycznych usings.

wyjściowa:

2051657985 
00:00:00.3690996 
2051657985 
00:00:02.2640266 

Drugi blok trwa 2,2 sekundy! Ale jeśli chcemy pozbyć się użytków, czas trwania stał się taki sam (~ 0.3s, jak pierwszy). Próbowałem już z .NET framework 4.5 i .net core 1.1, w wersji, wyniki są takie same.

Czy ktoś może wyjaśnić to zachowanie?

+2

Może spróbuj użyć zmiennych zamiast "Console.WriteLine"?To może być problem. – ispiro

+0

Po uruchomieniu kodu w linqpad daje mi to około 2,5 sekundy dla obu bloków kodu. Po uruchomieniu na ideone daje ~ 0,3 sekundy dla obu bloków: https://ideone.com/ReOpaH. – Chris

+2

Używaj również właściwego stopera. Nazywasz zmienną sw datetime, co oznacza, że ​​chcesz używać stopera, ale nie używasz go. – Chris

Odpowiedz

13

Musisz spojrzeć na kod maszynowy generowany przez jitter, aby zobaczyć przyczynę. Użyj opcji Narzędzia> Opcje> Usuwanie błędów> Ogólne> usuń zaznaczenie opcji Zablokuj optymalizację JIT. Przejdź do wersji Release. Ustaw punkt przerwania na pierwszej i drugiej pętli. Po trafieniu użyj opcji Debuguj> Windows> Demontaż.

Zobaczysz kod maszynowy dla organów pętli for:

    sum1 += i; 
00000035 add   esi,eax 

oraz:

    sum2 += i; 
000000d9 add   dword ptr [ebp-24h],eax 

Lub innymi słowy, zmienna sum1 jest przechowywana w rejestrze procesora esi. Ale zmienna sum2 jest przechowywana w pamięci na ramce stosu metody. Wielka, wielka różnica. Rejestry są bardzo szybkie, pamięć wolna. Pamięć dla ramki stosu będzie znajdować się w pamięci podręcznej L1, na nowoczesnych komputerach uzyskujących dostęp do pamięci podręcznej ma opóźnienie 3 cykli. Bufor sklepu zostanie szybko przytłoczony dużą liczbą zapisów i spowoduje zatrzymanie procesora.

Znaleźć sposób na zachowanie zmiennych w rejestrach procesorów to one of the primary jitter optimization duties. Ma to jednak ograniczenia, w szczególności w x86 dostępnych jest niewiele rejestrów. Kiedy wszystkie są zużyte, jitter nie ma opcji, ale zamiast tego korzysta z pamięci. Zauważ, że instrukcja using ma dodatkową ukrytą zmienną lokalną pod maską, dlatego też przyniosła efekt.

Idealnie dla optymalizatora fluktuacji dokonałby lepszego wyboru sposobu przydzielania rejestrów. Używanie ich do zmiennych pętli (co zrobiła) i zmiennych sumarycznych. Kompilator z wyprzedzeniem może to zrobić, mając wystarczająco dużo czasu na wykonanie analizy kodu. Jednak kompilator just-in-time działa pod ścisłym ograniczeniem czasowym.

podstawowe środki zaradcze to:

  • Zerwać kod do oddzielnych metod tak jak rejestr ESI mogą być ponownie wykorzystane.
  • Usuń wymuszenie jittera (projekt> Właściwości> zakładka Kompilacja> odznacz "Preferuj 32-bitowe"). x64 udostępnia 8 dodatkowych rejestrów.

Ostatni punktor odnosi się do starszego jittera x64 (docelowego .NET 3.5, aby go użyć), ale nie do przepisywania jittera x64 (aka RYuJIT) po raz pierwszy udostępnionego w wersji 4.6. Przepis, który był konieczny, ponieważ stary jitter zajął zbyt dużo czasu, optymalizując kod. Rozczarowujący, RyuJIT ma talent do rozczarowania, myślę, że jego optymalizator może zrobić tu lepszą robotę.

+0

Dziękuję, wygląda jak odpowiedź! co ciekawe, jeśli przesuniemy deklarację sumy 2 (var sum2 = 0;) na miejsce między blokami, wówczas rejestry są używane w obu pętlach. –

Powiązane problemy