c++高性能:多进程 cuda编程

        GPU(Device)用于异构计算,CPU(Host)控制GPU。极简入门。API(CUDA Driver API+CUDA Runtime API+CUDA Libraries)

hello world

// main.cu
#include <iostream>
#include <stdio.h>

void cpu_hello(){
    printf("cpu\n");
}
__global__ void cuda_hello(){
    printf("gpu\n");
}

int main() {
    cpu_hello();
    cuda_hello<<<1,10>>>();
    cudaDeviceSynchronize();//witout this no output

    std::cout << "Hello, World!" << std::endl;
    return 0;
}

编译

  • nvcc -arch=sm_86 -o CUDATEST main.cu -run
  • 30**用sm_86编译,如果不通过可以减小
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#gpu-compilation
nvcc x.cu
 -gencode arch=compute_50,code=sm_50
 -gencode arch=compute_60,code=sm_60
 -gencode arch=compute_70,code=\"compute_70,sm_70\"

嵌入与计算能力5.06.0兼容的二进制代码以及与计算能力7.0兼容的PTX和二进制代码。

global function and others

https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cuda-enabled-gpus
__global__ void fun(){} 在设备上执行,可从主机(计算能力> 5.0设备 )调用 必须无返回值
__device__ 在设备上执行,只能从设备调用
__host__ 在主机上执行,只能从主机调用。 可省略

块和线程

2块,3线程(执行2*3次 global) add<<<2,3>>>(a,b,c) 和mpi类似,使用块号或者线程号进行细节操作
girdDim.x 块的个数
blockDim.x 一个块的线程数
blockIdx.x 块的索引
threadIdx.x 一个块中线程的索引
在所有线程中的index blockIdx.x*blockDim.x+threadIdx.x blockIdx.x*3+threadIdx.x

dim3

dim3 dimBlock(x,y);Kernel<<<2,dimBlock>>>(argv) blockIdx.x * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x;
dim3 dimGrid(x,y,z);Kernel<<<dimGrid,2>>>(argv);

全为3D的索引(第几块的第几个线程):

  • int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ blockIdx.z * gridDim.x * gridDim.y; (第几块(对应最初始公式中的blockIdx.x):blockIdx.*为系数, gridDim.*为次数)

  • int Idx = blockId * (blockDim.x * blockDim.y * blockDim.z) + (threadIdx.z * (blockDim.x * blockDim.y)) + (threadIdx.y * blockDim.x)+ threadIdx.x; (后边部分对应于最初始公式的threadIdx.x)

  • 矩阵加法 https://b23.tv/AWa1Pml

Grid-Stride循环

__global__
void saxpy(int n, float a, float *x, float *y)
{
    
    
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < n) 
        y[i] = a * x[i] + y[i];
}
__global__
void saxpy(int n, float a, float *x, float *y)
{
    
    
    for (int i = blockIdx.x * blockDim.x + threadIdx.x; 
         i < n; 
         i += blockDim.x * gridDim.x) 
      {
    
    
          y[i] = a * x[i] + y[i];
      }
}

错误处理

内存

统一内存

设备内存

c cuda c 备注
malloc cudaMalloc 申请)显存
memcpy cudaMemcpy 同步执行函数,且具有方向参数
free cudaFree 释放现存
cudaMallocHost

cudaMemPrefetchAsync(prefeatch预取)

  • UM 分配存在切页过程,cudaMallocManaged 一般搭配使用cudaMemPrefetchAsync

  • 后边的参数设置是放到cpu还是gpu

  • 写在传入global函数之前int deviceId; cudaGetDevice(&deviceId); cudaMemPrefetchAsync(pointerToSomeUMData, size, deviceId); // Prefetch to GPU device.

  • cudaMemPrefetchAsync(pointerToSomeUMData, size, cudaCpuDeviceId); // Prefetch to host.

shared memory

  • __shared__ float sh_arr[128]
    在这里插入图片描述
  • https://www.jianshu.com/p/8d17817a7488

constant

  • __constant__ 只读,全局

SM(stream multiprocessor): 流处理器

  • GPU:每个GPU有若干个SM,每个SM并行而独立运行

  • 定义流:cudaStream_t s1;

  • 创建流:cudaStreamCreate(&s1);

  • 使用流:func_kernel<<< blocks,threads,0,s1 >>>

  • 销毁流:cudaStreamDestory(s1);

  • CUDA随笔之Stream的使用

CUDA同步操作

原子操作函数

  • 和多线程的原子含义相同,自动加去锁保持线程的独占操作
__global__ void increment_atomic(int *g)
{
    
    
	int i = blockIdx.x * blockDim.x + threadIdx.x; 
	i = i % ARRAY_SIZE;  
	atomicAdd(& g[i], 1);//原来为g[i] = g[i] + 1;  
}

_syncthreads()

  • 线程块内线程同步,保证线程块内所有线程都执行到统一位置

_threadfence()
一个线程调用__threadfence后,该线程在该语句前对全局存储器或共享存储器的访问已经全部完成,执行结果对grid中的所有线程可见。

_threadfence_block()
一个线程调用__threadfence_block后,该线程在该语句前对全局存储器或者共享存储器的访问已经全部完成,执行结果对block中的所有线程可见。

以上两个函数的重要作用是,及时通知其他线程,全局内存或者共享内存内的结果已经读入或写入完成了。

cudaStreamSynchronize()/cudaEventSynchronize()
主机端代码中使用cudaThreadSynchronize():实现CPU和GPU线程同步
kernel启动后控制权将异步返回,利用该函数可以确定所有设备端线程均已运行结束

跟踪分析器

NVIDIA Nsight GPU 跟踪分析器

https://developer.nvidia.com/nvidia-visual-profiler

归约算法 规约∑

  • CUDA专家手册:GPU编程权威指南
    在这里插入图片描述

CG

tips

programming class

image processing

teaching class

猜你喜欢

转载自blog.csdn.net/ResumeProject/article/details/129504838
今日推荐