CUDA C Clase 2 module load cuda • Obteniendo las propiedades del Device desde CUDA C • Funciones en CUDA C • Operaciones fundamentales en paralelo Copiando los códigos [flavioc@gpgpu-­‐fisica ~]$ ls /share/apps/codigos/alumnos_icnpg2015/ common ejemplos_thrust SUMA-­‐Vectores Get_device [flavioc@gpgpu-­‐fisica ~]$ cd icnpg2015/ [flavioc@gpgpu-­‐fisica icnpg2015]$ cp -­‐R /share/apps/codigos/alumnos_icnpg2015/Get_device/ . [flavioc@gpgpu-­‐fisica icnpg2015]$ cd Get_device [flavioc@gpgpu-­‐fisica Get_device]$ ls devicequery.cu devicequery.h main.cu Makefile submit_gpuh.sh vector_io.cu vector_io.h vector_ops.cu vector_ops.h Get device devicequery.cu int device_count; int driverVersion = 0, runtimeVersion = 0; cudaGetDeviceCount( &device_count ); // printf("Number of GPUs available : %d\n",device_count); cudaGetDevice(&card); cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, card); Cuenta el número de devices cuda_runtime_api.h cudaDeviceProp struct definido en cuda_runtime_api.h Get/Set device devicequery.cu int device_count; int driverVersion = 0, runtimeVersion = 0; cudaGetDeviceCount( &device_count ); // printf("Number of GPUs available : %d\n",device_count); cudaGetDevice(&card); cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, card); Cuenta el número de devices cuda_runtime_api.h cudaSetDevice(int) Selecciona el Device para correr cudaGetDevice(*int) Obtiene el Device en el que se está corriendo Generaciones de GPU GeForce GTX 780 GeForce GTX Titan CUDA Driver Version CUDA Capability Major/Minor version number Total amount of global memory Multiprocessors x Cores/MP = Cores Maximum number of threads per block Maximum sizes of each dimension of a block Maximum sizes of each dimension of a grid 7 ¡Hands On! Funciones en el device __device__ int addem( int a, int b ) { return a + b; } __global__ void add(int a, int b, int *dc ) { *dc = addem(a,b); } __global__ declara a add como una función que se ejecuta en el device __device__ declara a addem como una función que se ejecuta en el device, y se llama desde el device Los argumentos pasan por valor como en Lenguaje C 9 Funciones en CUDA C Sólo acceden a variables en la GPU No pueden tener número variable de argumentos No pueden ser recursivas (__device__ functions support recursion in device code compute capability > 2.x) No pueden contener variables estáticas Deben declararse con un calificador: __global__, __host__ o __device__ Funciones en CUDA C Calificador Se ejecuta en Se llama desde __device__ device device __global__ device host __host__ host host __global__ designa una función como kernel funciones __global__ son void funciones __global__ necesitan una configuración de lanzamiento <<<xx,yy>>> Las llamadas a funciones __global__ son asincrónicas, es decir que retornan antes que el device haya completado su ejecución CUDA Programming Guide, Appendix E Integrales, reducción y convolución Integración numérica por el método de trapecios b a 1 f(x)dx = 2 N N b 1 f(x)dx = (x i (xa i xi 1 ) yi2+ yi 1 xi y 1) yi + y i y i = f( xi ) 1 i=1 i=1 f(x) La integral se calcula sumando las areas de los trapecios La función está definida por dos arrays, x e y de tamaño N, y i = f( xi ) b a b N N 1 1 x f(x)dx = f(x)(dx xi = xi 1 ) (yxi i+ yxi i 11 ) yi + yi 2 a2 i=1 1 i=1 13 Integración numérica por el método de trapecios b a 1 f(x)dx = 2 N N b 1 f(x)dx = (x i (xa i xi 1 ) yi2+ yi 1 xi y 1) yi + y i y i = f( xi ) 1 i=1 i=1 f(x) Hay varias maneras de paralelizar Dos pasos: 1. Cada thread calcula 1 x)dx = 2 N (x i xi 1) yi + y i 1 b i=1 2. Suma en CPU a b N N 1 1 x f(x)dx = f(x)(dx xi = xi 1 ) (yxi i+ yxi i 11 ) yi + yi 2 a2 i=1 i=1 1 Método de trapecios prom2.cu __global__ void trapz_simple(float* x, float *data, float *integral, int N) { int index = threadIdx.x + blockIdx.x * blockDim.x ; float tmp; float h; float xi,xim,yi,yim; // compute the average of this thread's neighbors xi = x[index]; // x_i xim = x[index-­‐1]; // x_{i-­‐1} b yi = data[index]; // y_i yim = data[index-­‐1]; // y_{i-­‐1} a h = xi-­‐xim; // h = x_{i}-­‐x_{i-­‐1} tmp = (yi+yim)*0.5f; // tmp = (y_i+x_{i-­‐1})/2 integral[index] = h* tmp; } 1 f(x)dx = 2 Este código tiene varios problemas.... N (x i i=1 xi 1) yi + y i 1 Método de trapecios prom2.cu __global__ void trapz_simple(float* x, float *data, float *integral, int N) { int index = threadIdx.x + blockIdx.x * blockDim.x ; float tmp; float h; float xi,xim,yi,yim; // compute the average of this thread's neighbors xi = x[index]; // x_i xim = x[index-­‐1]; // x_{i-­‐1} yi = data[index]; // y_i yim = data[index-­‐1]; // y_{i-­‐1} h = xi-­‐xim; // h = x_{i}-­‐x_{i-­‐1} tmp = (yi+yim)*0.5f; // tmp = (y_i+x_{i-­‐1})/2 integral[index] = h* tmp; } Qué pasa si index=0 ¿Porqué no funciona? y -1 0 1 0 1 2 3 4 5 4 5 6 8 7 8 9 index index-1 El thread con index=0 es un caso particular 17 Método de trapecios prom2.cu __global__ void trapz_simple(float* x, float *data, float *integral, int N) { int index = threadIdx.x + blockIdx.x * blockDim.x ; float tmp; float h; float xi,xim,yi,yim; // compute the average of this thread's neighbors if(index>0){ xi = x[index]; // x_i xim = x[index-­‐1]; // x_{i-­‐1} yi = data[index]; // y_i yim = data[index-­‐1]; // y_{i-­‐1} h = xi-­‐xim; // h = x_{i}-­‐x_{i-­‐1} tmp = (yi+yim)*0.5f; // tmp = (y_i+x_{i-­‐1})/2 integral[index] = h* tmp; }else{ integral[0]=0.0; } index=0 es un caso particular Método de trapecios prom.cu __global__ void trapz_simple(float* x, float *data, float *integral, int N) { int index = threadIdx.x + blockIdx.x * blockDim.x ; float tmp; float h; float xi,xim,yi,yim; // compute the average of this thread's neighbors xi = x[index]; // x_i xim = x[index>0?index-­‐1:0]; // x_{i-­‐1} yi = data[index]; // y_i yim = data[index>0?index-­‐1:0]; // y_{i-­‐1} h = xi-­‐xim; // h = x_{i}-­‐x_{i-­‐1} tmp = (yi+yim)*0.5f; // tmp = (y_i+x_{i-­‐1})/2 integral[index] = h* tmp; } Otra forma de hacerlo Pasando opciones al main int main( int argc, const char** argv ) { .... if(argc==1){ // Help si no hay argumentos printf("Usage %s [array size] [number of threads per block]\n",argv[0]); exit(1); } if(argc==2){ // un solo argumento implica numthreads=32 printf("Assuming number of threads per block=32\n"); N = atoi(argv[1]); numthreads = 32; } argc cuenta el número de argumentos if(argc==3){ N = atoi(argv[1]); numthreads = atoi(argv[2]); } argv[0] es el nombre del exe Pasando opciones al main [flavioc@gpgpu-fisica ~]$ cp -a /share/apps/codigos/Trapecios . [flavioc@gpgpu-fisica ~]$ cd Trapecios [flavioc@gpgpu-fisica ~]$ nvcc prom2.cu -o trap [flavioc@gpgpu-fisica ~]$ ./trap Usage ./trap [array size] [number of threads per block] [flavioc@gpgpu-fisica ~]$ qsub submit_gpuh.sh 1024 32 Se pueden hacer varias corridas sin compilar Su turno.... 22 Reducción en GPU b a 1 f(x)dx = 2 N N b 1 f(x)dx = (x i (xa i xi 1 ) yi2+ yi 1 xi y 1) yi + y i y i = f( xi ) 1 i=1 i=1 f(x) Hay varias maneras de paralelizar Dos pasos: 1. Cada thread calcula 1 x)dx = 2 N (x i xi 1) yi + y i 1 b i=1 2. Suma en GPU a b N N 1 1 x f(x)dx = f(x)(dx xi = xi 1 ) (yxi i+ yxi i 11 ) yi + yi 2 a2 i=1 i=1 1 Reduction/Scan Scan: una transformación de una secuencia de valores en otra Es una de las operaciones más importantes en gpgpu Es una operación ‘horizontal’ (ej: producto escalar) a diferencia de una operación ‘vertical’ o componente a componente (ej: suma de vectores) prefix sum: cada elemento de la secuencia es suma de todos los anteriores Sequence 4 0 5 5 0 5 5 1 3 1 0 3 1 1 3 5 Scan 4 4 9 14 14 19 24 25 28 29 29 32 33 34 37 42 24 Reduction/Scan Scan: una transformación de una secuencia de valores en otra Es una de las operaciones más importantes en gpgpu Es una operación ‘horizontal’ (ej: producto escalar) a diferencia de una operación ‘vertical’ o componente a componente (ej: suma de vectores) prefix sum: cada elemento de la secuencia es suma de todos los anteriores Sequence 4 0 5 5 0 5 5 1 3 1 0 3 1 1 3 5 Scan 4 4 9 14 14 19 24 25 28 29 29 32 33 34 37 42 25 Prefix Sum Algoritmo offset = 1 4 0 5 5 0 5 5 1 3 1 0 3 1 1 3 5 offset = 2 4 4 5 10 5 5 10 6 4 4 1 3 4 2 4 8 offset = 4 4 4 9 14 10 15 15 11 14 10 5 7 5 5 8 10 offset = 8 4 4 9 14 14 19 24 25 24 25 20 18 19 15 13 17 offset = 16 4 4 9 14 14 19 24 25 28 29 29 32 33 34 37 42 26 Suma global: Entrelazada Values (in shared memory) 10 Step 1 Stride 1 Step 2 Stride 2 Thread IDs 0 Values 11 Thread IDs 0 Values 18 Step 3 Stride 4 Thread IDs Thread IDs 8 -1 1 1 7 0 -2 2 -1 -2 3 5 3 -2 8 5 1 1 7 -1 6 -2 -3 4 -5 2 7 5 -3 9 -2 8 5 4 0 11 6 7 2 11 0 2 7 11 2 2 3 -3 9 7 13 11 2 2 1 0 Values 24 Step 4 Stride 8 1 1 7 -1 6 -2 8 5 17 -3 9 7 13 11 2 2 1 7 -1 6 -2 8 5 17 -3 9 7 13 11 2 2 0 Values 41 Thanks to: Stanford, CS193G Suma global: Contigua Values (in shared memory) 10 Step 1 Stride 8 Step 2 Stride 4 Step 3 Stride 2 Step 4 Stride 1 1 8 -1 0 -2 3 5 0 1 2 3 4 5 6 7 Values 8 -2 10 6 0 9 3 Thread IDs 0 1 2 3 Values 8 7 13 13 0 9 Thread IDs 0 1 Values 21 20 13 13 0 0 Thread IDs Thread IDs -2 -3 2 7 0 11 0 2 7 -2 -3 2 7 0 11 0 2 3 7 -2 -3 2 7 0 11 0 2 9 3 7 -2 -3 2 7 0 11 0 2 9 3 7 -2 -3 2 7 0 11 0 2 0 Values 41 20 13 13 Thanks to: Stanford, CS193G Reduction/Scan: integral Si la secuencia es el vector integral[] del ejemplo prom prefix sum es la integral ‘indefinida’ y el último elemento de la secuencia es la integral que buscamos Sequence 4 0 5 5 0 5 5 1 3 1 0 3 1 1 3 5 Prefix Sum 4 4 9 14 14 19 24 25 28 29 29 32 33 34 37 42 ¿Cómo se hace en la GPU? 29 Midiendo el tiempo de ejecución en GPU cudaEvent_t start,stop; cudaEventCreate(&start); cudaEventCreate(&stop); cudaEventRecord( start, 0 ); // llamado a Kernel/s cudaEventRecord( stop, 0 ); cudaEventSynchronize( stop ); //Necesario float elapsedTime; cudaEventElapsedTime( &elapsedTime,start, stop )); // en milisec. printf( "Time in Kernel: %3.1f ms\n", elapsedTime); En kernels cortos (como los de estas clases) puede ser necesario correrlos varias veces usando un for/while Trabajo Práctico 1 [flavioc@gpgpu-­‐fisica ~]$ ls /share/apps/codigos/TPs TP1 [flavioc@gpgpu-­‐fisica ~]$ ls /share/apps/codigos/TPs/TP1 g1ej1 g1ej2 g1ej3 g1ej4 g1ej5 [flavioc@gpgpu-­‐fisica TP1]$ cp -­‐R /share/apps/codigos/TPs/TP1 icnpg2013/ ¡Hands On! ¿ Dudas ? Trabajo Práctico 1 http://fisica.cab.cnea.gov.ar/gpgpu/index.php/en/icnpg