Próbuję zrozumieć na wysokim poziomie, w jaki sposób pojedyncze wątki przebiegają przez wiele rdzeni. Poniżej znajduje się moje najlepsze zrozumienie. Nie sądzę jednak, aby było to poprawne.
Na podstawie mojego czytania hiperwątkowości wydaje się, że system operacyjny porządkuje instrukcje wszystkich wątków w taki sposób, że nie czekają na siebie. Następnie interfejs CPU dalej porządkuje te instrukcje, dystrybuując jeden wątek do każdego rdzenia i dystrybuując niezależne instrukcje z każdego wątku wśród dowolnych otwartych cykli.
Więc jeśli istnieje tylko jeden wątek, system operacyjny nie dokona żadnej optymalizacji. Jednak interfejs CPU rozdzieli niezależne zestawy instrukcji pomiędzy każdy rdzeń.
Według https://stackoverflow.com/a/15936270 określony język programowania może tworzyć więcej lub mniej wątków, ale nie ma znaczenia przy określaniu, co zrobić z tymi wątkami. Obsługują to system operacyjny i procesor, więc dzieje się tak niezależnie od używanego języka programowania.
Aby wyjaśnić, pytam o jeden wątek prowadzony przez wiele rdzeni, a nie o uruchamianie wielu wątków na jednym rdzeniu.
Co jest nie tak z moim podsumowaniem? Gdzie i jak instrukcje wątku dzielą się na wiele rdzeni? Czy język programowania ma znaczenie? Wiem, że to szeroki temat; Mam nadzieję na zrozumienie tego na wysokim poziomie.
źródło
Odpowiedzi:
System operacyjny oferuje wycinki czasu procesora dla wątków, które można uruchomić.
Jeśli jest tylko jeden rdzeń, wówczas system operacyjny planuje najbardziej odpowiedni wątek do uruchomienia na tym rdzeniu dla przedziału czasu. Po zakończeniu segmentu czasowego lub gdy działający wątek blokuje się na IO lub gdy procesor jest przerywany przez zdarzenia zewnętrzne, system operacyjny ponownie ocenia, który wątek ma zostać uruchomiony (i może ponownie wybrać ten sam wątek lub inny).
Kwalifikowalność do uruchomienia składa się z wariantów dotyczących uczciwości, priorytetu i gotowości, a dzięki tej metodzie różne wątki uzyskują przedziały czasowe, niektóre bardziej niż inne.
Jeśli istnieje wiele rdzeni, N, system operacyjny planuje najbardziej odpowiednie N wątków do uruchomienia na rdzeniach.
Powinowactwo procesora to kwestia wydajności. Za każdym razem, gdy procesor uruchamia inny wątek niż poprzednio, ma tendencję do nieco spowalniania, ponieważ pamięć podręczna jest ciepła dla poprzedniego wątku, ale zimna dla nowego. Zatem uruchamianie tego samego wątku na tym samym procesorze w wielu przedziałach czasowych jest zaletą wydajności.
Jednak system operacyjny może oferować jeden wątek w różnych przedziałach czasowych i może obracać się przez wszystkie procesory w różnych przedziałach czasowych. Nie może jednak, jak mówi @ gnasher729 , uruchamiać jeden wątek na wielu procesorach jednocześnie.
Hyperthreading to metoda sprzętowa, dzięki której jeden ulepszony rdzeń procesora może obsługiwać wykonywanie dwóch lub więcej różnych wątków jednocześnie. (Taki procesor może oferować dodatkowe wątki przy niższych kosztach w nieruchomościach krzemowych niż dodatkowe pełne rdzenie.) Ten ulepszony rdzeń procesora musi obsługiwać dodatkowy stan dla innych wątków, takich jak wartości rejestru procesora, a także ma stan i zachowanie koordynacyjne, które umożliwia współdzielenie jednostek funkcjonalnych w tym CPU bez łączenia wątków.
Hyperthreading, choć technicznie trudny z punktu widzenia sprzętowego, z perspektywy programisty, model wykonania jest jedynie modelem dodatkowych rdzeni procesora, a nie czymś bardziej złożonym. Tak więc system operacyjny widzi dodatkowe rdzenie procesora, choć pojawiają się pewne nowe problemy z koligacją procesora, ponieważ kilka wątków hiperwątkowanych współdzieli architekturę pamięci podręcznej jednego rdzenia procesora.
Możemy naiwnie myśleć, że dwa wątki działające na hiperszytowanym rdzeniu działają o połowę szybciej niż każdy z własnym pełnym rdzeniem. Ale niekoniecznie tak jest, ponieważ wykonanie pojedynczego wątku jest pełne wolnych cykli, a pewna ich część może być wykorzystana przez inny wątek hiperwątkowy. Ponadto, nawet w cyklach bez luzu, jeden wątek może wykorzystywać inne jednostki funkcjonalne niż drugi, więc może wystąpić jednoczesne wykonanie. Ulepszony procesor do hiperwątkowania może mieć kilka innych mocno używanych jednostek funkcjonalnych specjalnie do obsługi tego.
źródło
Nie ma czegoś takiego jak pojedynczy wątek działający na wielu rdzeniach jednocześnie.
Nie oznacza to jednak, że instrukcje z jednego wątku nie mogą być wykonywane równolegle. Istnieją mechanizmy zwane potokowaniem instrukcji i wykonywaniem poza kolejnością, które na to pozwalają. Każdy rdzeń ma wiele zbędnych zasobów, które nie są wykorzystywane przez proste instrukcje, więc wiele takich instrukcji można uruchomić razem (o ile następny nie zależy od poprzedniego wyniku). Jednak nadal dzieje się to w jednym rdzeniu.
Hiperwątkowość jest rodzajem ekstremalnego wariantu tego pomysłu, w którym jeden rdzeń nie tylko wykonuje instrukcje z jednego wątku równolegle, ale miesza instrukcje z dwóch różnych wątków, aby jeszcze bardziej zoptymalizować wykorzystanie zasobów.
Powiązane wpisy w Wikipedii: Potokowanie instrukcji , wykonywanie poza kolejnością .
źródło
a[i] = b[i] + c[i]
pętli, każda iteracja jest niezależna, więc ładowanie, dodawanie i przechowywanie z różnych iteracji może być jednocześnie w locie. Musi zachować iluzję, że instrukcje wykonywane w kolejności programu, ale na przykład sklep, który nie trafia do pamięci podręcznej, nie opóźnia wątku (dopóki nie zabraknie miejsca w buforze sklepu).Podsumowanie: Znajdowanie i wykorzystywanie równoległości (na poziomie instrukcji) w programie jednowątkowym odbywa się wyłącznie sprzętowo, przez rdzeń procesora, na którym działa. I tylko nad oknem kilkuset instrukcji, a nie na dużą skalę zamawiania.
Programy jednowątkowe nie czerpią korzyści z wielordzeniowych procesorów, z wyjątkiem tego, że inne rzeczy mogą działać na innych rdzeniach zamiast tracić czas na zadanie jednowątkowe.
System operacyjny NIE zagląda do strumieni instrukcji wątków. Planuje tylko wątki do rdzeni.
W rzeczywistości każdy rdzeń uruchamia funkcję harmonogramu systemu operacyjnego, gdy musi dowiedzieć się, co dalej. Planowanie jest algorytmem rozproszonym. Aby lepiej zrozumieć maszyny wielordzeniowe, pomyśl o każdym rdzeniu jako o osobnym uruchamianiu jądra. Podobnie jak program wielowątkowy, jądro jest napisane, aby jego kod na jednym rdzeniu mógł bezpiecznie oddziaływać z jego kodem na innych rdzeniach w celu aktualizacji wspólnych struktur danych (takich jak lista wątków, które są gotowe do uruchomienia.
W każdym razie system operacyjny bierze udział w pomaganiu procesom wielowątkowym w wykorzystaniu równoległości na poziomie wątków, które muszą być jawnie ujawnione poprzez ręczne napisanie programu wielowątkowego . (Lub przez kompilator z automatyczną równoległością z OpenMP lub coś takiego).
Rdzeń procesora uruchamia tylko jeden strumień instrukcji, jeśli nie jest zatrzymany (śpi do następnego przerwania, np. Przerwania timera). Często jest to wątek, ale może to być również moduł obsługi przerwań jądra lub inny kod jądra, jeśli jądro postanowiło zrobić coś innego niż powrót do poprzedniego wątku po obsłudze i przerwie lub wywołaniu systemowym.
W przypadku HyperThreading lub innych konstrukcji SMT fizyczny rdzeń procesora działa jak wiele „logicznych” rdzeni. Jedyną różnicą z punktu widzenia systemu operacyjnego między procesorem czterordzeniowym z hyperthreadingiem (4c8t) a zwykłą maszyną 8-rdzeniową (8c8t) jest to, że system operacyjny obsługujący HT spróbuje zaplanować wątki w celu oddzielenia rdzeni fizycznych, aby nie „ konkurować ze sobą. System operacyjny, który nie wiedział o hiperwątkowaniu, zobaczyłby tylko 8 rdzeni (chyba że wyłączysz HT w BIOSie, wykryje tylko 4).
Termin „ front-end” odnosi się do części rdzenia procesora, która pobiera kod maszynowy, dekoduje instrukcje i wydaje je do części rdzenia poza kolejnością . Każdy rdzeń ma własny interfejs i jest częścią rdzenia jako całości. Pobierane przez niego instrukcje są aktualnie uruchomione przez procesor.
W niedziałającej części rdzenia instrukcje (lub uops) są wysyłane do portów wykonawczych, gdy ich operandy wejściowe są gotowe i jest wolny port wykonawczy. Nie musi się to zdarzać w kolejności programów, więc w ten sposób procesor OOO może wykorzystać równoległość na poziomie instrukcji w jednym wątku .
Jeśli zamienisz „rdzeń” na „jednostkę wykonawczą” w swoim pomyśle, jesteś blisko poprawienia. Tak, procesor równolegle dystrybuuje niezależne instrukcje / polecenia do jednostek wykonawczych. (Ale istnieje pewna pomyłka terminologiczna, ponieważ powiedziałeś „front-end”, kiedy tak naprawdę to planista instrukcji CPU, zwany Reservation Station, wybiera instrukcje gotowe do wykonania).
Wykonanie poza kolejnością może znaleźć ILP tylko na poziomie lokalnym, tylko do kilkuset instrukcji, a nie między dwiema niezależnymi pętlami (chyba że są krótkie).
Na przykład równoważnik tego asm
będzie działać tak szybko, jak ta sama pętla, zwiększając tylko jeden licznik na Intel Haswell.
i++
zależy tylko od poprzedniej wartościi
, podczas gdyj++
zależy tylko od poprzedniej wartościj
, więc dwa łańcuchy zależności mogą działać równolegle, nie przerywając iluzji wszystkiego, co jest wykonywane w kolejności programu.Na x86 pętla wyglądałaby mniej więcej tak:
Haswell ma 4 porty wykonywania liczb całkowitych, a wszystkie z nich mają jednostki sumujące, więc może utrzymać przepustowość do 4
inc
instrukcji na zegar, jeśli wszystkie są niezależne. (Przy opóźnieniu = 1, więc potrzebujesz tylko 4 rejestrów, aby zmaksymalizować przepustowość, utrzymując 4inc
instrukcje w locie. Porównaj to z wektorowym FP MUL lub FMA: opóźnienie = 5 przepustowość = 0,5 potrzebuje 10 wektorowych akumulatorów, aby utrzymać 10 FMA w locie aby zmaksymalizować przepustowość. Każdy wektor może mieć 256b i pomieścić 8 pływaków o pojedynczej precyzji).Przejęta gałąź jest również wąskim gardłem: pętla zawsze zajmuje co najmniej jeden cały zegar na iterację, ponieważ przepustowość przejętej gałęzi jest ograniczona do 1 na zegar. Mógłbym umieścić jeszcze jedną instrukcję w pętli bez zmniejszania wydajności, chyba że odczytuje / zapisuje
eax
lubedx
w takim przypadku wydłużyłby ten łańcuch zależności. Umieszczenie 2 dodatkowych instrukcji w pętli (lub jednej złożonej instrukcji wielopunktowej) stworzyłoby wąskie gardło w interfejsie, ponieważ może wydać tylko 4 impulsy na zegar do rdzenia poza kolejnością. (Zobacz to SO Q&A, aby uzyskać szczegółowe informacje na temat tego, co dzieje się w przypadku pętli, które nie są wielokrotnością 4 uops: bufor pętli i cache uop sprawiają, że rzeczy są interesujące.)W bardziej skomplikowanych przypadkach znalezienie równoległości wymaga spojrzenia na większe okno instrukcji . (np. może istnieje sekwencja 10 instrukcji, które wszystkie zależą od siebie, a następnie kilka niezależnych).
Pojemność bufora ponownego zamówienia jest jednym z czynników ograniczających rozmiar okna poza kolejnością. W przypadku Intel Haswell jest to 192 ups. (I możesz nawet zmierzyć to eksperymentalnie , wraz z pojemnością zmiany nazwy rejestru (rozmiar pliku rejestru).) Rdzenie procesora o niskiej mocy, takie jak ARM, mają znacznie mniejsze rozmiary ROB, jeśli w ogóle wykonują się poza kolejnością.
Należy również pamiętać, że procesory muszą być przetwarzane potokowo, a także poza kolejnością. Musi więc pobierać i dekodować instrukcje na długo przed tymi, które są wykonywane, najlepiej o wystarczającej przepustowości, aby uzupełnić bufory po pominięciu jakichkolwiek cykli pobierania. Gałęzie są trudne, ponieważ nie wiemy, skąd je pobrać, jeśli nie wiemy, w którą stronę poszła gałąź. Właśnie dlatego przewidywanie gałęzi jest tak ważne. (I dlaczego współczesne procesory używają spekulatywnego wykonywania: domyślają się, w którą stronę pójdzie gałąź, i zaczną pobierać / dekodować / wykonywać strumień instrukcji. Po wykryciu błędnej prognozy przywracają do ostatniego znanego dobrego stanu i wykonują stamtąd.)
Jeśli chcesz dowiedzieć się więcej o wewnętrznych procesorach, na wiki wiki Stackoverflow x86 znajduje się kilka linków, w tym przewodnik mikroarchitera Agner Fog oraz szczegółowe opisy Davida Kantera ze schematami procesorów Intel i AMD. Z jego podsumowania mikroarchitektury Intel Haswell jest to ostatni schemat całego potoku rdzenia Haswella (nie całego układu).
To jest schemat blokowy pojedynczego rdzenia procesora . Czterordzeniowy procesor ma 4 na chipie, każdy z własną pamięcią podręczną L1 / L2 (współdzielenie pamięci podręcznej L3, kontrolerów pamięci i połączeń PCIe z urządzeniami systemowymi).
Wiem, że jest to niezwykle skomplikowane. Artykuł Kantera pokazuje także części tego, aby na przykład rozmawiać o interfejsie oddzielnie od jednostek wykonawczych lub pamięci podręcznych.
źródło
inc
instrukcji w tym samym cyklu zegara, na swoich 4 liczbach całkowitych jednostek wykonawczych ALU.