Blog (23)
Komentarze (1.2k)
Recenzje (0)
@masterProcesory wielordzeniowe a wydajność aplikacji

Procesory wielordzeniowe a wydajność aplikacji

Procesory dwu i więcej rdzeniowe zagościły już dawno w naszych domowych komputerach. Niestety nie zawsze przekłada się to na proporcjonalny wzrost, odczuwalnej wydajności naszego blaszaka. Tak właściwie nigdy nie otrzymujemy dwukrotnie wyższej wydajności przy dodaniu jednego jajka.

Dlaczego tak się dzieje? Słyszy się często, iż programy oraz gry nie są pisane pod wielordzeniowe procesory. Co to właściwie znaczy i czy oznacza to że programiści są leniwi? W niektórych przypadkach faktycznie tak jest, jednakże w większości problem dotyczy całkowicie czegoś innego, czego ideę postaram się wyjaśnić.

Wyobraźmy sobie taką sytuację, mamy do napełniania wodą jedno wiadro. Nic prostszego, podkładamy wiadro do kranu i nalewamy z maksymalnym przepływem, co zajmuje nam określony czas. Idziemy krok dalej, zwiększamy wydajność napełniania wiadra wodą, instalujemy drugi kran w odległości 2 metrów od pierwszego. Czy teraz wiadro napełni się szybciej? Oczywiście nie, gdyż nie jesteśmy w stanie nalewać do tego samego wiadra z dwóch źródeł wody, oddalonych od siebie.

Weźmy inny trywialny przykład, skoro jedna kobieta nosi ciąże 9 miesięcy, to czy to znaczy że dwie urodzą dziecko w 4,5 miesiąca? Innymi słowy, pewnych czynności nie sposób podzielić w taki sposób aby uzyskać wzrost wydajności mając do dyspozycji dwie jednostki wykonawcze. Rozwiązując równanie matematyczne, nie możemy podzielić naszej pracy na dwie części i dać jedną z nich komuś innemu, gdyż każdy kolejny krok przekształceń zależy od kroku wcześniejszego.

Tak samo w oprogramowaniu, istnieje szereg zadań których nie sposób podzielić na więcej równoległe wykonywanych strumieni, lub podział ten jest bardzo trudny. Jak na ironię, większość operacji można wykonać równolegle, dlaczego więc programiści tego nie robią?

Wielowątkowość

Aby zrozumieć ten problem, musimy przybliżyć pojęcie jakim jest wielowątkowość w systemach operacyjnych. Każda aplikacja czy gra może posiadać kilka, równolegle wykonujących się instrukcji zlokalizowanych w różnych wątkach. Wygląda to tak jakbyśmy uruchomili dwa odtwarzacze wideo i w obu jednocześnie wyświetlali filmy. Oczywiście fizycznie procesor wykonuje tylko jedną instrukcję na raz, jednakże z punktu widzenia samego programu dzieje się to niejako równocześnie.

Każdy program posiada co najmniej jeden wątek, tzw. wątek główny. Jeśli mamy do czynienia z aplikacją okienkową, z reguł wątek ten odpowiada za interakcję z użytkownikiem. Dzięki temu, po kliknięciu przycisku nasz program reaguje, coś robi, innymi słowy działa. Na pewno znana jest Wam sytuacja w której obciążona aplikacja przestaje odpowiadać na nasze działania, okna nie daje się przeciągnąć, jego zawartość się nie odświeża. Dzieję się tak gdy program zaczyna wykonywać obliczenia w wątku głównym. W tym stanie program nie może przetwarzać komunikatów i dlatego nie odpowiada na nasze działania.

Idealną sytuacją jest, gdy skomplikowane obliczenia aplikacja wykonuje w wątku pobocznym. Wtedy to, interfejs pozostaje „przy życiu” a my mamy kontrolę nad programem. Zastosowanie wątków pobocznych wiąże się z problemami synchronizacji danych programu, dlatego też nie jest to powszechna praktyka.

2 rdzenie, 1 wątek

Skoro zatem mamy już dobrze napisany program, z wątkiem pobocznym, to dlaczego nie działa on 2x szybciej na  maszynie dwurdzeniowej? Z tego prostego powodu, iż wątek główny zajmuje minimalny czas procesora (gdyż nie ma nic do roboty poza reagowaniem na akcje użytkownika), a poboczny w którym aplikacja wykonuje obliczenia maksymalny dostępny. Procesor nie może sobie tego jednego wątku rozbić na dwa rdzenie gdyż wynik działania danego rozkazu, zależny jest od wyniku rozkazu poprzedniego. W praktyce wygląda to tak że rozkazy są wykonywane naprzemiennie na każdym z rdzeni obciążając je po ok 50%. Nie mniej, jest to wykorzystywanie jedynie jednego rdzenia w całości.

Rozwiązaniem problemu jest zastosowanie dwóch lub większej ilości wątków wykonujących obliczenia, ale co ważne, tak aby rozkład pracy był rozłożony równomiernie pomiędzy nie. W takiej sytuacji, gdy 2 wątki są obciążone, 2 rdzenie procesora pracują na pełnych obrotach.

A co jeśli mamy procesor jedno rdzeniowy? Aplikacja w dalszym ciągu będzie działać poprawnie. Nie istnieje coś takiego jak program jedno lub wielordzeniowy, co najwyżej może zostać tak napisany że wykorzystuje to co oferują jednostki z kilkoma rdzeniami. Tutaj pojawia się problem przedstawiony na samym początku, problem podziału zadań na niezależnie wykonujące się strumienie/wątki. Pewnych czynności nie da się przyspieszyć zwiększając skalowalność poziomą a jedynie pionową (czyli stosując jeden szybszy procesor jedno rdzeniowy).

4, 8, 16 i więcej rdzeni

Procesory 4 rdzeniowe nie są już dziś czymś nadzwyczajnym, patrząc dalej w przyszłość doczekamy się pewnie i 16 jajkowych jednostek. Tutaj problem niejako się powtarza. W zależności od tego na ile wątków problem został rozbity, na tyle program będzie szybciej działał na coraz to większej ilości rdzeni. Oczywiście nie można popadać w skrajności, nie ma sensu dzielić operacji która trwa 1 sekundę na 16 wątków tylko po to by na odpowiednio wydajnym komputerze trwało to 16x krócej. Sama inicjacja tylu wątków w systemie pochłonęła by w takiej sytuacji więcej czasu.

Zadania które trwają dostatecznie krótko, wykonuje się w wątku głównym, gdyż użytkownik i tak nie zdążył by w tym czasie zrobić niczego innego z oknem programu. Gorzej jeśli niespodziewanie operacja się wydłuży czy wpadnie w nieskończoną pętle, mamy wtedy najzwyczajniejszą w świecie powieszoną aplikację ;).

CTRL+ALT+DEL

Jak głosi pewne przysłowie, „prawdziwy programista wiesza się razem ze swoim programem”. Dobrze że niewielu z nich stosuje tą życiową mądrość, gdyż bardzo szybko mogło by dojść do wymarcia gatunku.

Wybrane dla Ciebie
Komentarze (27)