Εισαγωγή στον GPGPU προγραμματισμό Ηλίας K. Σάββας Καθηγητής Τμ. Μηχ. Πληροφορικής ΤΕ, ΤΕΙ Θεσσαλίας savvas@teilar.gr 50,000,000 Χρόνια που μας πήρε να φτάσουμε αυτό τον αριθμό χρηστών κάποιας υπηρεσίας: 50,000,000 (1 από 2) 1
50,000,000 (2 από 2) Χρεωστικές κάρτες (Debit cards): 12 χρόνια Κινητή τηλεφωνία (Cell phone): 12 χρόνια Internet: 7 χρόνια PayPal: 5 χρόνια ipods: 4 χρόνια YouTube: 4 χρόνια Facebook: 3 χρόνια Twitter: 2 χρόνια Angry birds (παιχνίδι): 35 ημέρες Αλλάζουμε ταχύτατα 2
Τι προωθεί την τεχνολογία? ΟΙΚΟΝΟΜΙΑ!!! Και τα παιχνίδια είναι ένας κυρίαρχος μοχλός κίνησης της οικονομίας ΚΑΡΤΕΣ ΓΡΑΦΙΚΩΝ Περιεχόμενα Ιστορικά στοιχεία. Nvdia, AMD. To μοντέλο προγραμματισμού GPU. Εισαγωγή στην CUDA. Παραδείγματα. OpenACC. Ιστορικά στοιχεία (1 από 2) GPU (Graphics Processing Unit): Σχεδιασμένη για να επιτυγχάνει ταχύτατα διαχείριση και αλλαγή της μνήμης με τέτοιο τρόπο ώστε να επιταχύνει την δημιουργία εικόνων σε ένα frame buffer με έξοδο την οθόνη. Ο όρος από την NVDIA το 1999 GeForce 256: H πρώτη GPU 3
Ιστορικά στοιχεία (2 από 2) GPGPU (General Purpose GPU): Εκτελεί υπολογισμούς που συνήθως αναλάμβανε η CPU Μπορεί να εμπεριέχει χιλιάδες πυρήνες σε αντίθεση με την CPU (το πολύ 12 πυρήνες) Μαζικά παράλληλη επεξεργασία (όχι μόνο για γραφικά) Προγραμματιστικά εργαλεία: Nvidia CUDA (Compute Unified Device Architecture): High Performance Computing (2001) Microsoft: DirectCompute Apple/Kchronos: OpenCL (Open Computing Language) GPU Chip σε computer video cards, PlayStation 3, XBOX κλπ Δύο βασικοί κατασκευαστές: NVIDIA και AMD. NVDIA Desktop GPUs: Η σειρά GeForce για CPU Mobile GPUs: H σειρά GeForce για Mobile Workstation/HPC GPUs: Quadro NVS, Tesla, Kepler 4
ΝVDIA GPU: (1 από 3) Υποστηρίζει CUDA και OpenCL: Fermi(Tesla version) : Έως και 512 πυρήνες Έως και 6GB μνήμη Έως και 665 GFLOPS Double precision Kepler το 2012 Maxwell το 2014 NVDIA GPU CLOUD: GPU πλατφόρμα, συμβατή με όλους τους τύπους ΗΥ. Κατάλληλη για εφαρμογές νευρωνικών δικτύων. ΝVDIA GPU: (2 από 3) GEFORCE GTX 1080: NVIDIA CUDA cores: 2560 Ταχύτητα μνήμης: 10Gbps Bandwidth μνήμης: 320 GB/sec Τιμή: 549$ (8/2017) https://www.youtube.com/watch?v=-p28lkwtzri ΝVDIA GPU: (3 από 3) Σε αυτόν τον υπολογιστή: GeForce 920M: Μνήμη: 3GB DDR3 Εύρος ζώνης μνήμης: 14.40 GB/s OpenGL, CUDA Cores: 384 5
AMD GPU: Desktop GPUs: Η σειρά Radeon Mobile GPUs: Mobility Radeon Workstation GPUs: FirePro, FireStream Υποστηρίζουν OpenCL αλλά όχι CUDA Συγκριτικά Ένας τυπικός υπέρυπολογιστής: Κατανεμημένη μνήμη, Πολύ-πύρηνος (12 ανά κόμβο) Ταχύτατο δίκτυο Κατενεμημένος προγρ/μός: MPI Υβριδικός προγ/μός: MPI με OpenMP, Pthreads GPU: Κατανεμημένη μνήμη, >1000 πυρήνες Υπέρ-Ταχύτατο δίκτυο Παράλληλος προγ/μός: CUDA, OpenCL Υβριδικός προγ/μός: MPI με OpenMp με CUDA/OpenCL CPU vs GPU (1 από 2) H GPU είναι σχεδιασμένη για compute-intensive παράλληλο προγραμματισμό. Η GPU χρησιμοποιεί τα περισσότερα transistors για επεξεργαστικούς σκοπούς παρά για cash και flow control. 6
CPU vs GPU (2 από 2) GPGPU προγ/μός Δεν είναι κατάλληλος για όλα τα είδη των παράλληλων μοντέλων, κυρίως για SPMD: Single Program Multiple Data Δυνατό σημείο: Compute intensive computing Η μεταφορά δεδομένων μεταξύ CPU και GPU μπορεί να δημιουργήσει πρόβλημα (bottleneck) Δουλεύοντας μαζί (CPU + GPU) 1. Μεταφορά των προς επεξεργασία δεδομένων από την RAM στην μνήμη της GPU 2. Η CPU τροφοδοτεί την GPU με οδηγίες (πρόγραμμα) 3. Η GPU εκτελεί τις οδηγίες παράλληλα στους διαθέσιμους πυρήνες της 4. Μεταφορά των αποτελεσμάτων στην RAM 7
Ένα πρώτο παράδειγμα global void mykernel() { printf("hello World from GPU!\n"); int main() { printf("hello World From CPU!\n"); mykernel<<<1,10>>>(); return 0; Μεταγλώττιση και εκτέλεση $ nvcc -arch sm_20 hello.cu -o hello $./hello Hello World from CPU! Ορολογία Thread: είναι ένα έτοιμος για εκτέλεση επιμέρους κώδικας. Κάθε thread έχει τα δικά του instruction address counter και register state. Warp: μία ομάδα από 32 παράλληλα threads. Block: μία ομάδα από Warps. Ένα block εκτελείται σε ένα multiprocessor. Κάθε block έχει την δική του διαμοιραζόμενη μνήμη και registers στον multiprocessor. Grid: μία ομάδα από Blocks. Host: είναι ότι έχει σχέση με την CPU στις εφαρμογές CUDA. Device: είναι ότι έχει σχέση με την GPU στις εφαρμογές CUDA. 8
To μοντέλο προγ/μού GPU (1 από 2) H GPU ονομάζεται device ενώ η CPU host. Ο κώδικας για την GPU (kernel) εκτελείται στο device από πολλά threads. Τα threads ομαδοποιούνται σε thread blocks. Ο κώδικας γράφεται από την σκοπιά ενός μοναδικού thread. Το κάθε thread εκτελεί μία μοναδική διαδρομή του κώδικα (αυτό μπορεί να προκαλέσει προβλήματα επίδοσης). To μοντέλο προγ/μού GPU (2 από 2) Δέσμευση μνήμης στη GPU. Αντιγραφή δεδομένων από τη μνήμη της CPU στη μνήμη της GPU. Κλήση του CUDA kernel να εκτελέσει ένα συγκεκριμένο υπολογισμό. Αντιγραφή των δεδομένων (αποτέλεσμα του υπολογισμού) από τη μνήμη της GPU πίσω στη μνήμη της CPU. Απελευθέρωση στης μνήμης στη GPU. Ιεραρχία των threads Threads: μοναδικά στο block που ανήκουν 3d αναγνωριστικά. Blocks: μοναδικά στο Grid που ανήκουν 3d αναγνωριστικά H διάσταση με την εκκίνηση του kernel. Built-in device μεταβλητές: threadidx, blockidx, blockdim, griddim. 9
Εισαγωγή στην CUDA Compute Unified Device Architecture CUDA: επέκταση των γλωσσών C και C++ ώστε να έχουν την δυνατότητα προγραμματισμού της GPU. Απαιτεί: Υποστηριζόμενο ΛΣ gcc, cl.exe - Visual Studio, Clang - Xcode CUDA Development Toolkit CUDA Drivers CUDA SDK (nvcc compiler, libraries, samples) GUI Tools (Eclipse Nsight, Visual Studio Nsight) Εμπειρία σε προγραμματισμό με C Το πρώτο πρόγραμμα (πάλι ) global void mykernel() { printf("hello World from GPU!\n"); int main() { printf("hello World From CPU!\n"); mykernel<<<1,10>>>(); return 0; Αναγνωριστικά συναρτήσεων Εκτελείται στη: Καλείται μόνον από: Παρατηρήσεις device Συσκευή (device) συσκευή global συσκευή CPU Ο επιστρεφόμενος τύπος θα πρέπει υποχρεωτικά να είναι void host CPU CPU Μπορεί να παραλειφθεί Παρατηρήσεις: 1) Συναρτήσεις που εκτελούνται από την CPU όπως τις ξέραμε. 2) Συναρτήσεις που θα κληθούν από την CPU για να εκτελεστούν στο device: global 3) Συναρτήσεις που θα κληθούν αλλά και εκτελεστούν από το device: device 10
Ένα πιο σύνθετο πρόγραμμα Πρόσθεση διανυσμάτων: C=A+B Κλασσικός κώδικας: for (i=0; i<n; i++) C[i] = A[i] + B[i]; Παράλληλη έκδοση (P=αριθμός κόμβων ή πυρήνων): for (i=myid; i<n; i+=p) C[i] = A[i] + B[i] CUDA έκδοση: global void vectoradd(const float *A, const float *B, float *C) { int index = blockdim.x * blockidx.x + threadidx.x; if (index < N) { C[index] = A[index] + B[index]; Άθροισμα διανυσμάτων (1 από 4) #include <stdio.h> // Για τις CUDA βιβλιοθήκες με πρόθεμα "cuda_" #include <cuda_runtime.h> #define N 1000000 // CUDA Kernel Device code: C=A+B global void vectoradd(const float *A, const float *B, float *C) { int index = blockdim.x * blockidx.x + threadidx.x; if (index < N) { C[index] = A[index] + B[index]; Σχόλια Ποιο thread θα υπολογίσει το 21 ο στοιχείο του C (έστω M=8 threads ανά block): 2x8=16 ενώ 3x8=24, επομένως θα ανήκει στο 3 ο (2) block. 21-16=5, επομένως το 5 ο thread του 2 ου block Δηλαδή: index = threadidx.x+blockidx.x*m 11
Άθροισμα διανυσμάτων (2 από 4) int main(void) { int numelements = 1000000; //πλήθος στοιχείων size_t size = N * sizeof(float); //μέγεθος πινάκων // Δέσμευση μνήμης στον host για τους πίνακες Α, Β, C float *h_a = (float *)malloc(size); float *h_b = (float *)malloc(size); float *h_c = (float *)malloc(size); // Αρχικοποίηση των πινάκων Α και Β στον host for (int i = 0; i < N; ++i) { h_a[i] = rand()/(float)rand_max; h_b[i] = rand()/(float)rand_max; Άθροισμα διανυσμάτων (3 από 4) // Δέσμευση μνήμης στο device για τους πίνακες Α, Β, C cudamalloc((void **)&d_a, size); cudamalloc((void **)&d_b, size); cudamalloc((void **)&d_c, size); /*Αντιγραφή των πινάκων Α και Β από τη μνήμη του host στη μνήμη του device */ cudamemcpy(d_a, h_a, size, cudamemcpyhosttodevice); cudamemcpy(d_b, h_b, size, cudamemcpyhosttodevice); /*Εκκίνηση του kernel vectoradd δηλώνοντας τον αριθμό των block που θα περιέχει το grid και τον αριθμό των threads σε κάθε block */ int threadsperblock = 256; int blockspergrid =(N + threadsperblock - 1) / threadsperblock; vectoradd<<<blockspergrid, threadsperblock>>>(d_a, d_b, d_c); Σχόλια cudamalloc: δέσμευση μνήμης στο device. cudamemcpy(d_a, h_a, size, cudamemcpyhosttodevice): Αντιγραφή του πίνακα h_a στον d_a μεγέθους size από το host στο device. threadsperblock = 256: 256 threads ανά block. blockspergrid =(N + threadsperblock - 1) / threadsperblock: Έστω Ν=1000 blockspergrid=4 vectoradd<<<blockspergrid, threadsperblock>>>(d_a, d_b, d_c): κλήση της global συνάρτησης για την πρόσθεση με τις ορισμένες πλέον παραμέτρους blockpergrid και threadperblock. 12
Άθροισμα διανυσμάτων (4 από 4) /* Αντιγραφή του πίνακα αποτελέσματος C από τη μνήμη του device στη μνήμη του host */ cudamemcpy(h_c, d_c, size, cudamemcpydevicetohost); // Απελευθέρωση μνήμης στο device cudafree(d_a); cudafree(d_b); cudafree(d_c); // Απελευθέρωση μνήμης στον host free(h_a); free(h_b); free(h_c); // Επαναφορά του device και τερματισμός cudadevicereset(); return 0; Πρακτικές εφαρμογές της CUDA Fast video transcoding. Βελτίωση ποιότητας video. Έρευνα πετρελαίου και φυσικών πόρων. Ιατρική απεικόνιση. Νευρωνικά δίκτυα. Προσομοιώσεις δυναμικής ρευστότητας. Ανάλυση πρωτεϊνών στα αμινοξέα από τα οποία αποτελούνται. Και πολλές άλλες! Παρατηρήσεις Ας δούμε τις παρακάτω κλήσεις της vectoradd: vectoradd<<1,1>> :Σειριακή εκτέλεση στο device vectoradd<<n,1>> : Παράλληλη εκτέλεση από ακριβώς Ν threads. Γιατί? 13
Υβριδικά μοντέλα παράλληλου προγραμματισμού MPI + OpenMP CUDA + MPI CUDA + OpenMP CUDA + MPI + OpenMP OpenACC (Open ACCelerators) Προγραμματιστικό μοντέλο για παράλληλο προγραμματισμό (αναπτύχθηκε από Cray, CAPS, Nvidia και PGI). Απλοποιεί ετερογενή παράλληλο προγραμματισμό για συστήματα CPU/GPU κρύβοντας πολλές λεπτομέρειες, πχ: #pragma acc kernels και το επόμενο μέρος του κώδικα εκτελείται στην GPU Βιβλιοθήκες/οδηγίες για C, C++, FORTRAN. Αρκετές ομοιότητες με OpenMP: OpenACC: παράδειγμα #pragma acc kernels { #pragma acc loop tile(32,4) device_type(nvidia) for( int j = 1; j < n-1; j++) { for( int i = 1; i < m-1; i++ ) { Anew[j][i] = 0.25 * ( A[j][i+1] + A[j][i-1] + A[j- 1][i] + A[j+1][i]); 14
GPU Cloud Computing NVIDIA GPU Cloud GPU Google Cloud Platform NVDIA GPU in Microsoft Azure Amazon EC2 Elastic GPUs IBM Bluemix Προτεινόμενη βιβλιογραφία Εισαγωγή στον GPGPU προγραμματισμό Ηλίας Σάββας Καθηγητής Τμ. Μηχ. Πληροφορικής ΤΕ, ΤΕΙ Θεσσαλίας savvas@teilar.gr 15