while (condition) {
...
}
Przepływ pracy:
- sprawdzić stan;
- jeśli fałsz, przeskocz na zewnątrz pętli;
- uruchom jedną iterację;
- wskocz na górę.
if (condition) do {
...
} while (condition);
Przepływ pracy:
- sprawdzić stan;
- jeśli fałsz, przeskocz poza pętlę;
- uruchom jedną iterację;
- sprawdzić stan;
- jeśli prawda, przejdź do kroku 3.
Porównując te dwa, można łatwo zauważyć, że ten ostatni może w ogóle nie wykonywać żadnych skoków, pod warunkiem, że w pętli jest dokładnie jeden krok i ogólnie liczba skoków będzie o jeden mniejsza niż liczba iteracji. Ten pierwszy będzie musiał skoczyć z powrotem, aby sprawdzić warunek, tylko po to, aby wyskoczyć z pętli, gdy warunek jest fałszywy.
Skoki na nowoczesnych architekturach procesorów potokowych mogą być dość kosztowne: ponieważ procesor kończy wykonywanie sprawdzeń przed skokiem, instrukcje poza tym skokiem są już w połowie potoku. Całe to przetwarzanie musi zostać odrzucone, jeśli przewidywanie gałęzi nie powiedzie się. Dalsze wykonanie jest opóźnione podczas ponownego przygotowania potoku.
Wyjaśnienie wspomnianej prognozy rozgałęzienia : dla każdego rodzaju skoku warunkowego procesor ma dwie instrukcje, z których każda zawiera zakład na wynik. Na przykład, umieściłbyś instrukcję mówiącą „ skok, jeśli nie zero, obstawianie niezerowe ” na końcu pętli, ponieważ skok będzie musiał zostać wykonany we wszystkich iteracjach z wyjątkiem ostatniej. W ten sposób CPU zaczyna pompować swój potok z instrukcjami podążającymi za celem skoku zamiast tymi, które następują po samej instrukcji skoku.
Ważna uwaga
Proszę nie nie wziąć to jako przykład, jak zoptymalizować na poziomie kodu źródłowego. Byłoby to całkowicie błędne, ponieważ, jak już jasno wynika z twojego pytania, transformacja z pierwszej formy do drugiej jest czymś, co kompilator JIT robi rutynowo, całkowicie samodzielnie.