CUDA

De Viquipèdia
Salta a: navegació, cerca
Infotaula de programariCUDA
Desenvolupador(s) NVIDIA Corporation
Versió inicial 23 juny 2007
Versió estable 8.0 [Sept.2016]
Sistema operatiu Windows 7, Windows Vista, Windows XP, Windows Server 2008, Windows Server 2003, Linux, Mac OS X
Tipus llenguatge de programació
Llicència Software propietari, freeware
Més informació
Lloc web Nvidia's CUDA zone
Modifica dades a Wikidata

CUDA és una plataforma de computació paral·lela i model d'interfície de programació d'aplicacions (API) creada per Nvidia. Permet a desenvolupadors i enginyers de software fer servir unitats de processament gràfic (GPU) amb capacitat CUDA per a processament de caràcter general. Quan va ser introduída per Nvidia, el nom de CUDA era un acrónim de Compute Unified Device Architecture (Arquitectura de comput de dispositius unificats).

La plataforma CUDA és una capa de software que dóna accés directe al conjunt virtual d'instruccions de la GPU i als elements de cómput paral·lel a efectes d'executar nuclis de cómput anomenats kernels.

La plataforma va ser desenvolupada amb l'objectiu de treballar conjuntament amb llenguatges de programació com C, C++ i Fortran. Aquesta accessibilitat facilita als especialistes de programació paral·lela l'ús dels recursos que ofereix una GPU, en contrast amb anteriors APIs com Direct3D i OpenGL, les quals tenien uns requeriments importants de coneixement previ de programació gràfica. CUDA dóna suport a entorns de treball orientats a programació com OpenACC i OpenCL.

Història[modifica]

La unitat de processament gràfic (GPU), és un processador d'ordinador especialitzat i es dirigeix a les demandes en temps real de càlcul intensiu de gràfics d'alta resolució en 3D. A l'any 2012, les GPUs havien evolucionat fins a convertir-se en sistemes multi-core que són altament paral·lels i permeten la manipulació eficient de grans blocs de dades. Aquest disseny és més eficaç que la unitat central de processament de propòsit general (CPU) per als algorismes que han de treballar grans blocs de dades.

Jerarquia de fils d'execució[1][modifica]

Organització dels fils d'execució

Per l’execució de programes paral·lels a través de kernels CUDA s’utilitza una jerarquia de fils que està donada per tres categories:

  • Els threads que estan organitzats en blocs de fils d'execució (threads).
  • Els blocs (block) que estan organitzats en malles de blocs.
  • Les malles (grid), és la jerarquia més alta i només es pot executar en un kernel.

La dimensió de la malla s'obté de la variable gridDim i la dimensió de un bloc de la variable blockDim; d'altra banda, a cada fil se li assigna un identificador únic donat per la variable threadIdx; d'igual forma cada bloc s'identifica amb la variable blockIdx.

  • ThreadIdx és una variables de tipus unit3 que conté l'índex d'un fil dins d'un bloc.
  • BlockIdx és una variables de tipus unit3 que conté l'índex d'un bloc dins d'una malla.
  • unit3 és un vector inter construït de tipus sencer, té una funció constructor unit3 nom_variable(x,i,z).
  • BlockDim és una variable de tipus dim3 que conté la dimensió d'un bloc.
  • GridDim és una variable de tipus dim3 que conté la dimensió d'una malla.
  • dim3 és un vector inter construït, basat en unit3, és utilitzat per especificar dimensions, la seva funció constructor és dim3 nom_variable(x,i,z), qualsevol component no especificat s'inicialitza a 1.

Els fils poden estar organitzats, d'acord a l'aplicació, en blocs d'una dimensió (per manipular vectors), de dues dimensions (per manipular matrius) i de tres dimensions (per manipular arranjaments tridimensionals).

Jerarquia de memòria[2][modifica]

CUDA memòria

Sobre CUDA també es s'utilitza una jerarquia de memòria, de forma que cada thread té accés a diferents tipus de memòria. En primer lloc una memòria local al propi thread, aquesta memòria s'allotja a la memòria principal de la GPU (off-xip). A més, tots els fils d'execució d'un mateix bloc comparteixen una regió de memòria compartida (on-xip) per comunicar-se entre ells, la memòria compartida té el mateix temps de vida que el bloc de fils d'execució. Per últim, tots els fils d'execució tenen accés a la mateixa memòria global (device memory).

Existeixen a més altres 2 espais de memòria addicionals de només lectura:

  • Constant memory
  • Texture memory

CUDA Kernel CUDA incorpora un nou model d'execució diferent al model seqüencial de les computadores tradicionals. A CUDA, el codi serà executat per múltiples fils d'execció a la vegada (centenars o milers). La programació ha de ser modelada segons la jerarquia de fils d'execució.[3]

Una funció kernel ha de ser invocada per la CPU (host), però s'executa exclusivament en GPU.

Els kernels tenen dues característiques fonamentals:

  • Els kernels no poden retornar un valor de forma explícita. Els resultats han de ser desats sobre estructures de dades que s'hagin enviat a la funció en el moment de la crida.
  • Els kernels generen de forma explícita la jerarquia de fils d'execció quan es realitza la crida segons el nombre de blocs de fils d'execució i el nombre de fils d'execució per bloc. Així doncs, una mateixa funció CUDA pot ser cridada diverses vegades en un mateix codi, i tenir una jerarquia de fils d'execució diferents segons la crida.
// Declaració del kernel CUDA. 
__global__ void foo(double *vector,int limit)
{
    // Calcular l'identificador del thread
    int identificador = blockIdx.x*blockDim.x + threadIdx.x;
 
    // Comprobar que estem dintre del rang del vector
    if (identificador < limit)
        vector[identificador] = 0;
}

Invocació:

Una vegada declarada la funció, és recomanable declarar variables per a gestionar la jerarquia de fils d'execució.

  int CTA_NUM = (N + 1023)/1024;
  dim3 blocks(CTA_NUM, 1, 1);
  dim3 threadsPerBlock (1024, 1, 1);

Finalment, es realitza la crida al kernel

  foo<<< blocks, threadsPerBlock >>>(d_vector_in, d_vector_out, N);

Exemple[modifica]

Codi d'exemple: Suma de vectors

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
// Declaració del kernel CUDA. 
__global__ void sumaVectors(double *a, double *b, double *c, int n)
{
    // Calcular l'identificador del thread
    int id = blockIdx.x*blockDim.x+threadIdx.x;
 
    // Comprobar que estem dintre del rang del vector
    if (id < n)
        c[id] = a[id] + b[id];
}
 
// Funció principal
int main( int argc, char* argv[] )
{
    // Mida del vector
    int n = 1024*1024;
 
    // Declarem els punters d'entrada per la memòria del host 
    double *host_vector_a;
    double *host_vector_b;
    // Declarem els punters de sortida per la memòria del host 
    double *host_vector_c;
 
    // Declarem els punters d'entrada per la memòria del dispositiu (GPU)
    double *device_vector_a;
    double *device_vector_b;
    // Declarem els punters de sortida per la memòria del dispositiu (GPU)
    double *device_vector_c;
 

    // Calculem la mida del vector en bytes
    size_t bytes = n*sizeof(double);
 
    // Reservar memòria a l'espai del host per als vectors
    host_vector_a = (double*)malloc(bytes);
    host_vector_b = (double*)malloc(bytes);
    host_vector_c = (double*)malloc(bytes);
 
    // Reservar memòria a l'espai del dispositiu (GPU) per als vectors
    cudaMalloc(&device_vector_a, bytes);
    cudaMalloc(&device_vector_b, bytes);
    cudaMalloc(&device_vector_c, bytes);
 
    int i;
    // Inicialitzar valors del vectors
    for( i = 0; i < n; i++ ) {
        host_vector_a[i] = sin(i)*sin(i);
        host_vector_b[i] = cos(i)*cos(i);
    }
 
    // Copiar els vectors del host al dispositiu (D <-- H)
    cudaMemcpy( device_vector_a, host_vector_a, bytes, cudaMemcpyHostToDevice);
    cudaMemcpy( device_vector_b, host_vector_b, bytes, cudaMemcpyHostToDevice);
 
    int blockSize, gridSize;
 
    // Nombre de threads per bloc
    blockSize = 1024;
 
    // Nombre de blocks al grid
    gridSize = (int)ceil((float)n/blockSize);
 
    // Executem el kernel
    sumaVectors<<<gridSize, blockSize>>>(device_vector_a, device_vector_b, device_vector_c, n);
 
    // Copiem el vector de sortida del dispositiu al host (H <-- D)
    cudaMemcpy( host_vector_c, device_vector_c, bytes, cudaMemcpyDeviceToHost );
 
    // Calculem el resultat final
    double sum = 0;
    for(i=0; i<n; i++)
        sum += host_vector_c[i];
    printf("final result: %f\n", sum/n);
 
    // Alliberem la memòria del dispositiu
    cudaFree(device_vector_a);
    cudaFree(device_vector_b);
    cudaFree(device_vector_c);
 
    // Alliberem la memòria del host
    free(host_vector_a);
    free(host_vector_b);
    free(host_vector_c);
 
    return 0;
}

Sincronització[4][modifica]

Com els diferents fils col·laboren entre ells i poden compartir dades, es requereixen unes directives de sincronització. En un kernel, es pot declarar una barrera afegint una crida a __syncthreads(), amb això tots els fils s'esperaran fins que tots els fils arribin al mateix punt.

Tarjetes Suportades[modifica]

Nvidia GeForce
GeForce GTX 1080
GeForce GTX 1070
GeForce GTX 1060
GeForce GTX 1050Ti
GeForce GTX 1050
GeForce GTX TITAN X
GeForce GTX 980Ti
GeForce GTX 980
GeForce GTX 970
GeForce GTX 960
GeForce GTX 950
GeForce GTX TITAN Z
GeForce GTX TITAN
GeForce GTX 780Ti
GeForce GTX 780
GeForce GTX 770
GeForce GTX 760
GeForce GTX 750Ti
GeForce GTX 750
GeForce GTX 690
GeForce GTX 680
GeForce GTX 670
GeForce GTX 650
GeForce GT 640
GeForce GTX 590
GeForce GTX 580
GeForce GTX 570
GeForce GTX 560
GeForce GTX 550
GeForce GTX 480
GeForce GTX 470
GeForce GTX 465
GeForce GTX 460
GeForce GTS 450
GeForce GT 440
GeForce GT 430
GeForce GTX 295
GeForce GTX 285
GeForce GTX 280
GeForce GTX 275
GeForce GTX 260
GeForce GTS 250
GeForce GTS 240
GeForce GT 240
GeForce GT 220
GeForce 210/G210
GeForce 9800 GX2
GeForce 9800 GTX+
GeForce 9800 GTX
GeForce 9800 GT
GeForce 9600 GSO
GeForce 9600 GT
GeForce 9500 GT
GeForce 9400 GT
GeForce 9400 m
GeForce 9300 m
GeForce 9100 m
GeForce 8800 Ultra
GeForce 8800 GTX
GeForce 8800 GTS
GeForce 8800 GT
GeForce 8800 GS
GeForce 8600 GTS
GeForce 8600 GT
GeForce 8600 m
GeForce 8500 GT
GeForce 8400 GS
GeForce 8300 m
GeForce 8200 m
GeForce 8100 m
Nvidia GeForce Mobile
GeForce GTX 880M
GeForce GTX 780M
GeForce GTX 770M
GeForce GTX 765M
GeForce GTX 760M
GeForce GT 750M
GeForce GT 745M
GeForce GT 740M
GeForce GT 735M
GeForce GT 730M
GeForce GTX 680MX
GeForce GTX 680M
GeForce GTX 675MX
GeForce GTX 675M
GeForce GTX 670MX
GeForce GTX 670M
GeForce GTX 660M
GeForce GT 650M
GeForce GT 645M
GeForce GT 640M
GeForce GTX 580M
GeForce GTX 570M
GeForce GTX 560M
GeForce GT 555M
GeForce GT 550M
GeForce GT 540M
GeForce GT 525M
GeForce GT 520M
GeForce GTX 480M
GeForce GTX 285M
GeForce GTX 280M
GeForce GTX 260M
GeForce GTS 360M
GeForce GTS 350M
GeForce GTS 260M
GeForce GTS 250M
GeForce GT 620M
GeForce GT 335M
GeForce GT 330M
GeForce GT 325M
GeForce GT 320M
GeForce 310M
GeForce GT 240M
GeForce GT 230M
GeForce GT 220M
GeForce G210M
GeForce GTS 160M
GeForce GTS 150M
GeForce GT 130M
GeForce GT 120M
GeForce G110M
GeForce G105M
GeForce G103M
GeForce G102M
GeForce G100
GeForce 9800M GTX
GeForce 9800M GTS
GeForce 9800M GT
GeForce 9800M GS
GeForce 9700M GTS
GeForce 9700M GT
GeForce 9650M GT
GeForce 9650M GS
GeForce 9600M GT
GeForce 9600M GS
GeForce 9500M GS
GeForce 9500M G
GeForce 9400M G
GeForce 9300M GS
GeForce 9300M G
GeForce 9200M GS
GeForce 9100M G
GeForce 8800M GTX
GeForce 8800M GTS
GeForce 8700M GT
GeForce 8600M GT
GeForce 8600M GS
GeForce 8400M GT
GeForce 8400M GS
GeForce 8400M G
GeForce 8200M G
Nvidia Quadro
Quadro 6000
Quadro 5000
Quadro 4000
Quadro FX 5800
Quadro FX 5600
Quadro FX 4800
Quadro FX 4700 X2
Quadro FX 4600
Quadro FX 3800
Quadro FX 3700
Quadro FX 1800
Quadro FX 1700
Quadro FX 580
Quadro FX 570
Quadro FX 380
Quadro FX 370
Quadro NVS 450
Quadro NVS 420
Quadro NVS 295
Quadro NVS 290
Quadro Plex 1000 Model IV
Quadro Plex 1000 Model S4
Nvidia Quadro Mobile
Quadro FX 3800M
Quadro FX 3700M
Quadro FX 3600M
Quadro FX 2800M
Quadro FX 2700M
Quadro FX 1800M
Quadro FX 1700M
Quadro FX 1600M
Quadro FX 880M
Quadro FX 770M
Quadro FX 570M
Quadro FX 380M
Quadro FX 370M
Quadro FX 360M
Quadro NVS 320M
Quadro NVS 160M
Quadro NVS 150M
Quadro NVS 140M
Quadro NVS 135M
Quadro NVS 130M
Nvidia Tesla
Tesla C2050
Tesla S1070
Tesla M1060
Tesla C1060
Tesla C870
Tesla D870
Tesla S870


Referències[modifica]

Enllaços externs[modifica]