Notas de estudio CUDA (1)

Notas de estudio CUDA (1)

Tutoriales de referencia :
1. QINZHAOYU/CudaSteps
2. Programación CUDA (1) Conceptos básicos
3. CUDA C/C++ Tutorial 1: Aceleración de aplicaciones

1. Hardware GPU y herramientas de desarrollo de programas CUDA

En una plataforma de computación heterogénea compuesta por CPU y GPU, la CPU que desempeña un papel de control generalmente se denomina 主机(host),
y la GPU que desempeña un papel de aceleración se denomina 设备(device).
Tanto el host como el dispositivo tienen su propia DRAM, que generalmente están conectadas por un bus PCIe.

La potencia informática de la GPU no es equivalente al rendimiento informático; un parámetro importante para caracterizar el rendimiento informático es el valor máximo de las operaciones de punto flotante (FLOPS).
Hay puntos de precisión simple y precisión doble para picos aritméticos de coma flotante. Para las GPU de la serie Tesla, el FLOPS de doble precisión es generalmente la mitad de la precisión simple; para las GPU de la serie GeForce, el FLOPS de doble precisión es generalmente 1/32 de la precisión simple.

CUDA proporciona dos capas de API, a saber CUDA 驱动APIy CUDA 运行时API.


2. Organización de subprocesos en CUDA

1. nvcc compila código C++

nvcc -o ./bin/hello_cu.exe hello.cu 
./bin/hello_cu.exe
nvcc: hello world!

2. Uso de funciones del kernel en programas CUDA

Un programa CUDA que utiliza la GPU tiene código de host y código de dispositivo.
La llamada del host al dispositivo se 核函数realiza a través de (función kernel).

int main()
{
    主机代码
    核函数的调用
    主机代码

    return 0;
}

3. Características de la función del núcleo:

1) Se debe agregar el límite global
; 2) El tipo de retorno debe ser void type void
3) La función kernel no es compatible con iostream de C++
4) El método de llamada de la función kernel:

hello_from_gpu<<<1, 1>>>   # <<<grid size, block size>>>

Cuando el host llama a una función del kernel, debe indicar cuántos subprocesos se asignan en el dispositivo. Los subprocesos en la función kernel a menudo se organizan en varios bloques de subprocesos:
A. El primer número entre corchetes triples es 线程块的个数(número de bloque de subprocesos)
B. El segundo número entre corchetes triples es 每个线程块中的线程数(número de subprocesos por bloque).
Todos los bloques de subprocesos de una función kernel forman una cuadrícula (cuadrícula), y el número de bloques de subprocesos se denomina tamaño de cuadrícula (tamaño de cuadrícula); cada bloque de subprocesos contiene el mismo número de subprocesos, y este número se denomina tamaño de bloque de subprocesos ( tamaño de bloque). tamaño).
Por lo tanto, el número total de subprocesos de la función kernel es el tamaño de cuadrícula * tamaño de bloque de subprocesos.

Después de llamar a la función del kernel, llame a la función API de tiempo de ejecución de CUDA para sincronizar el host y el dispositivo.Función: espere a que el código del dispositivo (GPU) termine de ejecutarse y luego continúe ejecutándose en la CPU.

cudaDeviceSynchronize(); // 与其他并行化的代码类似,核函数启动方式为异步,即 CPU 代码将继续执行而不会等待核函数执行完成;但此行代码:可以让Host 代码(CPU) 等待 Device 代码(GPU) 执行完毕,再在CPU上继续执行。

4. Organización de subprocesos de CUDA

4.1 Organización de subprocesos de CUDA

La cantidad total de subprocesos de la función del kernel debe ser al menos igual a la cantidad de núcleos de cómputo (la computadora actual tiene 16 núcleos) antes de que sea posible utilizar completamente todos los recursos de cómputo de la GPU.

hello_from_gpu<<<2, 4>>>

El tamaño de la cuadrícula es 2, el tamaño del bloque de subprocesos es 4 y el número total de subprocesos es 8. El modo de ejecución del código en la función del núcleo es “单指令-多线程”que cada subproceso ejecuta la misma cadena de códigos.
A partir de la arquitectura Kepler, el tamaño de cuadrícula máximo permitido es 2^31 - 1 (cuadrícula unidimensional) y el tamaño de bloque de subprocesos máximo permitido es 2^10 (1024).

El número total de subprocesos se puede determinar mediante dos parámetros:

  1. gridDim.x, el tamaño de la cuadrícula;
  2. blockDim.x, el tamaño del bloque de subprocesos;

La identidad de cada subproceso se puede determinar mediante dos parámetros:

  1. blockIdx.x, es decir, el índice de bloque de subprocesos de un subproceso en una cuadrícula, [0, gridDm.x);
  2. threadIdx.x, es decir, el índice de subprocesos de un subproceso en un bloque de subprocesos, [0, blockDim.x);

Tanto la cuadrícula como el bloque de subprocesos se pueden extender a una estructura tridimensional (cada eje tiene un valor predeterminado de 1):

  1. rejilla 3D grid_size(gridDim.x, gridDim.y, gridDim.z);
  2. Bloque de hilo tridimensional block_size(blockDim.x, blockDim.y, blockDim.z);

En consecuencia, bajo la estructura tridimensional, los parámetros de identidad de cada hilo:

  1. Id. de bloque de subprocesos (blockIdx.x, blockIdx.y, blockIdx.z);
  2. Id. de subproceso (threadIdx.x, threadIdx.y, threadIdx.z);

El ID del subproceso de cuadrícula multidimensional en el bloque de subprocesos:

tid = threadIdx.z * (blockDim.x * blockDim.y)  // 当前线程块上前面的所有线程数
    + threadIdx.y * (blockDim.x)               // 当前线程块上当前面上前面行的所有线程数
    + threadIdx.x                              // 当前线程块上当前面上当前行的线程数

El ID del bloque de subprocesos de cuadrícula multidimensional en la cuadrícula:

bid = blockIdx.z * (gridDim.x * gridDim.y)
    + blockIdx.y * (gridDim.x)
    + blockIdx.x

Los subprocesos en un bloque de subprocesos también se pueden organizar en diferentes urdimbres de subprocesos, cada uno de los cuales consta de 32 subprocesos consecutivos.
Para las GPU desde la arquitectura Kepler hasta la arquitectura Turing, el valor máximo permitido del tamaño de la cuadrícula en las direcciones x, y, z (2^31 - 1, 2^16 - 1, 2^16 -1); el valor máximo permitido del tamaño del bloque de subprocesos en las direcciones x, y, z (1024, 1024, 64), mientras se requiere un subproceso bloque Hay como máximo 1024hilos.

Expansión :
<<<>>> operador forma de parámetro de configuración de ejecución completa de la función del kernel es<<<Dg, Db, Ns, S>>>

1) El parámetro Dg se utiliza para definir la dimensión y el tamaño de toda la cuadrícula, es decir, cuántos bloques tiene una cuadrícula. Es tipo dim3. Dim3 Dg(Dg.x, Dg.y, 1) significa que cada fila en la grilla tiene bloques Dg.x, cada columna tiene bloques Dg.y, y la tercera dimensión siempre es 1 (actualmente, una función kernel solo tiene uno red). Hay bloques Dg.x Dg.y en toda la cuadrícula, y el valor máximo de Dg.x y Dg.y es 65535.
2) El parámetro Db se usa para definir la dimensión y el tamaño de un bloque, es decir, cuántos hilos tiene un bloque. Es tipo dim3. Dim3 Db(Db.x, Db.y, Db.z) significa que hay subprocesos Db.x en cada fila en todo el bloque, subprocesos Db.y en cada columna y la altura es Db.z. El valor máximo de Db.x y Db.y es 512 y el valor máximo de Db.z es 62. Hay
subprocesos Db.x Db.y*Db.z en un bloque. El valor máximo de este producto es 768 para hardware con capacidad informática 1.0 y 1.1, y el valor máximo admitido por hardware con capacidad informática 1.2 y 1.3 es 1024.
3) El parámetro Ns es un parámetro opcional, que se utiliza para establecer el valor máximo de cada bloque, excepto la Memoria compartida asignada estáticamente, y 能动态分配的shared memory大小la unidad es byte. Cuando no se requiere asignación dinámica, el valor es 0 o se omite.
4) El parámetro S es un cudaStream_t类型parámetro opcional con un valor inicial de cero, que indica en qué flujo se encuentra la función del núcleo.

4.2 Archivos de encabezado CUDA

El sufijo de los archivos de encabezado CUDA generalmente es ".cuh";
al mismo tiempo, se pueden incluir archivos de encabezado c/cpp ".h" y ".hpp", y el compilador nvcc incluirá automáticamente los archivos de encabezado cuda necesarios,
como como <cuda.h>, <cuda_runtime.h>, mientras que el primero también incluye el archivo de encabezado de C++ <stdlib.h>.

4.3 Usar nvcc para compilar el programa CUDA

nvcc primero separará todos los códigos fuente en 主机代码y 设备代码;
el código host es totalmente compatible con la sintaxis de C++, mientras que el código del dispositivo solo la admite parcialmente.

Proceso de compilación :
nvcc primero compilará el código del dispositivo en código de pseudoensamblaje PTX (ejecución paralela de subprocesos) y luego lo compilará en código de objeto binario cubin.
Al compilar en código PTX, se requiere la opción -arch=compute_XYpara especificar la capacidad informática de una arquitectura virtual; al compilar en código cubin, se requiere la opción para -code=sm_ZWespecificar la capacidad informática de una arquitectura real para determinar la GPU que puede usar el archivo ejecutable.

La potencia de cómputo de la arquitectura real debe ser mayor o igual a la potencia de cómputo de la arquitectura virtual, por ejemplo:

-arch=compute_35  -code=sm_60  (right)
-arch=compute_60  -code=sm_35  (wrong)

4.4 Arquitectura de la tarjeta gráfica y potencia informática

Los módulos Jetson Orin incluyen lo siguiente:
GPU NVIDIA Ampere Architecture con hasta 2048 núcleos CUDA y hasta 64 núcleos Tensor

Ampere (CUDA 11 ~presente) SM80 o SM_80, compute_80 ​​​​– NVIDIA A100 (ya no se llama Tesla – GA100), NVIDIA DGX-A100 SM86
o SM_86, compute_86 – (desde CUDA 11.1 en adelante) Tesla GA10x, RTX Ampere – RTX 3080 , GA102-RTX 3090, RTX A6000, RTX A40


3. El marco básico de un programa CUDA simple

1. Para un programa cuda con un solo archivo fuente, el marco básico es:

包含头文件

定义常量或宏

声明 c++ 自定义函数和 cuda 核函数的原型

int main()
{
    1. 分配主机和设备内存
    2. 初始化主机中数据
    3. 将某些数据从主机复制到设备
    4. 调用核函数在设备中计算
    5. 将某些数据从设备复制到主机
    6. 释放主机和设备内存
}

2. Requisitos de la función del núcleo CUDA:

1) El tipo de devolución debe ser nulo, pero se puede usar return en la función (pero no puede devolver ningún valor); 2)
Se debe usar el calificador glolbal , y también se puede agregar el calificador c ++;
3) La función principal admite el mecanismo de sobrecarga de C++;
4) La función kernel no admite un número variable de listas de parámetros, es decir, se debe determinar el número de parámetros;
5) En general, la matriz (puntero) que se pasa a la función kernel debe apuntar al memoria del dispositivo (excepto el "mecanismo de programación de memoria unificada");
6 ) 核函数不可成为一个类的成员(que generalmente terminan enLa función contenedora llama a la función kernel, defina la función contenedora como un miembro de la clase);
7) Antes de la capacidad informática 3.5, las funciones del kernel no pueden llamarse entre sí; después de eso, pueden llamarse a través del mecanismo "paralelo dinámico";
8) Ya sea que se llamen desde el host o desde el dispositivo, la función del kernel se ejecuta en el dispositivo ("<<<,>>>" especifica la configuración de ejecución).

3. Función de dispositivo personalizado

Una función del núcleo puede llamar a una función personalizada sin configuración de ejecución, es decir, una función de dispositivo.
La función del dispositivo se ejecuta en el dispositivo y se llama en el dispositivo (gpu), mientras que la función del núcleo se ejecuta en el dispositivo y se llama en el host (cpu).
Reglas gramaticales :
1) __global__Las funciones modificadas se denominan funciones del kernel, que generalmente son llamadas por el host y ejecutadas en el dispositivo;
2) __device__Las funciones modificadas se denominan funciones del dispositivo, que solo pueden ser llamadas por funciones del kernel u otras funciones del dispositivo y ejecutadas en el dispositivo;
3 ) __host__para modificar la función ordinaria de c++ del segmento del host, que se llama y ejecuta en el host y, por lo general, se puede omitir; 4 )
y se puede usar para __host__modificar __device__la función al mismo tiempo, lo que reduce la redundancia del código. En este momento, el compilador 5) respectivamente en el host y compilará la función en el dispositivo;
6) No puede usar __global__y __device__modificar la función al mismo tiempo;
7) No puede usar __global__y __host__modificar la función al mismo tiempo ;
9) Puede usar para __noinline__sugerir que el compilador no debe tratar una función de dispositivo como una función en línea;
10) Puede usar para __forceinline__sugerir que el compilador Una función de dispositivo se tratará como una función en línea.
11) Las funciones del dispositivo pueden tener valores de retorno.

Continuará... Artículo siguiente:

Supongo que te gusta

Origin blog.csdn.net/weixin_36354875/article/details/125683170
Recomendado
Clasificación