2012-03-09 15 views
7

Dzień dobry,Wydajność łamanie oprócz jednej pętli na dwie pętle

Załóżmy, że masz prosty dla pętli jak poniżej ...

for(int i=0;i<10;i++) 
{ 
    //statement 1 
    //statement 2 
} 

Załóżmy, że oświadczenie 1 i 2 były oświadczenie O (1). Poza niewielkim narzutem "rozpoczęcia" kolejnej pętli, podziałałby na to, że pętla na dwie (nie zagnieżdżone, ale sekwencyjne) pętle będzie równie szybka? Na przykład ...

for(int i=0;i<10;i++) 
{ 
    //statement 1 
} 
for(int i=0;i<10;i++) 
{ 
    //statement 2 
} 

Dlaczego zadaje takie głupie pytanie jest, że mam system wykrywania kolizji (CDS), który ma pętli wszystkich obiektów. Chcę „dzielą” funkcjonalność mojego systemu CDS więc mogę po prostu zadzwonić

cds.update(objectlist); 

zamiast złamać mój system CD w górę. (Nie martwcie się zbytnio o moją implementację CDS ... Myślę, że wiem, co robię, po prostu nie wiem jak to wyjaśnić, to, co naprawdę muszę wiedzieć, to czy wykonuję potężne uderzenie wydajności dla pętli na wszystkich swoich obiektach ponownie.

Odpowiedz

2

To zależy od aplikacji.

Możliwe Wady (rozszczepienia):

  • dane nie pasuje do pamięci podręcznej danych L1, dlatego go załadować raz na pierwszej pętli, a następnie załaduj go na drugiej pętli

Możliwe Zyski (rozszczepienia):

  • Twój cd pętla ains wiele zmiennych, dzielenie pomaga zredukować nacisk rejestru/stosu, a optymalizator zamienia go na lepszy kod maszynowy
  • używane funkcje kasują pamięć podręczną instrukcji L1, więc pamięć podręczna jest ładowana w każdej iteracji, a przez dzielenie zarządzasz załadowaniem jej raz (tylko) na pierwszej iteracji każdej pętli

wykazy te z pewnością nie są wyczerpujące, ale już można wyczuć, że istnieje napięcie między kodemi danych. Trudno nam więc zdobyć wykształcone/dzikie domysły, gdy nie wiemy nic.

Wątpliwości: profil. Użyj callgrind, sprawdź chowanie pamięci podręcznej w każdym przypadku, sprawdź liczbę wykonanych instrukcji. Zmierz czas.

1

miarę złożoności o dużym dotyczy to nie robi różnicy czy pętla 1 o (n), a więc jest to rozwiązanie dwóch pętli.
jako o ile chodzi o mikrooptymalizację, trudno powiedzieć, że koszt pętli jest raczej niewielki, nie wiemy, jaki jest koszt dostępu do twoich obiektów (jeśli są w wektorze, to też powinno być raczej małe) , ale jest wiele do rozważenia, aby udzielić użytecznej odpowiedzi:

0

Masz rację zauważając że wystąpi pewien wzrost wydajności poprzez utworzenie drugiej pętli. Dlatego nie może być "równie szybki"; ponieważ ten narzut, choć niewielki, nadal znajduje się nad głową.

Nie będę starał się inteligentnie mówić o tym, jak należy budować systemy kolizyjne, ale jeśli próbujesz zoptymalizować wydajność, lepiej unikać budowania niepotrzebnych struktur kontrolnych, jeśli możesz sobie z tym poradzić bez wyrywania włosów.

Pamiętaj, że przedwczesna optymalizacja jest jedną z najgorszych rzeczy, które możesz zrobić. Obawiam się, że optymalizacja w przypadku problemów z wydajnością, moim zdaniem.

+0

Jak stefaanv zauważyć, koszt zapętlenie przez wszystkich przedmiotów po raz drugi jest nieokreślony z informacjami dałeś. – patrickn

+0

Chciałbym również zauważyć, że dwie struktury kontroli, które wysłałeś, rozwiązują różne problemy, a zatem nie są łatwo porównywane w kontekście wydajności. – patrickn

+0

Bez znajomości większej ilości szczegółów i bez faktycznego pomiaru nie można powiedzieć, która wersja jest szybsza. Buforowanie, zarówno dane i instrukcje, jak i przewidywania rozgałęzień (i tabele) oraz spekulacyjne wykonanie, zwiększają złożoność dzisiejszej optymalizacji. Dobra uwaga przy przedwczesnej optymalizacji. Zmierz najpierw w rzeczywistym świecie, a następnie zoptymalizuj. –

3

Pod względem algorytmicznej złożoności dzielenie pętli nie ma znaczenia.

Pod względem wydajności dzielenie się pętlami może poprawić wydajność, pogorszyć wydajność lub nie spowodować żadnych różnic - zależy to od systemu operacyjnego, sprzętu i - oczywiście - od tego, jakie są statement 1 i statement 2.

2

Z dwoma pętlami będzie pan płacił za:

  • wzrosła wielkości generowanego kodu
  • 2x tyle oddział przewiduje
  • zależności co układ danych rachunku 1 i 2 można być przeładowywania danych do pamięci podręcznej.

Ostatni punkt może mieć ogromny wpływ na obie strony. Powinieneś mierzyć jak przy każdej optymalizacji perf.

+2

Twój trzeci punkt jest prawdopodobnie najważniejszy. Sprowadzi się to do tego, czy mieszczą się Państwo w pamięci cache pierwszego poziomu, czy nie. Jeśli oba połączone wszystkie dane pasują do podziału pamięci podręcznej, prawdopodobnie nie będą pomocne, ale jeśli zbyt duże dla pamięci podręcznej i podziału jest wystarczająco małe, zyski mogą być znaczne. –

1

Jak już wspomniano, złożoność pozostaje.

Jednak w realnym świecie nie jesteśmy w stanie przewidzieć, która wersja działa szybciej. Poniżej przedstawiono czynniki, które odgrywają role, ogromne ones:

  • buforowanie danych
  • buforowanie Instruction
  • spekulatywna wykonanie
  • przewidywania Oddział
  • cel Oddział buforuje
  • liczbę dostępnych rejestrów CPU
  • Wielkość bufora podręcznego

(uwaga: nad nimi wszystkimi znajduje się miecz przeklinający Damoklesa; wszystkie są wikipedyzowalne i można googlable)

Szczególnie ostatni czynnik sprawia, że ​​czasami niemożliwe jest skompilowanie jednego prawdziwego kodu dla kodu, którego wydajność zależy od konkretnych rozmiarów bufora. Niektóre aplikacje będą działały szybciej na procesorach z dużymi pamięciami podręcznymi, podczas gdy wolniejsze będą działać na małych pamięciach podręcznych, a dla niektórych innych aplikacji będzie odwrotnie.

Solutions:

  • Niech Twój kompilator wykonać zadanie transformacji pętli. Współczesne g ++ są całkiem dobre w tej dyscyplinie. Kolejną dyscypliną, w której g ++ jest dobra, jest wektoryzacja automatyczna. Pamiętaj, że kompilatory wiedzą więcej o architekturze komputera niż prawie wszyscy.
  • Wysyłaj różne pliki binarne i dyspozytora.
  • Użyj cache-oblivious data structures/layouts and algorithms, które dostosują się do docelowej pamięci podręcznej.

Zawsze dobrze jest dążyć do oprogramowania, które dostosowuje się do celu, najlepiej bez obniżania jakości kodu. Przed wykonaniem ręcznej optymalizacji, mikroskopowej lub makroskopowej, zmierz świat rzeczywisty, a następnie i dopiero wtedy zoptymalizuj.

Literatura: * Agner Fog's Guides * Intel's Guides

Powiązane problemy