[Notas de introdução ao CUDA] Visão geral

1. Arquitetura CUDA

(1) Uma GPU contém múltiplos processadores multi-core;
(2) Um processador multi-core contém múltiplos processadores thread
(3) Processadores thread são as unidades de computação mais básicas e possuem sua própria memória local e registradores

2, thread, bloco, grade significado

(1) thread corresponde ao processador de threads no hardware;
(2) grade corresponde a uma GPU
(3) bloco é composto por múltiplos processadores de threads em um processador multi-core;
(4) um processador multi-core pode ser dividido em Múltiplos blocos;
(5) Ao executar uma instrução, uma unidade de execução warp (warp) executará 32 threads em paralelo, portanto, quando dividimos o tamanho do bloco, geralmente é definido como um múltiplo de 32

3. Programação CUDA

(1) Aloque a memória de vídeo na GPU e copie os dados da CPU para a memória de vídeo
(2) Use a função kernel para concluir o cálculo dos dados na memória de vídeo da GPU
(3) Copie os resultados do cálculo no memória de vídeo de volta à memória da CPU para
calcular a adição de matrizes: C = A + B, seja AB uma matriz unidimensional com comprimento n

//A função do kernel (ou seja, a função executada na GPU/declarada com __global__)
explica aqui que
threadIdx indica o número de threads e o intervalo é o tamanho do bloco (blockDim);
blockIdx é o número de blocos e o intervalo é tamanho da grade;

__global__
void vecAddKernel(float* A_d, float* B_d, float* C_d, int n)
{
    
    
    int i = threadIdx.x + blockDim.x * blockIdx.x;  //计算线程ID
    if (i < n) C_d[i] = A_d[i] + B_d[i];  //筛选ID小于n的线程,即例如线程1计算C_d[1] = A_d[1] + B_d[1]
}



Como ligar:

//划分GPU的block和Grid
    int threadPerBlock = 256;  //一个warp大小为32,一般设置为32的倍数
    int blockPerGrid = (n + threadPerBlock - 1)/threadPerBlock;  //根据划分的blocksize计算gridsize
	
	//调用核函数
    vecAddKernel <<< blockPerGrid, threadPerBlock >>> (da, db, dc, n);


O blockPerGrid, threadPerBlock acima correspondem respectivamente a Gridsize, blocksize (blockDim)

Quando a dimensão é bidimensional:


//核函数(传入显存ABC以及维度信息MNK)
__global__ void multiplicateMatrix(float *array_A, float *array_B, float *array_C, int M_p, int K_p, int N_p)
{
    
    
	//这里我们划分的lblock和grid是二维的,分别计算线程的二维索引(x方向和y方向的索引)
	int ix = threadIdx.x + blockDim.x*blockIdx.x;//row number,
	int iy = threadIdx.y + blockDim.y*blockIdx.y;//col number

	if (ix < N_p && iy < M_p)  //筛选线程,每个线程计算C中的一个元素,线程的xy索引与C的元素位置索引对应
	{
    
    
		float sum = 0;
		for (int k = 0; k < K_p; k++) //C中的某个元素为A中对应行和B中对应列向量的乘积和。
		{
    
    
			sum += array_A[iy*K_p + k] * array_B[k*N_p + ix];
		}
		array_C[iy*N_p + ix] = sum;
	}
}

//划分GPU的block和Grid
    int dimx = 2;
    int dimy = 2;
    dim3 block(dimx, dimy);
    dim3 grid((M + block.x - 1) / block.x, (N + block.y - 1) / block.y);
    
    //调用核函数
    multiplicateMatrix<<<grid,block>>> (d_A, d_B, d_C, M, K, N);

Pergunta: por que dim3 block(2, 2) não precisa ser um múltiplo de 32

4. Estratégia de aceleração avançada CUDA

(1) Use memória compartilhada para acelerar o acesso à memória
Registro de encadeamento: 1 ciclo
Memória compartilhada de bloco: 5 ciclos
Memória global de grade: 500 ciclos
Memória constante de grade: 5 ciclos
(2) Use fluxo para acelerar a leitura e gravação demoradas de grandes lotes de arquivo IO
Quando usamos GPU Ao realizar cálculos, podemos abrir ativamente vários fluxos, semelhante ao multi-threading de uma CPU. Podemos atribuir um grande número de leituras e gravações de arquivos a vários fluxos para execução ou usar diferentes fluxos para calcular diferentes funções do kernel. Os vários fluxos abertos são assíncronos e os cálculos no fluxo e no lado da CPU também são assíncronos. Portanto, precisamos prestar atenção ao adicionar operações síncronas.
Vale a pena notar que, limitado pela largura de banda do barramento PCIe, quando um fluxo está realizando operações de leitura e gravação, o outro fluxo não pode realizar operações de leitura e gravação ao mesmo tempo, mas outros fluxos podem realizar tarefas de cálculo numérico. Isso é um pouco semelhante ao mecanismo de pipeline na CPU.
(3) Chame a API da biblioteca cuBLAS para cálculo de matriz
cuBLAS é uma implementação BLAS que permite aos usuários usar os recursos de computação da GPU da NVIDIA. Ao usar o cuBLAS, o aplicativo deve alocar o espaço de memória da GPU exigido pela matriz ou vetor, carregar os dados, chamar a função cuBLAS necessária e, em seguida, carregar o resultado do cálculo do espaço de memória da GPU para o host. A API cuBLAS também fornece alguns funções auxiliares para gravar ou ler dados da GPU.
Documentação oficial: https://docs.nvidia.com/cuda/cublas/index.html

おすすめ

転載: blog.csdn.net/qq_34106574/article/details/128330070