Skocz do zawartości
Isharoth

C++ | SDL2 - Wielowątkowość w pętli

Rekomendowane odpowiedzi

Cześć, zacząłem zabawę z wielowątkowością w C++ i jednej rzeczy nie rozumiem. Kiedy wrzucę joina do pierwszej pętli, wtedy program działa prawidłowo, ale jednak wolniej niż na pojedynczym wątku (co jest akurat zrozumiałe). Chciałbym jednak, żeby wszystkie wątki wykonały swoje zadanie, a główny wątek grzecznie poczekał aż wszystkie będą gotowe. Niestety gdy zrobiłem drugą pętlę, w której dołączam wszystkie wątki, to program się zamyka. W jaki sposób mogę to ugryźć?

 

std::thread t[8];
for (int j = 0; j < 8; ++j)
{
  t[j] = std::thread(&Map::threadDraw, this, j, tiles_size, Tiles);
}

for (int j = 0; j < 8; ++j)
{
  t[j].join();
}

 

Mam pętlę, która wykonuje się nawet kilkadziesiąt tysięcy razy (teraz pętla znajduje się w funkcji threadDraw). Podzieliłem to zadanie tak, aby każdy wątek miał do wykonania taką samą część pracy. Niestety nie wiem jak to zrobić w dobry sposób i z czego skorzystać, bo na pewno czegoś tu brakuje.

 

Doczytałem, że problem może powodować SDL (bo piszę grę). Podobno funkcje z SDL operujące na renderze, muszą być wykonywane na wątku, w którym ten renderer został stworzony. Tyle, że osoba która to pisała zaznaczyła, że dotyczy to sytuacji, w której wykorzystujemy OpenGL, podczas gdy ja mam obecnie DirectX. Jeśli rzeczywiście problem leży gdzieś tutaj to chyba sobie daruję wielowątkowość, bo musiałbym przepisać połowę kodu, a mam tydzień czasu na skończenie tego. szczerbaty.gif

Z drugiej strony, wątpię że to akurat ten sam problem, bo przecież tak czy inaczej wykonuję to samo na różnych wątkach. Kwestia tego kiedy dołączę dany wątek, a niestety rozwiązanie jakie przedstawiłem wyżej kończy się crashem.

Edytowane przez Isharoth

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Najbezpieczniej jest rysowac w jednym tylko watku i w 99,99% przypadkow to jest wystarczajace do osiagniecia liczby FPS ktorej potrzebujesz. Nie widze powodu dla ktorego mialbys renderowac w roznych watkach jednoczesnie. Bo na pewno nie jest to wydajnosc.

 

Powinienes rysowac w jednym watku i zrobic konstrukcje w ktorej polecenia pobierane sa z kolejki do ktorej dostep jest synchronizowany.

 

Alternatywna metoda - utworzyc sobie mutex i w momencie kiedy wywolujesz metode ktorej wynikiem sa calle do renderea przejmowac wlasnosc na tym mutexie. Czyli de facto tez synchronizowac rysowanie. W ten sposob sprawisz ze renderowanie bedzie thread safe.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Jeśli chodzi o samo rysowanie to tak, niestety grę wcześniej pisałem bez myśli o wielowątkowości i teraz wychodzą takie problemy. Debuger wskazuje na RenderCopy, czyli jednak to jest to. Co ciekawe na DirectX gra crashuje, na OpenGL działa, ale nie rysuje poprawnie mapy.

Myślałem o wielowątkowości, bo każdy bloczek rysowany na mapie traktuję jako osobny obiekt. Więc w pętli w której rysuję mapę przy mapie 160x160 odwołuję się do 25600 obiektów, sprawdzam ich właściwości, animacje i renderuję na mapie w odpowiedniej pozycji. Na Ryzenie 2700X wychodzi około 90 fps, co nie jest zbyt dobrym wynikiem jak na grę 2D lol2.gif. Niestety trzeba by tu było część rzeczy przepisać. Zastanowię się jeszcze nad tym co napisałeś, bo sam póki co nie mam żadnych pomysłów.

Czytałem o tych mutexach, ale póki co nie ogarniam o co chodzi. Dzięki bardzo za propozycje! :)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Problem polega na tym, ze nie ogarniasz programowania wspolbieznego. Doczytaj sobie o tym i o podstawowych mechanizmach synchronizacji.

 

Temat jest ciekawy i warty zglebienia :)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Naiwne zrównoleglenie + synchronizacja może się skończyć gorzej niż wariant jednowątkowy...

 

Profilowałeś program, czy optymalizujesz "na pałę"?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Profilowałeś program, czy optymalizujesz "na pałę"?

Na pałę nie. Wszystko dotyczy operacji która powtarza się najczęściej i zabiera sporo fps. Samo rysowanie mapy składającej się z 25600 bloczków i mam spadek z ~2500fps do 90 fps. Na pewno da się to zrobić w lepszy sposób, ale grę muszę mieć skończoną do następnego tygodnia (projekt na studia). Pewnie będę dalej nad tym pracować w ramach hobby. Problem w tym, że mam jedną prostą pętlę przy rysowaniu mapy, w której odwołuję się do każdego bloczku i rysuję jego teksturę w odpowiednim miejscu. No i tu moim gwoździem do trumny jest to, że SDLowe RenderCopy będzie zawsze wywoływane z poziomu klasy bloczku (a raczej mojego texture managera, do którego odwołuję się w tej klasie) i tu się cały program wysypuje przy wielowątkowości. Gdybym brał wcześniej pod uwagę, że będę próbował robić multithreading, to pewnie rozpisałbym to inaczej. Na początku myślałem, że będzie dało się rozłożyć tą prostą pętlę na wątki, jednak problem jest bardziej złożony, a ja pierwszy raz w ogóle ruszałem wielowątkowość przy programowaniu. bigsmile.gif

 

I tak już zoptymalizowałem co się dało, bo wcześniej było poniżej 60 fps przy tym samym rozmiarze mapy. Po prostu zrobię mniejsze mapki, a jak skończę robić tę grę, to i tak raczej przepiszę cały silnik od podstaw i to już nie na SDL, tylko czystym OpenGL, bo z shaderami też chciałbym się pobawić. :)

Edytowane przez Isharoth

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Przy tygodniowym deadline odpuść jakąkolwiek wielowątkowość ;)

 

Jeżeli coś by miało jakieś szanse zadziałać, to użycie wątków tak, by każdy niezależnie składał swoją 1/8 ostatecznego obrazu, i dopiero w głównym wątku to wszystko złożyć w całość. Jak i czy to wykonalne - mnie nie pytaj, nie tykałem SDL'a od lat :P

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach
samo rysowanie mapy składającej się z 25600 bloczków i mam spadek z ~2500fps do 90 fps

Zastanawia mnie po co Ci 2500 fps? 90 fps to dobry wynik, chociaz na pewno mozna go poprawic, jednak bedzie to kosztowalo troche czasu.

 

Bez wiekszej ilosci kodu trudno bedzie pomoc :)

 

Twoj problem polega na tym, ze program nie dostarcza wystarczajaco szybko danych do potoku karty graficznej. Profilowanie byloby wskazane. Nawet najprostsze i lopatologiczne na poczatek z manadzerem zadan i programem GPU-Z i interesuje Cie "gpu load".

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Zastanawia mnie po co Ci 2500 fps? 90 fps to dobry wynik, chociaz na pewno mozna go poprawic, jednak bedzie to kosztowalo troche czasu.

 

Tak, ale jak 90 fps mam na 2700X, to ile będzie na słabszych CPU? Właśnie o to się martwiłem, tym bardziej jakbym chciał zrobić jeszcze większe mapy. Przed optymalizacją na moim laptopie było jakoś ~40fps, bardzo słabiutko jak na grę 2D, a to tylko mapa z bloczkami, które mają swoje właściwości. A za optymalizację też będę oceniany. lol2.gif Cały spadek wydajności obraca się tylko wokół tej jednej, prostej pętli w której wywołuję metodę update dla 25 tys. obiektów, dlatego właśnie to chciałem rozdzielić na wątki. :)

GPU raczej nie ma za dużo do roboty.

 

W grze działa mi nawet OSD z Afterburnera, więc wszystko mam pod ręką. Użycie GPU na minimalnym taktowaniu skacze od 5 do 30%. Zwracam jednak szczególną uwagę na CPU, bo nawet gdy na próbę zakomentowałem całą metodę update, to zobaczyłem, że samo wywoływanie tylu obiektów zabiera aż tyle fps. A jeden wątek CPU jest prawie cały czas na 100%. wink.gif

 

Haha, problem leżał w mojej iteracji wektora. Po zmianie pętli na:

 

for (auto& i : Tiles)
{
  i->update();
}

całość działa 4 razy szybciej. szczerbaty.gif

Edytowane przez Isharoth

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Pokaz poprzednia wersje :)

Sprawdzasz to na konfiguracji debug? Bo jesli tak to jest kompletnie niemiarodajne.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Wcześniej było tak:

 

for (int i = 0; i < tiles_size; i++)
{
	Tiles[i]->update();
}

 

 

Ok, po zmianie sposobu iteracji miałem 390fps, teraz po zmianie konfiguracji na release mam ~1500. lol2.gif Problem z głowy, bez wielowątkowości (ale i tak do tego przysiądę). Dzięki za pomoc. :)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Jest ktoś w stanie wytłumaczyć skąd taka różnica w wydajności między tymi dwiema pętlami?

Na moje oko i to co znalazłem w sieci, to nie powinno być tak dużej różnicy.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Operator [] wykonuje dodatkowe operacje do sprawdzenia czy jesteśmy in range.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

operator[] nie wykonuje sprawdzenia zakresu.

iterowanie po zakresach powoduje uzycie kilku sprytnych optymalizacji - doczytaj w necie bo duzo by wymieniac.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Autor dokonał zmiany jeszcze w momencie, gdy prowadził "pomiar" w debug.

A tam - przynajmniej w implementacji ms - operator[] sprawdza zakres.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

To ciekawie by było porównać obie wersje w release.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach
Napisano (edytowane)

Chętnie sprawdzę jutro i dam znać. Bo dziś siedziałem nad tym tyle, że nawet nie mam już więcej ochoty wracać do Visual Studio. szczerbaty.gif

 

Na release nie widzę różnicy.

Edytowane przez Isharoth

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się

  • Ostatnio przeglądający   0 użytkowników

    Brak zarejestrowanych użytkowników przeglądających tę stronę.

  • Tematy

  • Odpowiedzi

    • Niektórych może zainteresuje moja opinia jako osoby, która nigdy wcześniej nie grała w jakiegokolwiek "Finala". Nie sądzę aby liczba platform miała jakiekolwiek decydujące znaczenie w kwestii wyników sprzedażowych tej gry. Pobrałem sobie demo FF VII Rebirth i powiem szczerze, że się odbiłem. Już od samego początku nie było swego to nazwijmy rodzaju "chemii" a ciut później tzn jak trafiłem do wioski, było jedynie gorzej. Nie podszedł mi zarówno sam projekt gry jak i strona techniczna. W mojej opinii ten tytuł jest strasznie archaiczny, całość przypomina mi 20-25 letni projekt z odświeżoną grafiką. Być może trzeba być wieloletnim fanem tej serii lub po prostu wielbicielem japońszczyzny ale w moje gusta ta gra nie trafiła. Jutro chcę jeszcze sobie pobrać demo FF XVI ale jeśli w tym wypadku też się odbiję to po prostu będę wiedział, że to nie jest seria dla mnie.
    • Niestety budżet ograniczał, na takie wodotryski 😟 
    • Nie ma co się dziwić masz mega budżetowe kości na tych modułach, to o czym piszesz typu 14-14-14xxx czy 14-15-15 / 14-16-16 / 15-15-15 itd z niskim trfc przy wysokim taktowaniu to się robiło na dobrych modułach b-die a nie na budżetówkach jak good ramy  ale przynajmniej masz dual rank i to jest plus bo nie każde 2x16 był/są DR zawsze to nadrabiasz tym te luzniejsze timingi.  Przy x3d i tak nie wiele to daje a takie "fikuśne" nastawy typu 16-8-22 to też niczego nie zmienia, równie dobrze możesz tam dać 16-16-22, w skrócie aby ta ósemka coś zmieniała to musiałbyś tcl mieć też na 8 co jest niemożliwe u nikogo na takich wartościach  
    • Dokładnie tak jak Krzysiak napisał to wyglądało i nikogo nie wyśmiewam  napisałem jaki był problem i to był jedyny jaki miałem z amd a lecę na takich platformach od lat, intela miałem raz w życiu u siebie w PC  
    • Na wstępie, nie wiem czy wybrałem odpowiedni dział, jeśli nie to proszę o przeniesienie tematu w właściwe miejsce Dziękuję. Witam szanownych użytkowników Zwracam się do Was z wielką prośbą o pomoc w takiej sprawie Niedawno złożyłem sobie starszego PC-ta do zabawy w retro, komputer był na Windowsie XP i posiada dysk 80GB Miałem na nim zapisane mnóstwo różnego rodzaju starych programów pod DOS i starych gier, które zbierałem przez dłuugi czas, większość pochodziła jeszcze z starych dyskietek które zostały już zagubione bądź sformatowane Tyle historii w dużym skrócie, a teraz co się stało, bawiąc się dzisiaj uruchomiłem komputer z dyskietki startowej z DOSem 6.22 i uruchomiłem program Fdisk i przez własną głupotę omyłkowo usunąłem partycję startową dysku C 😭na której było wszystko, od systemu Win XP poprzez wszystkie programy i gry, o ile postawić na nowo Windowsa to nie problem o tyle najbardziej zależy mi na całej reszcie, czy da się jakoś przywrócić tą partycję lub chociaż programy zapisane na dysku? Formatowania na szczęście w nieszczęściu nie robiłem. Jeśli da się to jakoś uratować, przywrócić partycję lub chociaż utracone oprogramowanie zbierane latami to jakim programem darmowym można to zrobić najłatwiej i najszybciej? Bardzo proszę Was o pomoc, wiem że można na Was liczyć Dodam że posiadam drugi nowszy komputer z Windowsem 10 do którego mogę podpiąć ten dysk HDD z tego starego komputera ale co dalej?
  • Aktywni użytkownicy

×
×
  • Dodaj nową pozycję...