Na najniższym poziomie (w sprzęcie) tak, jeśli są drogie. Aby zrozumieć dlaczego, musisz zrozumieć, jak działają potoki .
Bieżąca instrukcja do wykonania jest przechowywana w czymś, co zwykle nazywa się wskaźnikiem instrukcji (IP) lub licznikiem programu (PC); te terminy są synonimami, ale różne terminy są używane w różnych architekturach. W przypadku większości instrukcji komputer PC następnej instrukcji to tylko bieżący komputer osobisty plus długość bieżącej instrukcji. W przypadku większości architektur RISC wszystkie instrukcje mają stałą długość, więc komputer można zwiększać o stałą wartość. W przypadku architektur CISC, takich jak x86, instrukcje mogą mieć zmienną długość, więc logika, która dekoduje instrukcję, musi ustalić, jak długo bieżąca instrukcja ma znaleźć lokalizację następnej instrukcji.
Jednak w przypadku instrukcji rozgałęzienia następna instrukcja do wykonania nie jest następną lokalizacją po bieżącej instrukcji. Gałęzie to gotos - informują procesor, gdzie jest następna instrukcja. Gałęzie mogą być warunkowe lub bezwarunkowe, a lokalizacja docelowa może być stała lub obliczona.
Warunkowe i bezwarunkowe są łatwe do zrozumienia - gałąź warunkowa jest brana tylko wtedy, gdy zachodzi pewien warunek (np. Czy jedna liczba jest równa drugiej); jeśli gałąź nie jest przejęta, sterowanie przechodzi do następnej instrukcji po gałęzi jak zwykle. W przypadku gałęzi bezwarunkowych brana jest zawsze. Gałęzie warunkowe pojawiają się w if
instrukcjach i testach kontrolnych pętli for
i while
. Bezwarunkowe gałęzie pojawiają się w nieskończonych pętlach, wywołaniach funkcji, zwrotach funkcji break
i continue
instrukcjach, niesławnych goto
instrukcjach i wielu innych (listy te nie są wyczerpujące).
Oddział docelowy to kolejna ważna kwestia. Większość oddziałów ma ustalony cel - przechodzą do określonej lokalizacji w kodzie, która jest ustalana w czasie kompilacji. To zawieraif
instrukcje, wszelkiego rodzaju pętle, zwykłe wywołania funkcji i wiele innych. Obliczone gałęzie obliczają miejsce docelowe gałęzi w czasie wykonywania. Obejmuje to switch
instrukcje (czasami), powracające z funkcji, wywołania funkcji wirtualnych i wywołania wskaźników funkcji.
Więc co to wszystko oznacza dla wydajności? Kiedy procesor widzi instrukcję rozgałęzienia pojawiającą się w jego potoku, musi dowiedzieć się, jak kontynuować zapełnianie potoku. Aby dowiedzieć się, jakie instrukcje pojawiają się po gałęzi w strumieniu programu, musi wiedzieć dwie rzeczy: (1) czy gałąź zostanie podjęta i (2) miejsce docelowe gałęzi. Ustalenie tego nazywa się prognozowaniem gałęzi i jest to trudny problem. Jeśli procesor zgadnie poprawnie, program działa dalej z pełną prędkością. Jeśli zamiast tego procesor zgadnie nieprawidłowo , po prostu spędził trochę czasu na obliczeniu niewłaściwej rzeczy. Teraz musi opróżnić swój potok i załadować go ponownie instrukcjami z właściwej ścieżki wykonania. Podsumowując: wielki hit wydajnościowy.
Tak więc powodem, dla którego wyciągi są drogie, są błędne przewidywania branży . To tylko na najniższym poziomie. Jeśli piszesz kod wysokiego poziomu, nie musisz się martwić o te szczegóły. Powinieneś przejmować się tym tylko wtedy, gdy piszesz kod krytyczny dla wydajności w C lub asemblerze. W takim przypadku pisanie kodu bez gałęzi może być często lepsze niż kod, który rozgałęzia się, nawet jeśli potrzeba kilku dodatkowych instrukcji. Istnieje kilka fajnych bit-twiddling sztuczki można zrobić, aby obliczyć takie rzeczy jak abs()
, min()
i max()
bez rozgałęzień.