Kiedy, jeśli w ogóle, rozwijanie pętli jest nadal przydatne?

Próbowałem zoptymalizować jakiś niezwykle krytyczny dla wydajności kod (algorytm szybkiego sortowania, który jest nazywany miliony i miliony razy w symulacji monte carlo) przez rozwijanie pętli. Oto wewnętrzna pętla, którą staram się przyspieszyć:

// Search for elements to swap.
while(myArray[++index1] < pivot) {}
while(pivot < myArray[--index2]) {}

Próbowałem rozwinąć do czegoś takiego:

while(true) {
    if(myArray[++index1] < pivot) break;
    if(myArray[++index1] < pivot) break;
    // More unrolling
}


while(true) {
    if(pivot < myArray[--index2]) break;
    if(pivot < myArray[--index2]) break;
    // More unrolling
}

To nie robi żadnej różnicy, więc zmieniłem ją z powrotem na bardziej czytelną formę. Miałem podobne doświadczenia innym razem próbowałem rozwinąć pętlę. Biorąc pod uwagę jakość branch predictors na nowoczesnym sprzęcie, kiedy, jeśli w ogóle, jest rozwijanie pętli nadal przydatna optymalizacja?

Author: dsimcha, 2010-02-27

9 answers

Rozwijanie pętli ma sens, jeśli można przerwać łańcuchy zależności. Daje to niekonsekwentny lub superskalarny procesor możliwość lepszego zaplanowania rzeczy, a tym samym szybszego działania.

Prosty przykład:

for (int i=0; i<n; i++)
{
  sum += data[i];
}

Tutaj łańcuch zależności argumentów jest bardzo krótki. Jeśli masz opóźnienie, ponieważ masz Cache-miss na tablicy danych procesor nie może nic zrobić, ale czekać.

Z drugiej strony ten kod:

for (int i=0; i<n; i+=4)
{
  sum1 += data[i+0];
  sum2 += data[i+1];
  sum3 += data[i+2];
  sum4 += data[i+3];
}
sum = sum1 + sum2 + sum3 + sum4;
Może biegać szybciej. W przypadku braku pamięci podręcznej lub inne przeciąganie w jednym obliczeniu istnieją jeszcze trzy inne łańcuchy zależności, które nie zależą od przeciągania. Procesor nie w porządku może je wykonać.
 126
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-09 21:54:49

To nie robi żadnej różnicy, ponieważ robisz tę samą liczbę porównań. Oto lepszy przykład. Zamiast:

for (int i=0; i<200; i++) {
  doStuff();
}

Napisz:

for (int i=0; i<50; i++) {
  doStuff();
  doStuff();
  doStuff();
  doStuff();
}

Nawet wtedy to prawie na pewno nie będzie miało znaczenia, ale teraz robisz 50 porównań zamiast 200 (wyobraź sobie, że porównanie jest bardziej złożone).

ręczne rozwijanie pętli w ogóle jest w dużej mierze artefaktem historii. To kolejna z rosnącej listy rzeczy, które dobry kompilator zrobi dla ciebie, gdy ma znaczenie. Na przykład większość ludzi nie zadaje sobie trudu pisania x <<= 1 lub x += x zamiast x *= 2. Po prostu napisz x *= 2, A kompilator zoptymalizuje go dla Ciebie do tego, co najlepsze.

W zasadzie coraz mniej jest potrzeby zastanawiania się nad kompilatorem.

 26
Author: cletus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-01 04:03:49

Niezależnie od przewidywania gałęzi na nowoczesnym sprzęcie, większość kompilatorów i tak rozwija pętle.

Warto byłoby dowiedzieć się, jak wiele optymalizacji robi dla ciebie twój kompilator.

Znalazłem prezentację Felixa von Leitnera bardzo pouczającą w tym temacie. Polecam przeczytać. Podsumowanie: nowoczesne Kompilatory są bardzo sprytne, więc optymalizacje ręczne prawie nigdy nie są skuteczne.

 14
Author: Peter Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-27 22:48:39

O ile to Rozumiem, nowoczesne Kompilatory już rozwijają pętle tam, gdzie jest to właściwe - przykładem jest gcc, jeśli przejdzie optymalizacja flagi to instrukcja mówi, że będzie:

Rozwiń pętle, których liczba iteracji można określić na czas kompilacji lub po wejściu do pętla.

Tak więc, w praktyce jest prawdopodobne, że Twój kompilator wykona za Ciebie trywialne przypadki. Dlatego do ciebie należy upewnienie się, że jak najwięcej Twoich pętli jest łatwych dla kompilator do określenia, ile iteracji będzie potrzebnych.

 2
Author: Rich Bradshaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-27 22:50:09

Rozwijanie pętli, niezależnie od tego, czy jest to rozwijanie ręczne, czy rozwijanie kompilatora, często może być nieproduktywne, szczególnie w przypadku nowszych procesorów x86 (Core 2, Core i7). Podsumowując: porównuj swój kod z rozwijaniem pętli i bez niego na dowolnych procesorach, na których planujesz wdrożyć ten kod.

 2
Author: Paul R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-27 23:40:26

Próba bez wiedzy to nie jest sposób, aby to zrobić.
Czy ten rodzaj zajmuje wysoki procent całkowitego czasu?

Wszystkie rozwijane pętle zmniejszają narzut pętli zwiększania / zmniejszania, porównywania dla warunku zatrzymania i skoków. Jeśli to, co robisz w pętli, zajmuje więcej cykli instrukcji niż sama pętla napowietrzna, nie zobaczysz większej poprawy w procentach.

Oto przykład, jak uzyskać maksymalną wydajność.

 1
Author: Mike Dunlavey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:28

Rozwijanie pętli może być pomocne w konkretnych przypadkach. Jedynym zyskiem nie jest pominięcie niektórych testów!

Może na przykład umożliwić skalarną wymianę, wydajne wstawianie prefetchingu oprogramowania... Zdziwiłbyś się, jak bardzo może to być przydatne (możesz łatwo uzyskać 10% przyspieszenie na większości pętli, nawet z-O3), agresywnie rozwijając.

Jak już wcześniej zostało powiedziane, zależy to w dużej mierze od pętli, a kompilator i eksperyment są niezbędne. Trudno ustalić regułę (lub heurystyka kompilatora dla rozwinięcia byłaby idealna)

 1
Author: Kamchatka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-01 20:38:44

Rozwijanie pętli zależy całkowicie od rozmiaru problemu. Jest to całkowicie zależne od algorytmu jest w stanie zmniejszyć rozmiar do mniejszych grup pracy. To, co zrobiłeś powyżej, nie wygląda tak. Nie jestem pewien, czy symulację monte carlo można w ogóle rozwinąć.

I dobrym scenariuszem dla rozwijania pętli byłoby obracanie obrazu. Ponieważ można obracać oddzielne grupy pracy. Aby to zadziałało, musisz zmniejszyć liczbę iteracji.

 0
Author: jwendl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-27 22:45:37

Rozwijanie pętli jest nadal przydatne, jeśli w pętli i z nią jest wiele zmiennych lokalnych. Aby ponownie użyć tych rejestrów więcej zamiast zapisywać jeden dla indeksu pętli.

W twoim przykładzie używasz małej ilości zmiennych lokalnych, nie nadużywając rejestrów.

Porównanie (do końca pętli) jest również główną wadą, jeśli porównanie jest ciężkie( np. Instrukcja Nie-test), zwłaszcza jeśli zależy to od funkcji zewnętrznej.

Rozwijanie pętli pomaga zwiększyć procesor świadomość przewidywania gałęzi również, ale te i tak się zdarzają.

 0
Author: LiraNuna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-27 22:49:40