Pokročilé architektury počítačů

Podobné dokumenty
Pokročilé architektury počítačů

Pokročilé architektury počítačů

Obecné výpočty na GPU v jazyce CUDA. Jiří Filipovič

CUDA J. Sloup a I. Šimeček

GPGPU. Jan Faigl. Gerstnerova Laboratoř pro inteligentní rozhodování a řízení České vysoké učení technické v Praze

Úvod do GPGPU J. Sloup, I. Šimeček

GPU a CUDA. Historie GPU. Co je GPGPU? Nvidia CUDA

Nvidia CUDA Paralelní programování na GPU

GPU A CUDA HISTORIE GPU CO JE GPGPU? NVIDIA CUDA

Optimalizace pro GPU hardware

GPU a CUDA. Historie GPU. Co je GPGPU? Nvidia CUDA

Nvidia CUDA Paralelní programování na GPU

OPS Paralelní systémy, seznam pojmů, klasifikace

Procesy a vlákna (Processes and Threads)

GPGPU Aplikace GPGPU. Obecné výpočty na grafických procesorech. Jan Vacata

Přehled paralelních architektur. Dělení paralelních architektur Flynnova taxonomie Komunikační modely paralelních architektur

Co je grafický akcelerátor

Základy informatiky. 2. Přednáška HW. Lenka Carr Motyčková. February 22, 2011 Základy informatiky 2

PŘEDSTAVENÍ GRAFICKÉHO PROCESORU NVIDIA G200

Základní operace. Prefix sum. Segmentovaný prefix-sum

Operační systémy. Správa paměti (SP) Požadavky na SP. Spojování a zavedení programu. Spojování programu (linking) Zavádění programu (loading)

Jan Nekvapil ČESKÉ VYSOKÉ UČENÍ TECHNICKÉ V PRAZE Fakulta elektrotechnická

Paralelní programování

Obsah. Kapitola 1 Hardware, procesory a vlákna Prohlídka útrob počítače...20 Motivace pro vícejádrové procesory...21

Přednáška. Vstup/Výstup. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

Metody připojování periferií

Adresní mody procesoru

Operační systémy. Přednáška 7: Správa paměti I

Přednáška 1. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

Paměťový podsystém počítače

Přednáška. Správa paměti I. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

GPU Computing.

Procesor. Procesor FPU ALU. Řadič mikrokód

Strojový kód k d a asembler procesoru MIPS SPIM. MIPS - prostředí NMS NMS. 32 ks 32bitových registrů ( adresa registru = 5 bitů).

Řešíme úlohu zpracování velkého množství dat. Data jsou symetrická, úloha je dobře paralelizovatelná

Principy operačních systémů. Lekce 5: Multiprogramming a multitasking, vlákna

Metody připojování periferií

Paralelní a distribuované výpočty (B4B36PDV)

VYSOKÉ UČENÍ TECHNICKÉ V BRNĚ BRNO UNIVERSITY OF TECHNOLOGY

Sběrnicová struktura PC Procesory PC funkce, vlastnosti Interní počítačové paměti PC

Řešíme úlohu zpracování velkého množství dat. Data jsou symetrická, úloha je dobře paralelizovatelná. Propaganda výrobců grafických karet:

Real Time programování v LabView. Ing. Martin Bušek, Ph.D.

VYSOKÉ UČENÍ TECHNICKÉ V BRNĚ BRNO UNIVERSITY OF TECHNOLOGY

Paralelní výpočty ve finančnictví

Operační systémy. Jednoduché stránkování. Virtuální paměť. Příklad: jednoduché stránkování. Virtuální paměť se stránkování. Memory Management Unit

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ

Princip funkce počítače

REALIZACE SUPERPOČÍTAČE POMOCÍ GRAFICKÉ KARTY

Vlákno (anglicky: thread) v informatice označuje vlákno výpočtu neboli samostatný výpočetní tok, tedy posloupnost po sobě jdoucích operací.

Přednáška. Správa paměti II. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

Vláknové programování část I

Představení a vývoj architektur vektorových procesorů

Charakteristika dalších verzí procesorů v PC

Procesor z pohledu programátora

Opakování programování

Architektura a koncepce OS OS a HW (archos_hw) Architektura a koncepce OS Jádro OS (archos_kernel) Architektura a koncepce OS Typy OS (archos_typy)

Architektury paralelních počítačů II.

VYSOKÉ UČENÍ TECHNICKÉ V BRNĚ BRNO UNIVERSITY OF TECHNOLOGY

Architektury paralelních počítačů I.

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

Počítač jako elektronické, Číslicové zařízení

Struktura programu v době běhu

Pohled do nitra mikroprocesoru Josef Horálek

MS WINDOWS II. Jádro. Správa objektů. Správa procesů. Zabezpečení. Správa paměti

vlastnosti, praktické zkušenosti

Struktura a architektura počítačů (BI-SAP) 11

Architektura Intel Atom

Profilová část maturitní zkoušky 2017/2018

Intel (2) Intel (1) Intel (3) Intel (4) Intel (6) Intel (5) Nezřetězené zpracování instrukcí:

8 Třídy, objekty, metody, předávání argumentů metod

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

SUPERPOČÍTAČE DANIEL LANGR ČVUT FIT / VZLÚ

Počítač jako prostředek řízení. Struktura a organizace počítače

Systém adresace paměti

09. Memory management. ZOS 2006, L.Pešička

Mezipaměti počítače. L2 cache. L3 cache

CHARAKTERISTIKA MODERNÍCH PENTIÍ. Flynnova klasifikace paralelních systémů

Pokročilé architektury počítačů

Operační systémy. Přednáška 1: Úvod

Paralelní programování

Architektura procesoru ARM

Sběrnicová struktura PC Procesory PC funkce, vlastnosti Interní počítačové paměti PC

Knihovny pro CUDA J. Sloup a I. Šimeček

Výpočet v módu jádro. - přerušení (od zařízení asynchronně) - výjimky - softvérové přerušení. v důsledku událostí

Činnost CPU. IMTEE Přednáška č. 2. Několik úrovní abstrakce od obvodů CPU: Hodinový cyklus fáze strojový cyklus instrukční cyklus

Disková pole (RAID) 1

Programování v jazyce C a C++

PROCESOR. Typy procesorů

Procesor. Základní prvky procesoru Instrukční sada Metody zvýšení výkonu procesoru

Základní principy konstrukce systémové sběrnice - shrnutí. Shrnout základní principy konstrukce a fungování systémových sběrnic.

Paralelní programování

Jiné výpočetní platformy J. Sloup, M. Skrbek, I. Šimeček

Generátor efektivního kódu fúzovaných CUDA kernelů

Řízení IO přenosů DMA řadičem

Paralení programování pro vícejádrové stroje s použitím OpenMP. B4B36PDV Paralelní a distribuované výpočty

Správné vytvoření a otevření textového souboru pro čtení a zápis představuje

Maturitní otázky z předmětu PROGRAMOVÁNÍ

VYUŽITÍ GRAFICKÉHO ADAPTÉRU PRO OBECNÉ VÝPOČTY GENERAL-PURPOSE COMPUTATION USING GRAPHICS CARD

Pár odpovědí jsem nenašla nikde, a tak jsem je logicky odvodila, a nebo jsem ponechala odpověď z pefky, proto je možné, že někde bude chyba.

Transkript:

Pokročilé architektury počítačů Přednáška 7 CUDA První paralelní aplikace Martin Milata

Obsah SIMD versus SIMT Omezení a HW implementace Způsob zpracování warp skupiny vláken CUDA - pohled programátora Připomenutí výpočetního modelu Práce s pamětí Kompilace a Runtime Optimalizace programu Důsledky výpočetního modelu Přístup do paměti

SIMD versus SIMT Single-Instruction, Multiple-Data (SIMD) Jedna instrukce je vykonávána na množině dat (obvykle vektor) stejným způsobem Neumožňuje divergenci cest při zpracování (jeden PC, jednotná sdílená sada registrů,...) Vektorové procesory (zpracování multimediálních dat), GPU Rozšíření x86 architektury: MMX, 3DNow!, SSEx, AVX, AltiVec Single-Instruction, Multiple-Thread (SIMT) Architektura navržená k vytváření, spravování, plánování a provádění skupin vláken (warp) Všechny vlákna ve warp startují na stejné adrese. V dalším provádění a případné divergenci cest jsou vzájemně nezávislé Rozdíl oproti čistě SIMD architektuře Vyžaduje přítomnost PC a oddělení sady registrů per vlákno

Single-Instruction, Multiple-Thread (SIMT) Při tvorbě programu se předpokládá nezávislost běhu všech vláken CUDA části programu Každé vlákno obsahuje stejné prováděné tělo instrukcí Kód vláken může obsahovat větvení programu a tím způsobit divergenci cest při provádění Vlákna jsou nezávislá s vlastním PC, registry,... divergence je možná Úplná nezávislost a oddělenost zpracování vláken není v CUDA architektuře z hardwarového pohledu dodržena Vlákna jsou ke zpracování předávána ve warp skupinách Instrukce warp provádí stream processor (SP) obdoba funkční jednotky Z pohledu SW jsou plně nezávislá s tím, že HW omezení ve warp jsou před programátorem skryta a řešena transparentně při vykonávání

Single-Instruction, Multiple-Thread (SIMT) Přepínání kontextů vláken Veškeré informace kontextu vlákna jsou udržována po dobu jeho běhu přímo v HW (program counter, registry, ) Přepnutí kontextu vlákna lze provést při vydávání instrukce má zanedbatelnou latenci Plánovač může warp instrukce plánovat nezávisle na jejich příslušnosti k různým warp skupinám vláken (přepne kontext). Instrukce volí na základně jejich připravenosti k provádění Omezení v rámci warp Zpracování je více podobné SIMD Warp obsahuje 32 instrukcí vydaných pro SP (stream processor) Zpracování 32 souvisejících datových polí je dané

SIMT a warp instrukce 32 datových položek je zpracováváno v rámci jedné warp instrukce SIMD přístup na úrovni HW pro jednu warp instrukci Každé vlákno disponuje vlastními zdroji definujícími jeho kontext (PC, registry, ), jeho instrukce je ale vydaná současně s 31 dalšími vlákny v rámci jedné warp instrukce Teoreticky možná divergence 32 nezávislých cest Společné vykonávání všech vláken po dobu jednotné cesty kódem programu (všechny vlákna zpracovávají v jeden okamžik stejnou instrukci) Nejefektivnější provádění warp instrukce

SIMT a warp instrukce Divergence cest v rámci warp Cesta vykonávání programu jednotlivých vláken se rozdělí (větvením programu) Warp instrukce není proveditelná najednou nutná serializace divergujících cest Střídavé omezení vykonávání vláken různých cest Snaha o minimalizaci divergence na úrovni warp instrukcí Výkonnostní dopad Koherence řídícího toku Warp vlákna jdou stejnou cestou vykonávání programu

Co je to CUDA? Compute Unified Device Architecture Umožňuje použít standardní programovací jazyk C k realizaci výpočtů na GPU Nevyžaduje znalosti standardního grafického API nebo hlubší znalost obecné problematiky programování pro/na GPU Jednoduchý start s viditelným reálným výkonnostním přínosem NVIDIA provádí návrh a vývoj potřeba NVIGIA GPU (GeForce 8xxx/Tesla/Quadro) Stabilní, dostupné (bezplatné), dokumentované a podporované řešení Podporováno jak pro Windows tak pro Linux

Proč CUDA? Jedná se prakticky o nejmodernějších API pro tzv. stream computing CUDA lze považovat za lídra v této oblasti Dostupnost CUDA enable zařízení Umožňuje využívat maximální výkon HW díky známé struktuře, výkonu a typů pamětí zpřístupnění různých typů pamětí programátorovi zaměření pro specifický HW, který je možné efektivně využívat Alternativní GPGPU programování umožňuje efektivní práci s GPU jen pomocí standardního grafického API Garantuje podporu pro budoucí verze HW

Máte CUDA enabled zařízení? Seznam není aktuální. Dnes rozsahem překračuje možnosti jedné stránky. Převzato z: http://www.nvidia.com/object/cuda_gpus.html (z roku 2010)

Výpočetní model GPU je v roli koprocesoru CPU počítače Určeno pro výpočet relativně přímočarého a masivně paralelizovatelného kódu Mnohokrát prováděné funkce nad nezávislými daty - např. tělo smyčky for Kompilát funkce (těla smyčky) pro grafický procesor se nazývá kernel Kernel se na GPU provádí jako množina vláken, které v omezené míře sdílejí alokované prostředky globální paměť, paměti konstant a textur, sdílená paměť v rámci bloku vláken CPU i GPU využívá, spravuje a přistupuje ke své paměti. Data mohou být mezi oběma prostory kopírována Přímý přístup do cizí paměti není možný Kopírování dat mezi pamětmi je časově náročná operace Vyžaduje účast sběrnice mezi CPU a GPU (obvykle PCI Express)

Grid, blok a vlákno Kernel umístěn ve výpočetním gridu Výpočetní grid se dále dělí na bloky vláken Jednotlivá vlákna provádějí operace kernel funkce Dimenze gridu a bloku je aplikačně konfigurovatelná Grid může být definován jako 1, 2 nebo 3 dimenzionální Maximální velikost je závislá na velikosti paměti a složitosti kernelu Každý blok má jednoznačný identifikátor v rámci gridu (block ID) každé vlákno má jednoznačný identifikátor v rámci bloku (thread ID)

Příklad: Sčítání matic // Kernel definition void MatAdd (float* A, float* B, float* C, int N) { for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j){ C[i][j]=A[i][j]+B[i][j]; int main() {... // Kernel invocation MatAdd(a, b, c, N); // Kernel definition global void MatAdd (float* A, float* B, float* C, int N) { int i = blockidx.x * blockdim.x + threadidx.x; int j = blockidx.y * blockdim.y + threadidx.y; if (i < N && j < N) C[i][j] = A[i][j] + B[i][j]; int main() {... // Kernel invocation dim3 threadsperblock(16, 16); dim3 numblocks(n / threadsperblock.x, N / threadsperblock.y); MatAdd<<<numBlocks, threadsperblock>>>(a,b,c);

Přístup k paměti Oddělené paměťové prostory hlavní paměť GPU paměti Explicitní alokace a dealokace GPU paměti cudamalloc() a cudafree() Ukazatelé do GPU paměti Kopírování dat mezi hlavní a GPU pamětí Pomalá operace cudamemcpy() Optimalizace práce s pamětí Minimalizace pomalých operací Použití pamětí využívající cache nebo rychlých pamětí

API Design Minimální rozšíření jazyka C Kvantifikátory funkcí a proměnných Direktiva provádění CUDA kernelu Vestavěné datové typy Runtime knihovny Komponenty pro uživatelský přístup a řízení GPU (ovládání částí programu běžící na CPU) Komponenty pro práci na GPU Společné komponenty Vestavěné datové typy, podmnožina standardních C knihoven Zdrojový kód musí být kompilován CUDA kompilátorem (nvcc)

Kompilátor NVCC CUDA kernel je obvykle uložen v souboru s příponou.cu NVCC používá standardní kompilátor k překladu kódu, který má být zpracován CPU NVCC nepodporuje mnohá rozšíření Mnoho STL jako např. iostream nelze použít Jak na velké projekty? Pomocí NVCC se provede kompilace kernelu a jeho volání z CPU (soubor.cu) Hlavičkový soubor s voláním kernelu z CPU se použije ve zdrojovém kódu

GPU Runtime Rozšíření dostupná pouze na GPU Méně přesné akcelerované matematické funkce syncthreads() pozastaví vykonávání dokud všechny vlákna v bloku nedosáhnou synchronizačního bodu Konverzní funkce s rozšířenými možnostmi zaokrouhlování Funkce konverze a reinterpretace datových typů Funkce pro práci s texturami Atomické funkce Garantují provedení read-modify-write atomické operace s atributem v paměti bez možnosti interference s jiným vláknem (operand je výlučně zamčen). Pouze zařízení revize 1.1 a vyšší

CPU Runtime Rozšíření dostupná na CPU Správa zařízení Práce s profily zařízení, správa multi-gpu prostředí,... Správa paměti cudamalloc(), cudamemcpy(), cudafree(),... Správa Textur Práce s pamětí textur z pohledu CPU,... Interoperabilita s OpenGL a DirectX Mapování globální paměti na OpenGL buffery Asynchronní souběžné provádění Řízení je vráceno na CPU dříve než GPU dokončí provádění Low-level (driver) API

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Stanovení velikosti vektoru Počet prvků vektoru (10) Nastavení počtu vláken bloku Maximální počet vláken v bloku (4) Důsledek je popis velikosti gridu int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); Počet prvků / Počet vláken bloku for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Definice CUDA kernelu Realizuje paralelní výpočet druhých mocnin prvků vektoru na GPU float *a ukazatel do paměti GPU zařízení int N skutečný počet prvků argument předaný hodnotou int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Definice CUDA kernelu Realizuje paralelní výpočet druhých mocnin prvků vektoru na GPU float *a ukazatel do paměti GPU zařízení int N skutečný počet prvků argument předaný hodnotou int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Alokace a inicializace vektoru v hlavní paměti ( paměť CPU ) Vektor a_h velikosti N float hodnot je k dispozici v paměti CPU size jako celková velikost pole pro uložení N float hodnot bude použita i při následné alokaci GPU paměti int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Alokace a kopírování hodnot vektoru do paměti GPU Vektor a_d velikosti N float hodnot je alokován v paměti GPU Při kopírování pamětí je specifikován a_d cíl a a_h zdroj přenosu, size velikost kopírované oblasti a cudamemcpyhosttodevice směr přenosu int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Výpočet počtu bloků vláken, do kterých bude provádění rozprostřeno Proměnná n_blocks definuje počet bloků v rámci gridu v 1D uspořádání Případný nenulový zbytek celočíselného podílu počtu prvků a počtu vláken v bloku vynutí alokaci bloku navíc (nebude plně využit) int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Volání provádění CUDA kernelu Za voláním funkce kernelu následují parametry direktivy volání n_blocks počet bloků a block_size jejich velikost (počet vláken v bloku) Vektor a_d je předán jako ukazatel do paměti GPU, následuje konstantní N int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, sizeof(float)*n, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Kopírování hodnoty vypočtem modifikovaného vektoru zpět do paměťového prostoru CPU Při kopírování pamětí je opět specifikován a_h cíl a a_d zdroj přenosu, size velikost kopírované oblasti a cudamemcpydevicetohost směr přenosu int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, size, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Zobrazení výsledku int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, size, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Příklad: Druhá mocnina prvků vektoru const int N = 10; const int blocksize = 4; global void square_array(float *a,int N) { int idx=blockidx.x*blockdim.x+threadidx.x; if (idx<n) a[idx] = a[idx] * a[idx]; Úklid dynamicky alokovaných pamětí před ukončením provádění programu Uvolnění alokované paměti v prostoru GPU pomocí cudafree(a_d) Uvolnění paměti v prostoru CPU pomocí free(a_h) int main(void) { float *a_h; const size_t size = N*sizeof(float); a_h = (float *)malloc(size); for (int i=0; i<n; i++) { a_h[i] = (float)i; float *a_d; cudamalloc((void **) &a_d, size); cudamemcpy(a_d, a_h, size, cudamemcpyhosttodevice); int n_blocks = N/block_size + (N%block_size == 0? 0:1); square_array <<<n_blocks, block_size>>>(a_d,n); cudamemcpy(a_h, a_d, size, cudamemcpydevicetohost); for (int i=0; i<n; i++) { printf("%d %f\n", i, a_h[i]); cudafree(a_d); free(a_h);

Vykonávání vláken Struktura GPU Obsahuje N multiprocesorů Každý multiprocesor obsahuje M skalárních procesorů Každý multiprocesor zpracovává skupiny bloků vláken Blok vláken může běžet jen na jednom multiprocesoru Bloky jsou rozděleny do skupin vláken tzv. warp Warp je prováděn paralelně V současné době obsahuje 32 vláken Vlákna jsou ve warp řazena pokud možno se sekvenčně vzrůstajícím threadid Plánovač přepíná provádění mezi warp instrukcemi

Důsledky výpočetního modelu Optimalizace rozměrů gridů a bloků Počet bloku / Počet multiprocesorů > 1 Všechny multiprocesory mohou být využity Počet vláken v bloku < 32 Warp instrukce nemá potenciálně dost vláken Počet bloku / Počet multiprocesorů > 2 Více bloků bude prováděno na multiprocesoru současně Zdroje per blok celkově dostupné zdroje Sdílená paměť a registry Bloky mohou být na multiprocesorech prováděny souběžně Předcházení divergence cest při zpracování skoků v rámci warp Pokles výkonu serializací divergujících cest Blok se skupinami N*32 vláken s nedivergujícími cestami

Přístup do paměti Přístup k rychlým pamětem registry, sdílená paměť, přístup ke stejné adrese paměti konstant (používá cache) Přístup do paměti textur Používá cache pro akceleraci přístupu Optimalizována pro 2D přístup Princip 2D lokality a prostorová cache Možnost efektivního čtení sousedních adres Není potřeba řešit slučování přístupů do paměti Globální paměť 4 cykly potřebné pro zpracování instrukce přístupu do paměti 400-800 cyklů následná latence paměti (čas pro vyřízení požadavku) Snižování počtu samostatných požadavků pomocí techniky jejich slučování

Akcelerace přístupu do globální paměti Akcelerace založená na technice slučování samostatných požadavků jednotlivých vláken warp Realizuje se pouze v rámci warp Vyžaduje přístup k souvislé a zarovnané oblasti v paměti 128 bytes float nebo int 256 bytes float2 neho int2 512 bytes float4 nebo int4 Warp base address (WBA) musí být násobkem 16*sizeof(type) MMU obsluhuje tzv. Half warp Vektory o N prvcích k-té vlákno může přistupovat pouze k prvku na adrese WBA + k Všechny vlákna se nemusí účastnit přístupu do paměti Omezení platí jak pro čtení tak pro zápis

Slučitelné přístupy do paměti všechna vlákna přistupují k prvkům podle vzoru k-té vlákno k-tý prvek všechny vlákna warp se přístupu do paměti neúčastní vzorec k-té vlákno k tý prvek je zachován

Neslučitelné přístupy do paměti Při přístupu není dodržen vzor k-té vlákno k-tý prvek Sloučení není možné kvůli nezarovnané bázové adrese Nezarovnaná velikost prvků float3 nebo int3

Slučování přístupu do paměti revize architektury 1.2 a vyšší Pokročilejší MMU umožňuje zmírnit požadavky na sloučení přístupu do paměti Sloučení je nyní možné pro jakýkoliv vzor přístupu do paměti, který lze realizovat v rámci bloku velikosti: 32 B 8 bit slovo 64 B 16 bit slovo 128 B 32 bit nebo 64 bit slovo Použití menších transakcí pro předcházení plýtvání pásma vlivem přenosu nepoužívaných slov

HW mírnění dopadu neslučitelných přístupů do globální paměti V zařízeních CUDE revize 2.0 a vyšších je nahrazena grafická cache paměť tzv. pravou hierarchií cache pamětí Hierarchie cache pamětí srovnatelná s hierarchií CPU Adresované bloku jsou ukládány v L1 a L2 cache Latence globální paměti mírněná latenci L1 resp. L2 cache Stále užitečné slučování požadavků na přístup do globální paměti Mnoho zařízení starší revize (< 2.0) Průměrná latence při náhodném přístupu je stále horší než při optimalizaci a slučování požadavků

Kam s načtenými daty Data z sloučeného požadavku na přístup do globální paměti mohou být uložena v malém alokovaném prostoru ve sdílené paměti ve sdílené paměti dále transformována a přeskládána do potřebné podoby pro další zpracování Sloučené načtení bloku a jeho následná úprava ve sdílené paměti je rychlejší než separované čtení prvků z globální paměti (např. transponování matic) Omezená velikost rychlých pamětí vyžaduje promyšlený přístup k práci s pamětí Nutnost rozdělovat počáteční velkou kolekci dat do malých oblastí, s následným výpočtem nad ní (submatice).

Proč CUDA a ne OpenGL/DirectX Výhody Není potřeba zvládnout API grafické karty Snazší práce s pamětí Dostupná dokumentace Nevýhody Specializace na zařízení (grafické čipy) jednoho výrobce CUDA není schopna využít absolutně všechnu výpočetní sílu čipu

Závěr SMID není SMIT HW architektura SMIT modelu CUDA a programování CUDA API První paralelní aplikace Základní optimalizace Velikosti gridu a bloku Práce s pamětí

Literatura D. Kanter: NVIDIA's GT200: Inside a Papallel Processor NVIDIA CUDA C Programming Guide Johan Seland: CUDA Programming Paul H. J. Kelly, Advanced Computer Architecture Lecture notes 332 P. N. Glaskowsky: NVIDIA s Fermi: The First Complete GPU Computing Architecture Internetové zdroje: http://www.nvidia.com/ http://gpgpu.org/