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ę.
Może spróbuj użyć zmiennych zamiast "Console.WriteLine"?To może być problem. – ispiro
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
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