Essas coisas sobre programação CUDA naqueles anos (1)

I. Visão geral

  • O propósito de usar a programação CUDA: Quando os métodos de aceleração comuns ( instruções SIMD , C++ multi-threading , OpenMP , etc.) .
  • Por exemplo: algoritmo de correspondência estéreo, treinamento e teste de aprendizagem profunda, reconstrução 3D, etc.
  • Requisitos de hardware: Você pode verificar a placa gráfica que suporta CUDA e o poder de computação da placa gráfica no site oficial .

2. Instalação do CDUA

O processo de instalação é relativamente simples, simplesmente dividido em três etapas:

  • 1. Prepare o instalador VS e CUDA baixado do site oficial (eu uso 10.2, existe uma versão superior agora, você pode experimentar). Durante o processo de instalação, ele detectará automaticamente se uma das versões de suporte do VS foi instalada na máquina. Se a versão do VS não corresponder à versão do Cuda, a instalação não poderá prosseguir. Além disso, se o computador tiver antivírus 360 instalado (é melhor desligá-lo diretamente), haverá avisos contínuos de suspeita de modificação de vírus durante o processo de instalação e todas as operações devem ser permitidas, caso contrário, a instalação não será possível.
  • 2. Após a conclusão da instalação, você pode abrir a janela de comando, digitar pathe verificar se existem variáveis ​​de ambiente correspondentes , conforme mostrado na figura abaixo.
    insira a descrição da imagem aqui
    Caso contrário, você mesmo pode adicionar variáveis ​​de ambiente . Geralmente, existem, porque são adicionados por padrão durante a instalação. Use nvcc -Vo comando para visualizar as informações de instalação CUDA correspondentes.
    insira a descrição da imagem aqui
  • 3. Abra o VS e você descobrirá que existe uma opção adicional de NVIDA. Após selecioná-la, você pode criar um novo projeto CUDA.
    insira a descrição da imagem aqui
  • 4, Referência: https://blog.csdn.net/HaleyDong/article/details/86093520

3. Descrição simples da estrutura

  • Veja a seguir um processo simples de fluxo de dados de host (CPU) e dispositivo (pode ser chamado de CUDA ou GPU).
    insira a descrição da imagem aqui
  • Grid é a camada mais externa, chamada grid, geralmente tridimensional, que gridDim.x,gridDim.y, gridDim.zrepresenta o tamanho de cada dimensão do grid.
  • Block representa um bloco de thread na grade, geralmente tridimensional, que blockDim.x,blockDim.y,blockDim.zrepresenta o tamanho de cada dimensão do bloco de thread; blockIdx.x,blockIdx.y,blockIdx.zrepresenta o índice do bloco de thread na grade.
  • O mais interno é o encadeamento realmente usado, que também é uma distribuição tridimensional, que threadIdx.x,threadIdx.y,threadIdx.zindica o valor do índice de cada encadeamento nas direções x, y e z no bloco de encadeamento.
  • A distribuição específica de threads e blocos de threads é mostrada na figura a seguir:
    insira a descrição da imagem aqui
  • Nota: Threads em blocos diferentes não podem afetar uns aos outros! Eles estão separados fisicamente! Threads em um bloco de thread podem interagir através da memória compartilhada . Lembre-se primeiro desses dois pontos e os discutiremos em detalhes mais tarde.

4. Exemplo padrão

  • Depois de usar o VS para criar um novo projeto CUDA, um arquivo kernel.cu será exibido por padrão. A seguir, uma anotação detalhada do arquivo:
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);


//修饰符“__global__”,这个修饰符告诉编译器,被修饰的函数应该编译为在GPU而不是在CPU上运行,
__global__ void addKernel(int *c, const int *a, const int *b)
{
    
    
	//threadIdx.x,表示的是thread在x方向上的索引号
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}

int main()
{
    
    
    const int arraySize = 5;
    const int a[arraySize] = {
    
     1, 2, 3, 4, 5 };
    const int b[arraySize] = {
    
     10, 20, 30, 40, 50 };
    int c[arraySize] = {
    
     0 };

    //调用GPU运算的入口函数,返回类型是cudaError_t
    cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "addWithCuda failed!");
        return 1;
    }

    printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
        c[0], c[1], c[2], c[3], c[4]);

    //函数用于释放所有申请的显存空间和重置设备状态;
    cudaStatus = cudaDeviceReset();
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaDeviceReset failed!");
        return 1;
    }

    return 0;
}

// Helper function for using CUDA to add vectors in parallel.
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
    
    
    int *dev_a = 0;
    int *dev_b = 0;
    int *dev_c = 0;
    cudaError_t cudaStatus;

    // Choose which GPU to run on, change this on a multi-GPU system.
	// 初始化设备上的GPU,并选择ID为0的GPU执行程序
    cudaStatus = cudaSetDevice(0);
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
        goto Error;
    }

    // Allocate GPU buffers for three vectors (two input, one output).
	// 为device(GPU)中的求和数组c分配内存
    cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

	// 为device(GPU)中的数组a分配内存
    cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

	// 为device(GPU)中的数组b分配内存
    cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    // Copy input vectors from host memory to GPU buffers.
	// 将CPU中的数组a数据拷贝到GPU
    cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

	// 将CPU中的数组b数据拷贝到GPU
    cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    // Launch a kernel on the GPU with one thread for each element.
	// “<<<>>>”表示运行时配置符号,在本程序中的定义是<<<1,size>>>,表示分配了一个线程块(Block),每个线程块有分配了size个线程
	// 这种设置默认线程块和线程的维度为1,即:blockIdx.x=0,threadId.x的范围为[0,size)
	// 一共开arraySize个线程,每个线程执行一组数据的加法。
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);

    // Check for any errors launching the kernel 函数用于返回最新的一个运行时调用错误,对于任何CUDA错误,都可以通过函数
    cudaStatus = cudaGetLastError();
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));  //函数来获取错误的详细信息。
        goto Error;
    }
    
    // cudaDeviceSynchronize waits for the kernel to finish, and returns
    // any errors encountered during the launch. 函数提供了一个阻塞,用于等待所有的线程都执行完各自的计算任务,然后继续往下执行。
    cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
        goto Error;
    }

    // Copy output vector from GPU buffer to host memory. 函数用于主机内存和设备显存以及主机与主机之间,设备与设备之间相互拷贝数据
    cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
    if (cudaStatus != cudaSuccess) {
    
    
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

Error:
    cudaFree(dev_c); //函数用于释放申请的显存空间。
    cudaFree(dev_a);
    cudaFree(dev_b);
    
    return cudaStatus;
}
  • Os comentários acima já são muito detalhados, então não vou explicar seus significados específicos. Os resultados reais da execução são os seguintes:
    insira a descrição da imagem aqui
  • Um applet hello_world:
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

__global__ void hello_world(void)
{
    
    
	printf("GPU: Hello world!\n");
}
int main(int argc, char **argv)
{
    
    
	printf("CPU: Hello world!\n");
	hello_world << <1, 10 >> >();
	cudaDeviceReset();//if no this line ,it can not output hello world from gpu
	return 0;
}
  • A saída resultante:
    insira a descrição da imagem aqui

5. Referência

Blog de Tan Sheng:

outros seis

Acho que você gosta

Origin blog.csdn.net/qq_38589460/article/details/120209431
Recomendado
Clasificación