Wstęp do obliczeń równoległych na GPU

Podobne dokumenty
Programowanie procesorów graficznych GPGPU. Krzysztof Banaś Obliczenia równoległe 1

Programowanie procesorów graficznych GPGPU. Krzysztof Banaś Obliczenia równoległe 1

Wykresy i interfejsy użytkownika

Porównanie wydajności CUDA i OpenCL na przykładzie równoległego algorytmu wyznaczania wartości funkcji celu dla problemu gniazdowego

Przygotowanie kilku wersji kodu zgodnie z wymogami wersji zadania,

TABLICA (ang. array) pojedyncza zmienna z wieloma komórkami, w których można zapamiętać wiele wartości tego samego typu danych.

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

Programowanie procesorów graficznych GPGPU


Języki i techniki programowania Ćwiczenia 2

Przykładowe sprawozdanie. Jan Pustelnik

Część XVII C++ Funkcje. Funkcja bezargumentowa Najprostszym przypadkiem funkcji jest jej wersja bezargumentowa. Spójrzmy na przykład.

Programowanie strukturalne i obiektowe. Funkcje

Moc płynąca z kart graficznych

PROGRAMOWANIE WSPÓŁCZESNYCH ARCHITEKTUR KOMPUTEROWYCH DR INŻ. KRZYSZTOF ROJEK

Podstawy programowania. Wykład: 8. Wskaźniki. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Ćwiczenie 4. Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1.

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Podstawy programowania

Podstawy Programowania

Wstęp do Programowania, laboratorium 02

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Wstęp do informatyki. Maszyna RAM. Schemat logiczny komputera. Maszyna RAM. RAM: szczegóły. Realizacja algorytmu przez komputer

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp. Krzysztof Banaś Podstawy programowania 1

Algorytmy dla maszyny PRAM

Algorytm. a programowanie -

część 8 wskaźniki - podstawy Jarosław Gramacki Instytut Informatyki i Elektroniki Podstawowe pojęcia

> C++ dynamiczna alokacja/rezerwacja/przydział pamięci. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

Tesla. Architektura Fermi

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Liczby zmiennoprzecinkowe i błędy

Algorytm i złożoność obliczeniowa algorytmu

Laboratorium 5: Tablice. Wyszukiwanie binarne

Liczby pseudolosowe. #include <stdio.h> #include <stdlib.h> int main() { printf("%d\n", RAND_MAX); return 0; }

Wykład 5: Klasy cz. 3

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Podstawy programowania skrót z wykładów:

ROZPROSZONY SYSTEM DO KRYPTOANALIZY SZYFRÓW OPARTYCH NA KRZYWYCH ELIPTYCZNYCH

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

kodowanienaekranie.pl

CUDA Median Filter filtr medianowy wykorzystujący bibliotekę CUDA sprawozdanie z projektu

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Informatyka I: Instrukcja 4.2

Implementacja sieci neuronowych na karcie graficznej. Waldemar Pawlaszek

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

Program 6. Program wykorzystujący strukturę osoba o polach: imię, nazwisko, wiek. W programie wykorzystane są dwie funkcje:

Programowanie procesorów graficznych NVIDIA (rdzenie CUDA) Wykład nr 1

Algorytmy i język C++

i3: internet - infrastruktury - innowacje

Tablice mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

IX. Wskaźniki.(3 godz.)

BIBLIOTEKA NUMPY, CZĘŚĆ 1

INFORMATYKA W SZKOLE. Podyplomowe Studia Pedagogiczne. Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227

Pliki. Informacje ogólne. Obsługa plików w języku C

Tablice, funkcje - wprowadzenie

W dowolnym momencie można zmienić typ wskaźnika.

Język skryptowy: Laboratorium 1. Wprowadzenie do języka Python

Tablice i struktury. czyli złożone typy danych. Programowanie Proceduralne 1

2 Przygotował: mgr inż. Maciej Lasota

Ćwiczenia z przetwarzania tablic 2D

1 Pierwsze kroki w C++ cz.3 2 Obsługa plików

Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1. Kraków 2013

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Język ludzki kod maszynowy

Elżbieta Kula - wprowadzenie do Turbo Pascala i algorytmiki

Matematyczne Podstawy Informatyki

Budowa komputera. Magistrala. Procesor Pamięć Układy I/O

Plan. krótkie opisy modułów. 1 Uwagi na temat wydajności CPython a. 2 Podstawowe techniki poprawiające wydajność obliczeniową

Język C, tablice i funkcje (laboratorium, EE1-DI)

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

Budowa komputera. Magistrala. Procesor Pamięć Układy I/O

7. Pętle for. Przykłady

Argumenty wywołania programu, operacje na plikach

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

Zaprojektować i zaimplementować algorytm realizujący następujące zadanie.

Układy VLSI Bramki 1.0

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

2. Zmienne i stałe. Przykłady Napisz program, który wypisze na ekran wynik dzielenia 281 i 117 w postaci liczby mieszanej (tj. 2 47/117).

Lab 9 Podstawy Programowania

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Kompletna dokumentacja kontenera C++ vector w -

Podstawy programowania. Wykład: 6. Tablice statyczne. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Sprzęt komputerowy 2. Autor prezentacji: 1 prof. dr hab. Maria Hilczer

Programowanie równoległe i rozproszone. Praca zbiorowa pod redakcją Andrzeja Karbowskiego i Ewy Niewiadomskiej-Szynkiewicz

Język C++ zajęcia nr 2

Laboratorium kryptograficzne dla licealistów 6

Transkrypt:

Spis treści 1 Wstęp do obliczeń równoległych na GPU 1.1 Zadanie 1.2 Profilowanie 1.2.1 Zadanie Wstęp do obliczeń równoległych na GPU W tej części ćwiczeń stworzymy pierwszy program wykorzystujący bibliotekę OpenCL do obliczeń na kartach graficznych. Posłużymy się pythonowym wrapperem do OpenCL - biblioteką PyOpencl. Same kernele będziemy pisać w języku C, ale wszystkie dodatkowe procedury będziemy mogli pisać przy użyciu standardowych poleceń Pythona. Zaczniemy od stworzenia prostego programu sumującego dwie tablice liczb zmiennoprzecinkowych i następnie mnożącego tę sumę przez pewien ustalony mnożnik. Na sam początek importujemy bibliotekę: import pyopencl as cl Kod OpenCL przechowywać będziemy w postaci zmiennej tekstowej. Stworzymy prosty kernel, który dostaje wejściu dwie tablice o wymiarach odpowiednio i oraz zmienną z mnożnikiem. Kernel wykorzystywać będzie jednostek obliczeniowych. Pierwsza tablica zawiera nasze dane wejściowe (ostatni wymiar koduje indeks tablicy wejściowej); druga tablica będzie tablicą w której przechowywać będziemy wynik obliczeń. Przykładowy kernel zdefiniowany jest poniżej: kernels=" kernel void Sum( global float *in, global float *out, const float add) { //zaczynamy od zadeklarowania zmiennych const int n = get_global_id(0); //indeks w pierwszym wymiarze danej jednostki roboczej const int m = get_global_id(1); //indeks w drugim wymiarze danej jednostki roboczej const int M = get_global_size(1); const int nm = n*m + m; // indeks w tablicy wyjsciowej odpowiadajacy wartości o współrzędnych (n,m) private int index1; // indeks w zmiennej wejsciowej odpowiadajacy wartości o współrzędnych (n,m,0) private int index2; // indeks w zmiennej wejsciowej odpowiadajacy wartości o współrzędnych (n,m,1) index1 = 0 + m * 2 + n * M * 2;

" index2 = 1 + m * 2 + n * M * 2; out[nm] = (in[index1]+in[index2]) * add; } Kernel ten dodaje do każdej komórki tablicy wejściowej wartość zmiennej add. W celu wywołania kernela będziemy musieli przygotować środowisko OpenCL, skompilować kod OpenCL (za pomocą odpowiednich funkcji w Pythonie) oraz zarezerwować miejsce w pamięci i przesłać dane do bufora pamięci na GPU. Na początek sprawdzimy jakie procesory mamy do dyspozycji platform = cl.get_platforms() Proszę wypisać zmienną platform. Zależnie od sprzętu i oprogramowania do dyspozycji będziemy mieli jedną lub więcej platform (gdy np. mamy proces Intela i kartę graficzną AMD) wraz z określoną liczbą urządzeń na każdej platformie. Dostępne urządzenia na pierwszej platformie możemy podejrzeć wywołując: print(platform[].get_devices()) Wybierzemy jedno z urządzeń i skompilujemy nasz kernel do obliczeń na tym urządzeniu my_device = [platform[].get_devices()[]] # ta czesc kodu musi byc dostosowana do sprzetu na ktorym sa prowadzone cwiczenia ctx = cl.context(my_gpu_device) queue = cl.commandqueue(ctx, device=my_gpu_device[]) mod = cl.program(ctx,kernels).build() Musimy jeszcze przygotować dane. Na początek zdefiniujemy przykładowe dane - pamiętaj przy tym, że obliczenia wykonywać możemy na liczbach pojedynczej precyzji. inp = np.zeros((300,200,2)) inp[:,:,] = np.arange((300*200)).reshape((300,200)) inp[:,:,1] = np.arange((300*200)).reshape((300,200)) - 100 inp = inp.astype(np.float32) # zmieniamy dane na pojedynczą precyzję out = np.zeros((300,200)).astype(np.float32) add=3.14 add = np.float32(add) Następnie zarezerwujemy pamięć, przenosząc od razu dane wejściowe. Poniższe zmienne stanowią rodzaj wskaźnika do interesujących nas miejsc w pamięci urządzenia. mf=cl.mem_flags

input_buffer = cl.buffer(ctx, mf.read_only mf.copy_host_ptr, hostbuf=inp) output_buffer = cl.buffer(ctx, mf.read_write mf.copy_host_ptr, hostbuf=out) Ponieważ zmienna add występuje jako stała, nie musimy wykonywać dodatkowych operacji rezerwowania pamięci i możemy podać ją bezpośrednio przy wywoływaniu kernela. Jesteśmy już do tego gotowi: event = mod.sum(queue, (np.int32(300), np.int32(200), 1), (1,1,1), input_buffer, output_buffer, add) # jak widać nasz kernel możemy wywołać jako metodę zmiennej mod. #przyjmuje ona na wejściu potok (?) obliczeniowy, całkowite wymiary grup roboczych, #wymiary???? oraz wskaźniki do buforów) Po wywołaniu kernela pozostaje nam tylko przenieść z powrotem dane wynikowe do pamięci hosta (tak aby były one dostępne do dalszych, standardowych obliczeń z poziomu Pythona). event = cl.enqueue_copy(queue, out, output_buffer) Proszę sprawdzić dane wynikowe i porównać je z analogicznymi danymi otrzymanymi z obliczeń liniowych: out_liniowy = (inp[:,:,] + inp[:,:,1])*add Zadanie Proszę stworzyć kernel, który będzie dostawać dwie tablice liczb zmiennoprzecinkowych, a następnie liczyć sufit (ang. ceil) z wartości bezwzględnych elementów obu tablic i zwracać największy wspólny dzielnik elementów tych tablic. Porównać działanie (wyniki) programu z poniższą liniową implementacją: import numpy as np import fractions gcd = np.frompyfunc(fractions.gcd, 2, 1) def funkcja(a,b): a=np.ceil(np.abs(a)) b=np.ceil(np.abs(b)) return gcd(a,b)

Profilowanie Główną przyczyną naszego zainteresowania obliczeniami na GPU jest potencjalny spadek czasu wykonywania wymaganych obliczeń. Z tego punktu widzenia istotna jest możliwość dokładnego określenia czasu obliczeń i porównania algorytmów liniowych z równoległymi. Praktyczny czas trwania obliczeń jest zależny od implementacji sprzętowej, parametrów komputera itp. Nie jest to pojęcie tożsame ze złożonością obliczeniową. Do mierzenia czasu obliczeń liniowych wykorzystamy standardową bibliotekę time. Aby wykonać pojedynczy pomiar czasu działania funkcji y(x) należy wywołać kod poniższej postaci from time import clock start = clock() # wskazanie zegara w danej chwili y(x) end = clock() t=end-start # zmierzony czas Oczywiście, pomiar taki jest obarczony błędem pomiarowym. Z tego względu rozsądne szacunki należy wykonywać w oparciu o dużą liczbę pomiarów. Biblioteka time niestety nie współpracuje dobrze z obliczeniami równoległymi - zawyżając wartość efektywnego czasu obliczeń. Z tego względu, OpenCL dostarcza własnych narzędzi do profilowania. Aby możliwe było profilowanie danej kolejki należy deklarując użyć odpowiedniego parametru: queue=cl.commandqueue(ctx, device=my_gpu_device[], properties=cl.command_queue_properties.profiling_enable) Aby zmierzyć czas wykonywania naszego kernela Sum z wcześniejszej części tekstu, wykonujemy event = mod.sum(queue, (np.int32(300), np.int32(200), 1), (1,1,1), input, output, add) t = (event.profile.end-event.profile.start)*1e-9 # zmierzony czas Zwróćmy uwagę na to, że wynik dotyczy wyłącznie samych obliczeń - nie uwzględnia on czasu potrzebnego na przeniesienie danych z pamięci hosta do pamięci urządzenia i z pamięci urządzenia do hosta. Czas komunikacji między urządzeniami jest względnie długi - sprawia to, że obliczenia równoległe są opłacalne dopiero przy odpowiednio dużych danych wejściowych. W oparciu o poniższy kod możemy zmierzyć, ile czasu trwa skopiowanie zawartości zmiennej in do bufora input w pamięci urządzenia: start_event=cl.enqueue_marker(queue) input = cl.buffer(ctx, mf.read_only mf.copy_host_ptr, hostbuf=in) t = (event.profile.end-start_event.profile.start)*1e-9 # zmierzony czas

Analogicznie zmierzyć możemy czas potrzebny na przeniesienie wyników z pamięci urządzenia do pamięci hosta. Zadanie 1. Dla kilku wybranych rozmiarów tablicy danych wejściowych zmierzyć (liczba powtórzeń >10000) czas potrzebny na: przeniesienie danych z hosta do urządzenia; wykonanie kernela Sum; przeniesienie danych z urządzenia do hosta. Czy czasy te rosną liniowo z rozmiarem tablicy? 2. Zmierzyć (liczba powtórzeń >10000) czas trwania obliczeń funkcji np.add(x,y) dla kilku wybranych rozmiarów tablic danych wejściowych (rozmiary tożsame z rozmiarami z podpunktu pierwszego). Czy wielkość ta zmienia się liniowo z rozmiarem tablicy? 3. Na podstawie powyższego ocenić: (dla konkretnego komputera) od jakiego rozmiaru tablicy zrównoleglenie obliczeń zaczyna być opłacalne?