W jaki sposób bloki / wypaczenia / wątki CUDA są mapowane na rdzenie CUDA?

142

Używam CUDA od kilku tygodni, ale mam pewne wątpliwości co do alokacji bloków / wypaczeń / wątków. Studiuję architekturę z dydaktycznego punktu widzenia (projekt uniwersytecki), więc osiągnięcie maksymalnej wydajności nie jest moim problemem.

Przede wszystkim chciałbym zrozumieć, czy dobrze zrozumiałem te fakty:

  1. Programista pisze jądro i organizuje jego wykonanie w siatce bloków wątków.

  2. Każdy blok jest przypisany do wieloprocesora strumieniowego (SM). Po przypisaniu nie można go migrować do innego SM.

  3. Każda SM dzieli swoje własne bloki na Warps (obecnie o maksymalnym rozmiarze 32 wątków). Wszystkie wątki w warpie są wykonywane jednocześnie na zasobach SM.

  4. Faktyczne wykonanie wątku jest wykonywane przez rdzenie CUDA zawarte w SM. Nie ma określonego mapowania między wątkami i rdzeniami.

  5. Jeśli osnowa zawiera 20 wątków, ale obecnie dostępnych jest tylko 16 rdzeni, osnowa nie będzie działać.

  6. Z drugiej strony, jeśli blok zawiera 48 wątków, zostanie podzielony na 2 wypaczenia i będą one wykonywane równolegle, pod warunkiem, że dostępna jest wystarczająca ilość pamięci.

  7. Jeśli wątek zostanie uruchomiony na rdzeniu, zostanie zatrzymany w celu uzyskania dostępu do pamięci lub długiej operacji zmiennoprzecinkowej, jego wykonanie może zostać wznowione na innym rdzeniu.

Czy mają rację?

Teraz mam GeForce 560 Ti, więc zgodnie ze specyfikacją jest wyposażony w 8 SM, każdy zawierający 48 rdzeni CUDA (łącznie 384 rdzenie).

Moim celem jest upewnienie się, że każdy rdzeń architektury wykonuje SAME instrukcje. Zakładając, że mój kod nie będzie wymagał więcej rejestrów niż te dostępne w każdym SM, wyobrażałem sobie różne podejścia:

  1. Tworzę 8 bloków po 48 wątków, więc każdy SM ma 1 blok do wykonania. Czy w takim przypadku 48 wątków będzie działać równolegle w SM (wykorzystując wszystkie 48 dostępnych dla nich rdzeni)?

  2. Czy jest jakaś różnica, jeśli uruchomię 64 bloki po 6 wątków? (Zakładając, że zostaną one odwzorowane równomiernie między SM)

  3. Jeśli "zanurzę" GPU w zaplanowanej pracy (na przykład tworząc 1024 bloki po 1024 wątki), to rozsądnie jest założyć, że wszystkie rdzenie zostaną użyte w pewnym momencie i wykonają te same obliczenia (zakładając, że wątki nigdy nie przeciągnij)?

  4. Czy istnieje sposób, aby sprawdzić te sytuacje za pomocą profilera?

  5. Czy jest jakieś odniesienie do tych rzeczy? Przeczytałem przewodnik programowania CUDA oraz rozdziały poświęcone architekturze sprzętowej w rozdziałach „Programowanie masowo równoległych procesorów” oraz „Projektowanie i tworzenie aplikacji CUDA”; ale nie mogłem uzyskać dokładnej odpowiedzi.

Daedalus
źródło
W komentarzu chciałbym dodać co to jest „rdzeń CUDA”. „CUDA core” lub „Execution unit” to w pełni potokowe liczby całkowite ALU i FPU, które wykonują jedną instrukcję arytmetyczną na cykl zegara w jednym wątku CUDA.
bruziuz

Odpowiedzi:

123

Dwie najlepsze referencje to

  1. Raport dotyczący architektury NVIDIA Fermi Compute
  2. Recenzje GF104

Spróbuję odpowiedzieć na każde Twoje pytanie.

Programista dzieli pracę na wątki, wątki na bloki wątków i bloki wątków na siatki. Dystrybutor pracy obliczeniowej przydziela bloki wątków do wieloprocesorów przesyłania strumieniowego (SM). Gdy blok wątków jest dystrybuowany do SM, zasoby dla bloku wątków są przydzielane (wypaczenia i pamięć współdzielona), a wątki są dzielone na grupy po 32 wątki zwane wypaczeniami. Po przydzieleniu wypaczenia nazywa się je aktywnym wypaczeniem. Dwa programy planujące wypaczenia wybierają dwa aktywne wypaczenia na cykl i wysyłają wypaczenia do jednostek wykonawczych. Aby uzyskać więcej informacji na temat jednostek wykonawczych i wysyłania instrukcji, patrz 1 str. 7-10 i 2 .

4 ' . Istnieje mapowanie między linią (indeks wątków w osnowie) a rdzeniem.

5 ' . Jeśli wypaczenie zawiera mniej niż 32 wątki, w większości przypadków zostanie wykonane tak samo, jak gdyby miało 32 wątki. Wypaczenia mogą mieć mniej niż 32 aktywne wątki z kilku powodów: liczba wątków na blok nie jest podzielna przez 32, program wykonuje rozbieżny blok, więc wątki, które nie przeszły bieżącą ścieżką, są oznaczane jako nieaktywne lub wątek w wypaczeniu został zakończony.

6 ' . Blok wątku zostanie podzielony na WarpsPerBlock = (ThreadsPerBlock + WarpSize - 1) / WarpSize Nie ma wymogu, aby harmonogramy wypaczania wybierały dwa wypaczenia z tego samego bloku wątku.

7 ' . Jednostka wykonawcza nie zatrzyma się w operacji pamięci. Jeśli zasób nie jest dostępny, gdy instrukcja jest gotowa do wysłania, instrukcja zostanie wysłana ponownie w przyszłości, gdy zasób będzie dostępny. Wypaczenia mogą utknąć na barierach, przy operacjach pamięciowych, operacjach na teksturach, zależnościach danych, ... Zatrzymane wypaczenie nie kwalifikuje się do wybrania przez harmonogram wypaczania. Na Fermi przydatne jest posiadanie co najmniej 2 kwalifikujących się wypaczeń na cykl, aby program planujący wypaczenie mógł wydać instrukcję.

Zobacz odniesienie 2, aby poznać różnice między GTX480 i GTX560.

Jeśli przeczytasz materiał referencyjny (kilka minut), myślę, że zauważysz, że twój cel nie ma sensu. Postaram się odpowiedzieć na Twoje uwagi.

1 ' . Jeśli uruchomisz jądro <<< 8, 48 >>>, otrzymasz 8 bloków, każdy z 2 wypaczeniami po 32 i 16 wątków. Nie ma gwarancji, że te 8 bloków zostanie przypisanych do różnych modułów SM. Jeżeli do SM przydzielone są 2 bloki, możliwe jest, że każdy program planujący odkształcenie może wybrać wypaczenie i wykonać wypaczenie. Będziesz używać tylko 32 z 48 rdzeni.

2 ' . Istnieje duża różnica między 8 blokami po 48 wątków a 64 blokami po 6 wątków. Załóżmy, że twoje jądro nie ma rozbieżności i każdy wątek wykonuje 10 instrukcji.

  • 8 bloków z 48 wątkami = 16 wypaczeń * 10 instrukcji = 160 instrukcji
  • 64 bloki z 6 wątkami = 64 wypaczenia * 10 instrukcji = 640 instrukcji

W celu uzyskania optymalnej wydajności podział pracy powinien być wielokrotnością 32 wątków. Sprzęt nie będzie łączył wątków z różnych wypaczeń.

3 ' . GTX560 może mieć 8 bloków SM * 8 = 64 bloki na raz lub 8 SM * 48 warps = 512 warps, jeśli jądro nie ma maksymalnej liczby rejestrów lub pamięci współdzielonej. W dowolnym momencie część pracy będzie aktywna na SM. Każdy moduł SM ma wiele jednostek wykonawczych (więcej niż rdzenie CUDA). To, które zasoby są używane w danym momencie, zależy od harmonogramów wypaczania i zestawu instrukcji aplikacji. Jeśli nie wykonasz operacji TEX, jednostki TEX będą bezczynne. Jeśli nie wykonasz specjalnej operacji zmiennoprzecinkowej, jednostki SUFU będą bezczynne.

4 ' . Parallel Nsight i pokaz Visual Profiler

za. wykonane IPC

b. wydany IPC

do. aktywne wypaczenia na aktywny cykl

re. kwalifikujące się wypaczenia na aktywny cykl (tylko w nocy)

mi. przyczyny przeciągnięcia warp (tylko w nocy)

fa. aktywnych wątków na wykonaną instrukcję

Profiler nie pokazuje procentu wykorzystania żadnej z jednostek wykonawczych. W przypadku GTX560 zgrubne oszacowanie to IssuedIPC / MaxIPC. Dla MaxIPC załóżmy, że GF100 (GTX480) to 2 GF10x (GTX560) to 4, ale cel to 3 to lepszy cel.

Greg Smith
źródło
1
Dziękuję za Twoją odpowiedź. Przeczytałem odniesienia, ale jest kilka rzeczy, których nie rozumiem w Twojej odpowiedzi. W poniższych pytaniach zakładam, że używamy architektury Fermi z 48 rdzeniami (16 rdzeni * 3 „grupy rdzeniowe”): 1. Wspomniał Pan o mapowaniu między rdzeniami a linią. Jaki to rodzaj mapowania? 2. Z odniesień wynika, że ​​każda „grupa podstawowa” wykonuje co najwyżej pół-wypaczenie (16 wątków) na cykl zegara. Więc teoretycznie, jeśli mamy 48 wątków w tym samym bloku, zostaną one zorganizowane w 3 pół-osnowy i uruchomione równolegle na 48 rdzeniach. Czy mam rację?
Daedalus
1
Rdzenie CUDA to liczba jednostek FP o pojedynczej precyzji. Myślenie o wykonaniu w kategoriach rdzeni CUDA nie jest poprawne. Każda osnowa ma 32 wątki. Wątki te zostaną wydane dla grupy jednostek wykonawczych (np. 16 rdzeni CUDA). Aby wysłać do wszystkich 48 rdzeni w jednym zegarze, jeden z dwóch programów planujących wypaczenie musi wybrać wypaczenie, które spełnia wymagania pary superskalarnej, a obie instrukcje muszą być typu wykonywanego przez rdzenie CUDA. Ponadto inny planista warp musi wybrać warp, którego następna instrukcja zostanie wykonana przez rdzenie CUDA.
Greg Smith
1
Nie ma wymogu, aby wypaczenia znajdowały się w tym samym bloku lub aby wypaczenia w bloku miały ten sam licznik programu.
Greg Smith
2
W twoim przykładzie każdy planista wybiera wypaczenie i wydaje 1 instrukcję. W tym przypadku zostaną użyte tylko 2 grupy jednostek wykonawczych. Aby użyć większej liczby jednostek wykonawczych, 1 programów planujących musi wydać podwójnie. Jak wskazano w odnośnikach, istnieje wiele typów jednostek wykonawczych (nie tylko to, co jest ukutymi rdzeniami cuda) i istnieją reguły parowania instrukcji (niezbyt dobrze udokumentowane), które muszą być spełnione, aby planiści mogli podwójnie wydać.
Greg Smith
1
@GregSmith Przeszukuję całą sieć, aby dowiedzieć się, skąd pochodzi te 8 aktywnych bloków na SM w architekturze Fermi. Nie ma o tym nawet wzmianki w białej księdze Fermi. Czy masz więcej informacji na ten temat?
Greg K.
8

„E. Jeśli warp zawiera 20 wątków, ale obecnie dostępnych jest tylko 16 rdzeni, warp nie będzie działać”.

jest nieprawidłowe. Mylisz rdzenie w ich zwykłym sensie (również w procesorach) - liczba „procesorów wieloprocesorowych” w GPU, z rdzeniami w marketingu nVIDIA („nasza karta ma tysiące rdzeni CUDA”).

Samo wypaczenie może być zaplanowane tylko na jednym rdzeniu (= wieloprocesorowym) i może działać do 32 wątków w tym samym czasie; nie może używać więcej niż jednego rdzenia.

Liczba „48 wypaczeń” to maksymalna liczba aktywnych wypaczeń (wypaczeń, które można wybrać do zaplanowania pracy w następnym cyklu, w dowolnym cyklu) na wieloprocesorowy procesor graficzny nVIDIA z wydajnością obliczeniową 2.x; a ta liczba odpowiada 1536 = 48 x 32 wątki.

Odpowiedz na podstawie tego seminarium internetowego

Andrej
źródło
@GregSmith: Edytowano odpowiedź, aby rozwiązać ten problem. To dobrze, że byłeś cierpliwy, ale - minęło pięć lat ...
einpoklum
pojedynczy rdzeń (= wieloprocesorowy)? Myślę, że pytanie zakłada terminologię jeden rdzeń = procesor, a nie wieloprocesor. Z twoją terminologią twoja odpowiedź jest prawidłowa.
Adarsh