当前位置: 首页 > news >正文

CUDA编程(二) —— CUDA编程模型

CUDA编程模型

常见的CUDA术语

      我们怎么写一个能在GPU跑的程序或函数呢?

      通过关键字就可以表示某个程序在CPU上跑还是在GPU上跑!如下表所示,比如我们用__global__定义一个kernel函数,就是CPU上调用,GPU上执行,注意__global__函数的返回值必须设置为void。

CPU和GPU间的数据传输怎么写?

GPU内存分配回收内存的函数接口:

  • cudaMalloc(): 在设备端分配global memory
  • cudaFree(): 释放存储空间

CPU的数据和GPU端数据做数据传输的函数接口是一样的,他们通过传递的函数实参(枚举类型)来表示传输方向:

cudaMemcpy(void *dst, void *src, size_t nbytes, enum cudaMemcpyKind direction)

enum cudaMemcpyKind:

  • cudaMemcpyHostToDevice(CPU到GPU)
  • cudaMemcpyDeviceToHost(GPU到CPU)
  • cudaMemcpyDeviceToDevice(GPU到GPU)

怎么用代码表示线程组织模型?

我们可以用dim3类来表示网格和线程块的组织方式,网格grid可以表示为一维和二维格式,线程块block可以表示为一维、二维和三维的数据格式。

dim3 DimGrid(100, 50);  //5000个线程块,维度是100*50
dim3 DimBlock(4, 8, 8);  //每个线层块内包含256个线程,线程块内的维度是4*8*8

怎样计算线程号

①如果使用N个线程块,每一个线程块只有一个线程

dim3 dimGrid(N);
dim3 dimBlock(1);

此时的线程号的计算方式就是

threadId = blockIdx.x;

其中threadId的取值范围为0到N-1。对于这种情况,我们可以将其看作是一个列向量,列向量中的每一行对应一个线程块。列向量中每一行只有1个元素,对应一个线程。

②使用M×N个线程块,每个线程块1个线程

由于线程块是2维的,故可以看做是一个M*N的2维矩阵,其线程号有两个维度,即:

dim3 dimGrid(M,N);
dim3 dimBlock(1);

其中

blockIdx.x 取值0到M-1

blcokIdx.y 取值0到N-1

这种情况一般用于处理2维数据结构,比如2维图像。每一个像素用一个线程来处理,此时需要线程号来映射图像像素的对应位置,如

pos = blockIdx.y * blcokDim.x + blockIdx.x; //其中gridDim.x等于M

③使用一个线程块,该线程具有N个线程

dim3 dimGrid(1);
dim3 dimBlock(N);

此时线程号的计算方式为

threadId = threadIdx.x;

其中threadId的范围是0到N-1,对于这种情况,可以看做是一个行向量,行向量中的每一个元素的每一个元素对应着一个线程。

④使用M个线程块,每个线程块内含有N个线程

dim3 dimGrid(M);
dim3 dimBlock(N);

这种情况,可以把它想象成二维矩阵,矩阵的行与线程块对应,矩阵的列与线程编号对应,那线程号的计算方式为

threadId = threadIdx.x + blcokIdx*blockDim.x;

上面其实就是把二维的索引空间转换为一维索引空间的过程。

⑤使用M×N的二维线程块,每一个线程块具有P×Q个线程

dim3 dimGrid(M, N);
dim3 dimBlock(P, Q);

这种情况其实是我们遇到的最多情况,特别适用于处理具有二维数据结构的算法,比如图像处理领域。

其索引有两个维度

threadId.x = blockIdx.x*blockDim.x+threadIdx.x;
threadId.y = blockIdx.y*blockDim.y+threadIdx.y;

上述公式就是把线程和线程块的索引映射为图像像素坐标的计算方法。

CUDA代码示例

test.cu

#include "device_launch_parameters.h"
#include <iostream>

int main()
{
    int deviceCount;
    cudaGetDeviceCount(&deviceCount);
    for(int i=0;i<deviceCount;i++)
    {
        cudaDeviceProp devProp;
        cudaGetDeviceProperties(&devProp, i);
        std::cout << "使用GPU device " << i << ": " << devProp.name << std::endl;
        std::cout << "设备全局内存总量: " << devProp.totalGlobalMem / 1024 / 1024 << "MB" << std::endl;
        std::cout << "SM的数量:" << devProp.multiProcessorCount << std::endl;
        std::cout << "每个线程块的共享内存大小:" << devProp.sharedMemPerBlock / 1024.0 << " KB" << std::endl;
        std::cout << "每个线程块的最大线程数:" << devProp.maxThreadsPerBlock << std::endl;
        std::cout << "设备上一个线程块(Block)种可用的32位寄存器数量: " << devProp.regsPerBlock << std::endl;
        std::cout << "每个EM的最大线程数:" << devProp.maxThreadsPerMultiProcessor << std::endl;
        std::cout << "每个EM的最大线程束数:" << devProp.maxThreadsPerMultiProcessor / 32 << std::endl;
        std::cout << "设备上多处理器的数量: " << devProp.multiProcessorCount << std::endl;
        std::cout << "======================================================" << std::endl;     
        
    }
    return 0;
}

所以.cu其实是C++代码

编译

nvcc test.cu -o test

输出结果:

使用GPU device 0: GeForce RTX 2080 Ti
设备全局内存总量: 11019MB
SM的数量:68
每个线程块的共享内存大小:48 KB
每个线程块的最大线程数:1024
设备上一个线程块(Block)种可用的32位寄存器数量: 65536
每个EM的最大线程数:1024
每个EM的最大线程束数:32
设备上多处理器的数量: 68
======================================================
使用GPU device 1: GeForce RTX 2080 Ti
设备全局内存总量: 11019MB
SM的数量:68
每个线程块的共享内存大小:48 KB
每个线程块的最大线程数:1024
设备上一个线程块(Block)种可用的32位寄存器数量: 65536
每个EM的最大线程数:1024
每个EM的最大线程束数:32
设备上多处理器的数量: 68
======================================================
......

10块GPU一共输出10组

相关文章:

  • Python Fastai框架
  • ubuntu安装docker
  • Linux(ubuntu)(十三) —— (系统)服务管理 (systemctlservicechkconfig)服务的运行级别(Runlevel)
  • linux 文件/目录名 颜色
  • nvcc(CUDA编译器)
  • docker使用GPU(nvidia-docker)
  • Pytorch分布式训练/多卡训练(二) —— Data Parallel并行(DDP)(2.3)(torch.multiprocessing(spawn) Apex)
  • OpenStack
  • Python logging日志模块
  • CUDA编程(三) —— 编程实践
  • Python函数传参(*星号)
  • Python调用函数带括号和不带括号的区别
  • Microsoft CMT 系统
  • Python导入上层目录中的包(..) / 导入同级目录的包 (相对导入)
  • mxnet导出模型json和params文件
  • 03Go 类型总结
  • conda常用的命令
  • ECS应用管理最佳实践
  • Go 语言编译器的 //go: 详解
  • KMP算法及优化
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • sessionStorage和localStorage
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • windows下使用nginx调试简介
  • 阿里研究院入选中国企业智库系统影响力榜
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 简单数学运算程序(不定期更新)
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 力扣(LeetCode)22
  • 探索 JS 中的模块化
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • ​​​​​​​​​​​​​​Γ函数
  • ​linux启动进程的方式
  • # 数论-逆元
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (4)Elastix图像配准:3D图像
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (生成器)yield与(迭代器)generator
  • (四)linux文件内容查看
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (一)SpringBoot3---尚硅谷总结
  • (转)Linux下编译安装log4cxx
  • (转)scrum常见工具列表
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .chm格式文件如何阅读
  • .NET MVC之AOP
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .net操作Excel出错解决
  • .net程序集学习心得
  • .net反编译工具
  • /etc/sudoers (root权限管理)
  • ??在JSP中,java和JavaScript如何交互?
  • @31省区市高考时间表来了,祝考试成功