Asynchroniczne podejście firmy Microsoft stanowi dobry zamiennik najczęstszych celów programowania wielowątkowego: poprawy zdolności reagowania w odniesieniu do zadań we / wy.
Należy jednak pamiętać, że podejście asynchroniczne nie jest w stanie w ogóle poprawić wydajności ani poprawić odpowiedzi w odniesieniu do zadań intensywnie wykorzystujących procesor.
Wielowątkowość dla responsywności
Wielowątkowość dla responsywności to tradycyjny sposób na utrzymanie reakcji programu podczas ciężkich zadań we / wy lub ciężkich zadań obliczeniowych. Zapisujesz pliki w wątku w tle, aby użytkownik mógł kontynuować pracę bez konieczności oczekiwania na zakończenie pracy dysku twardego. Wątek We / Wy często blokuje oczekiwanie na zakończenie zapisu, więc częste są zmiany kontekstu.
Podobnie, wykonując złożone obliczenia, chcesz umożliwić regularne przełączanie kontekstu, aby interfejs użytkownika pozostawał responsywny, a użytkownik nie sądzi, że program się zawiesił.
Celem nie jest generalnie uruchomienie wielu wątków na różnych procesorach. Zamiast tego interesuje nas po prostu przełączanie kontekstu między długo działającym zadaniem w tle a interfejsem użytkownika, aby interfejs użytkownika mógł aktualizować i odpowiadać użytkownikowi podczas działania zadania w tle. Ogólnie interfejs użytkownika nie pobiera dużej mocy procesora, a struktura wątków lub system operacyjny zazwyczaj decydują się na uruchomienie ich na tym samym procesorze.
W rzeczywistości tracimy ogólną wydajność z powodu dodatkowych kosztów przełączania kontekstu, ale nie obchodzi nas to, ponieważ wydajność procesora nie była naszym celem. Wiemy, że zwykle mamy więcej mocy procesora niż potrzebujemy, dlatego naszym celem w zakresie wielowątkowości jest wykonanie zadania dla użytkownika bez marnowania czasu użytkownika.
„Asynchroniczna” alternatywa
„Podejście asynchroniczne” zmienia ten obraz, umożliwiając przełączanie kontekstu w ramach jednego wątku. Gwarantuje to, że wszystkie nasze zadania będą działały na jednym procesorze, i może zapewnić niewielką poprawę wydajności pod względem mniejszej liczby tworzenia / czyszczenia wątków i mniejszej liczby rzeczywistych przełączeń kontekstu między wątkami.
Zamiast tworzyć nowy wątek, który będzie oczekiwał na odbiór zasobu sieciowego (np. Pobranie obrazu), async
stosowana jest metoda, dzięki której await
obraz staje się dostępny, a tymczasem ulega metodzie wywoływania.
Główną zaletą jest to, że nie musisz martwić się o problemy z wątkami, takie jak unikanie zakleszczenia, ponieważ w ogóle nie używasz blokad i synchronizacji, a programista konfiguruje wątek w tle i wraca z powrotem w wątku interfejsu użytkownika, gdy wynik wraca w celu bezpiecznej aktualizacji interfejsu użytkownika.
Nie zagłębiłem się zbytnio w szczegóły techniczne, ale mam wrażenie, że zarządzanie pobieraniem przy sporadycznej lekkiej aktywności procesora staje się zadaniem nie dla osobnego wątku, ale raczej czymś w rodzaju zadania w kolejce zdarzeń interfejsu użytkownika i kiedy pobieranie zostało zakończone, metoda asynchroniczna jest wznawiana z kolejki zdarzeń. Innymi słowy, await
oznacza coś w rodzaju „sprawdź, czy potrzebny wynik jest dostępny, jeśli nie, umieść mnie z powrotem w kolejce zadań tego wątku”.
Zauważ, że takie podejście nie rozwiązałoby problemu zadania intensywnie wykorzystującego procesor: nie ma danych, na które trzeba czekać, więc nie możemy uzyskać przełączników kontekstu, których potrzebujemy, bez utworzenia rzeczywistego wątku roboczego w tle. Oczywiście nadal wygodne może być użycie metody asynchronicznej do uruchomienia wątku w tle i zwrócenia wyniku w programie, który szeroko wykorzystuje podejście asynchroniczne.
Wielowątkowość dla wydajności
Ponieważ mówisz o „wydajności”, chciałbym również omówić, w jaki sposób wielowątkowość można wykorzystać do zwiększenia wydajności, co jest całkowicie niemożliwe w przypadku jednowątkowego podejścia asynchronicznego.
Kiedy faktycznie znajdujesz się w sytuacji, gdy nie masz wystarczającej mocy procesora na jednym procesorze i chcesz użyć wielowątkowości do wydajności, jest to często trudne. Z drugiej strony, jeśli jeden procesor nie ma wystarczającej mocy obliczeniowej, jest to często jedyne rozwiązanie, które może umożliwić Twojemu programowi wykonanie tego, co chciałbyś osiągnąć w rozsądnym czasie, co sprawia, że praca jest tego warta.
Trywialna równoległość
Oczywiście czasami uzyskanie szybkiego przyspieszenia z wielowątkowości może być łatwe.
Jeśli zdarzy się, że masz dużą liczbę niezależnych zadań wymagających intensywnych obliczeń (to znaczy zadań, których dane wejściowe i wyjściowe są bardzo małe w stosunku do obliczeń, które należy wykonać w celu ustalenia wyniku), wówczas często można uzyskać znaczne przyspieszenie poprzez tworzenie puli wątków (odpowiednio dobranych na podstawie liczby dostępnych procesorów) i posiadanie wątku głównego rozprowadza pracę i zbiera wyniki.
Praktyczny wielowątkowość dla wydajności
Nie chcę się przedstawiać jako ekspert, ale mam wrażenie, że ogólnie rzecz biorąc, najbardziej praktycznym wielowątkowością dla wydajności, która ma miejsce obecnie, jest szukanie miejsc w aplikacji o trywialnej równoległości i używanie wielu wątków czerpać korzyści.
Jak w przypadku każdej optymalizacji, zwykle lepiej jest zoptymalizować po wyprofilowaniu wydajności programu i zidentyfikowaniu gorących punktów: łatwo jest spowolnić program, decydując arbitralnie, że ta część powinna działać w jednym wątku, a ta w innym, bez najpierw określając, czy obie części zajmują znaczną część czasu procesora.
Dodatkowy wątek oznacza większe koszty konfiguracji / porzucenia oraz więcej przełączników kontekstu lub więcej kosztów komunikacji między procesorami. Jeśli nie robi wystarczająco dużo pracy, aby zrekompensować te koszty, jeśli jest na osobnym procesorze i nie musi być osobnym wątkiem ze względu na szybkość reakcji, spowolni to bez żadnych korzyści.
Poszukaj zadań, które mają niewiele współzależności i które zajmują znaczną część czasu wykonywania programu.
Jeśli nie mają wzajemnych zależności, to jest to trywialny paralelizm, możesz łatwo ustawić każdy z wątkiem i cieszyć się korzyściami.
Jeśli możesz znaleźć zadania o ograniczonej współzależności, aby blokowanie i synchronizacja w celu wymiany informacji nie spowalniały ich znacznie, wówczas wielowątkowość może nieco przyspieszyć, pod warunkiem, że unikasz niebezpieczeństw związanych z błędem logicznym podczas synchronizacji lub niepoprawne wyniki z powodu braku synchronizacji, gdy jest to konieczne.
Alternatywnie, niektóre z bardziej powszechnych aplikacji do wielowątkowości nie szukają (w pewnym sensie) przyspieszenia z góry określonego algorytmu, ale zamiast tego mają większy budżet na algorytm, który planują napisać: jeśli piszesz silnik gry , a twoja sztuczna inteligencja musi podjąć decyzję w ramach liczby klatek na sekundę, często możesz zwiększyć swoją inteligencję budżetu cyklu CPU, jeśli możesz dać jej własny procesor.
Pamiętaj jednak, aby wyprofilować wątki i upewnić się, że wykonują wystarczająco dużo pracy, aby w pewnym momencie zrekompensować koszty.
Algorytmy równoległe
Istnieje również wiele problemów, które można przyspieszyć za pomocą wielu procesorów, ale są one zbyt monolityczne, aby po prostu podzielić je na procesory.
Algorytmy równoległe muszą być dokładnie analizowane pod kątem czasu działania dużych O w odniesieniu do najlepszego dostępnego algorytmu nierównoległego, ponieważ koszt komunikacji między procesorami jest bardzo łatwy w celu wyeliminowania jakichkolwiek korzyści z używania wielu procesorów. Zasadniczo muszą używać mniejszej komunikacji między procesorami (w kategoriach dużych O) niż obliczeń na każdym procesorze.
W tej chwili nadal jest to w dużej mierze przestrzeń do badań akademickich, częściowo ze względu na wymaganą złożoną analizę, częściowo ze względu na dość powszechną banalną równoległość, częściowo dlatego, że nie mamy jeszcze tak wielu rdzeni procesora na naszych komputerach, że problemy, które nie można rozwiązać w rozsądnym czasie na jednym procesorze można rozwiązać w rozsądnym czasie przy użyciu wszystkich naszych procesorów.