Programowanie procesorów graficznych GPGPU Krzysztof Banaś Obliczenia równoległe 1
Projektowanie kerneli Zasady optymalizacji: należy maksymalizować liczbę wątków (w rozsądnych granicach, granice zależą też od możliwości sprzętu) globalna liczba wątków jest ograniczona (także indywidualnie dla każdego wymiaru przestrzeni wątków) liczba wątków w grupie jest ograniczona liczba grup jest ograniczona (także indywidualnie dla każdego wymiaru przestrzeni wątków) liczba grup aktywnych jest ograniczona:» przez ograniczoną liczbę rejestrów CU» przez ograniczony rozmiar pamięci wspólnej CU» przez możliwości sprzętu optymalny dobór podziału pracy na wątki może być bardzo złożony i różny dla każdego GPU można próbować rozwiązać problem analitycznie zdarza się, że konieczne są eksperymenty Krzysztof Banaś Obliczenia równoległe 2
Model pamięci OpenCl Krzysztof Banaś Obliczenia równoległe 3
Projektowanie kerneli Zasady optymalizacji: opóźnienie w dostępie do pamięci jest ukrywane przez współbieżne wykonywanie wielu wątków na pojedynczym PE wiele grup wątków na jednym CU bardzo duże grupy wątków (z rozmiarem będącym wielokrotnością warp/wavefront) obowiązują standardowe zasady: usuwanie zależności danych, zasobów redukcja złożoności wyrażeń (strength reduction) operacje / (dzielenie całkowite) i %(modulo) są zazwyczaj kosztowne minimalizacja liczby operacji optymalizacja dostępu do pamięci Krzysztof Banaś Obliczenia równoległe 4
OpenCL zależności zasobów Krzysztof Banaś Obliczenia równoległe 5
Przykład mnożenie macierz wektor Prosta strategia: jeden wątek jeden element wektora wyniku dla dużych wektorów oznacza rozmiar grupy 1 czy dostęp do tablicy M jest optymalny? kernel void mat_vec_1_kernel( const global float* M, uint width, uint height, const global float* V, global float* W) { uint j = get_global_id(0); const global float* row = M + j * width; float dotproduct = 0; for (uint i = 0; i < width; ++i) dotproduct += row[i] * V[i]; W[j] = dotproduct; } Krzysztof Banaś Obliczenia równoległe 6
Przykład mnożenie macierz wektor Modyfikacja: jeden wątek wiele elementów wektora wyniku zwiększenie rozmiaru grupy, liczba grup dobierana dowolnie dostęp do M nie zmieniony kernel void mat_vec_2_kernel( const global float* M, uint width, uint height, const global float* V, global float* W) { for (uint j = get_global_id(0); j < height; j += get_global_size(0)){ const global float* row = M + j * width; float dotproduct = 0; for (uint i = 0; i < width; ++i) dotproduct += row[i] * V[i]; W[j] = dotproduct; } Krzysztof Banaś Obliczenia równoległe 7
Przykład mnożenie macierz wektor Modyfikacja dostępu do M jeden wiersz dla grupy wątków, wiele wierszy na grupę kolejne wątki w grupie czytają kolejne wyrazy M konieczność redukcji kernel void mat_vec_3_kernel(... ) { for (uint j = get_group_id(0); j < height; j += get_num_groups(0)) { const global float* row = M + j * width; float sum = 0.0; for (uint x = get_local_id(0); x < width; x += get_local_size(0)) sum += row[x] * V[x]; // REDUKCJA... } Krzysztof Banaś Obliczenia równoległe 8
Redukcja GPU Redukcja naiwna redukcja wątki zapisują wynik do tablicy pojedynczy wątek sumuje wyrazy tablicy konieczność synchronizacji działania redukcja właściwa rozmaite warianty wykorzystania drzewa redukcji Krzysztof Banaś Obliczenia równoległe 9
Redukcja GPU Krzysztof Banaś Obliczenia równoległe 10
Redukcja GPU Wariant 1: standardowe drzewo redukcji partialdotproduct[get_local_id(0)] = sum; for (uint stride = 1; stride < get_local_size(0); stride *= 2) { barrier(clk_local_mem_fence); uint index = 2 * stride * get_local_id(0); if (index < get_local_size(0)) { partialdotproduct[index] += partialdotproduct[index + stride]; } } Krzysztof Banaś Obliczenia równoległe 11
GPU memory banks Krzysztof Banaś Obliczenia równoległe 12
Redukcja GPU Krzysztof Banaś Obliczenia równoległe 13
Redukcja GPU Wariant 2: optymalny dostęp do pamięci lokalnej (wspólnej) kolejne wątki uzyskują dostęp do kolejnych komórek pamięci partialdotproduct[get_local_id(0)] = sum; for (uint stride = get_local_size(0)/2; stride > 0; stride /= 2) { barrier(clk_local_mem_fence); if (get_local_id(0) < stride) { partialdotproduct[get_local_id(0)] += partialdotproduct[get_local_id(0)+stride]; } } ciąg dalszy kodu iloczynu macierz wektor if (get_local_id(0) == 0) W[y] = partialdotproduct[0]; barrier(clk_local_mem_fence); Krzysztof Banaś Obliczenia równoległe 14