CUDA como obter grade, bloco, tamanho de rosca e paralisar o cálculo de matriz não quadrada

Eu sou novo no CUDA e preciso de ajuda para entender algumas coisas. Preciso de ajuda para paralelizar esses dois loops forçados. Especificamente, como configurar o dimBlock e o dimGrid para fazer isso rodar mais rápido. Eu sei que isso se parece com o exemplo de adicionar vetor no SDK, mas esse exemplo é apenas para matrizes quadradas e quando tento modificar esse código para minha matriz de 128 x 1024 ele não funciona corretamente.

__global__ void mAdd(float* A, float* B, float* C) { for(int i = 0; i < 128; i++) { for(int i = 0; i < 1024; i++) { C[i * 1024 + j] = A[i * 1024 + j] + B[i * 1024 + j]; } } } 

Este código é parte de um loop maior e é a parte mais simples do código, então eu decidi tentar paralelizar thia e aprender CUDA ao mesmo tempo. Eu li os guias, mas ainda não entendi como obter o adequado não. de grids / block / threads indo e usá-los de forma eficaz.

Como você escreveu, esse kernel é completamente serial. Cada thread lançado para executá-lo está executando o mesmo trabalho.

A principal idéia por trás do CUDA (e do OpenCL e outros modelos de programação similares de “programa único e múltiplos dados”) é que você executa uma operação de “dados paralelos” – um em que a mesma operação deve ser executada muitas vezes – e escreva um kernel que execute essa operação. Um grande número de encadeamentos (semi) autônomos é então lançado para executar essa operação no dataset de input.

Em seu exemplo de adição de array, a operação paralela de dados é

 C[k] = A[k] + B[k]; 

para todos k entre 0 e 128 * 1024. Cada operação de adição é completamente independente e não tem requisitos de pedido e, portanto, pode ser executada por um thread diferente. Para expressar isso no CUDA, pode-se escrever o kernel assim:

 __global__ void mAdd(float* A, float* B, float* C, int n) { int k = threadIdx.x + blockIdx.x * blockDim.x; if (k < n) C[k] = A[k] + B[k]; } 

[disclaimer: código escrito no navegador, não testado, use a risco próprio]

Aqui, o loop interno e externo do código serial é substituído por um thread CUDA por operação e incluímos uma verificação de limite no código para que, nos casos em que mais encadeamentos sejam iniciados que as operações necessárias, nenhum estouro de buffer possa ocorrer. Se o kernel é então lançado assim:

 const int n = 128 * 1024; int blocksize = 512; // value usually chosen by tuning and hardware constraints int nblocks = n / nthreads; // value determine by block size and total work madd<<>>mAdd(A,B,C,n); 

Em seguida, 256 blocos, cada um contendo 512 threads, serão lançados no hardware da GPU para executar a operação de adição de matriz em paralelo. Observe que, se o tamanho dos dados de input não fosse expressável como um múltiplo arredondado do tamanho do bloco, o número de blocos precisaria ser arredondado para cobrir o dataset de input completo.

Todos os itens acima são uma visão geral extremamente simplificada do paradigma CUDA para uma operação muito trivial, mas talvez ele forneça informações suficientes para você continuar. A CUDA está bastante madura nos dias de hoje e há muito material educativo gratuito circulando na web que você provavelmente pode usar para iluminar ainda mais muitos aspectos do modelo de programação que encarei nesta resposta.