【CUDA】《CUDA编程:基础与实践》CUDA程序基本框架
创始人
2024-06-02 15:37:17
0

介绍

以向量加法为例介绍了CUDA程序得基本结构,使用xmake进行项目组织及编译。
相关代码:https://github.com/JilinLi4/LearningCUDA

一、 相关API

1. 设备CUDARuntime 初始化

在CUDA运行时API中,没有显示地初始化设备的函数。在第一次调用一个和设备管理及版本查询无关的运行时API函数时,设备将自动地初始化。

2. 设备内存的分配及释放

申请显存

cudaError_t cudaMalloc(void **address, size_t size);
  • address 待分配GPU内存的指针。
  • size 分配内存的字节数
  • 返回错误码,如果成功,返回 cudaSuccess

释放显存

cudaError_t cudaFree(void* address);

3. 主机与设备之间数据的传递

cudaError_t cudaMemcpy(void                *dst,const void          *src,size_t              count,enum cudaMemcpyKind kind
)
  • dst 是目标地址
  • src 是源地址
  • count 复制数据的字节数
  • kind 一个枚举类型的变量,取如下几个值:
    • cudaMemcpyHostToHost 从主机复制到主机
    • cudaMemcpyHostToDevice 从主机复制到设备
    • cudaMemcpyDevicetoHost 从设备复制到主机
    • cudaMemcpyDefault, 根据指针dst和src所指地址自行判断数据传输方向。

二、 核函数中数据与线程的对应

1. 使用多个线程的核函数

核函数中允许指派很多线程。因为一个GPU中有几千个计算核心,而总的线程数必须至少等于计算核心数才能充分利用GPU中的全部计算资源。
所以根据需要,在调用核函数时可以指定多个线程。比如:

func<<<2, 4>>>();

func指定了2个线程块,每个线程块中包含4个线程。总的线程数是 2×4=82 \times 4 = 82×4=8。也就是说,该程序中的核函数中代码执行方式是:“单指令-多线程”,即每一个线程都执行同一串指令。

2 使用线程索引

线程组织结构是由调用函数配置的。如:

func<<>>();

一般来说:

  • gridSize≤231−1\text{gridSize} \le 2^{31}-1gridSize≤231−1
  • blockSize≤1024\text{blockSize} \le 1024blockSize≤1024
    每个线程在核函数中都有一个唯一的身份表示。由于我们用两个参数指定线程数目。

3 核函数中的数据与线程对应

将有关数据从主机传至设备之后,就可以调用核函数在设备中进行计算了。使用以下线程布局:

  • blockSize=128\text{blockSize} = 128blockSize=128
  • gridSize=108/128\text{gridSize} = 10^8 / 128gridSize=108/128
    在这种情况下,核函数的数据与线程对应关系为:
const int n = blockDim.x * blockIdx.x + threadIdx.x;

4 是否需要使用 if 判断 n是否越界?

核函数没有使用参数N,当N是blockDim.x的整数倍时,不会引起问题,因为核函数的线程数刚好等于数组元素个数。然而,当N不是 blockDim.x的整数倍时,就有可能发生错误。例如将 N改为: 108+110^8 + 1108+1 而 block_size依然是128,如果griadSize的计算方式不变,那么必将有一个元素无法处理。所以我们应该将原来的计算方式:
gridSize=N/blockSize\text{gridSize} = N / \text{blockSize}gridSize=N/blockSize 改为
gridSize=(N+blockSize−1)/blockSize\text{gridSize} = (N + blockSize - 1) / \text{blockSize}gridSize=(N+blockSize−1)/blockSize

这样一来也会引入越界的风险,所以需要将线程索引的计算方式改为:

const int n = blockDim.x * blockIdx.x + threadIdx.x;
if(n >= N) return;
z[n] = x[n] + y[n];

三、 自定义核函数

1. 核函数的要求

  • 返回值必须是void,可以使用return 关键字,但是不可返回任何值
  • 必须使用限定符 global 也可以加上一些其他C++的限定符,如:static 限定符的次序任意
  • 函数名支持重载
  • 不可以向核函数传递非指针变量
  • 除非使用统一内存编程机制,否则传给核函数的指针必须只想设备内存
  • 核函数不可成为一个类的成员。通常做法是用一个包装函数调用核函数,将包装函数定义为一个类。
  • 核函数之间在算力 > 3.5 开始,可以调用其他核函数,也可以调用自己。
  • 核函数必须指定线程布局配置。

2. 函数执行空间标识符

  • __ global__ 称为核函数,一般由主机调用,也可以被核函数包括自己调用。
  • __ device__ 称为设备函数,只能被核函数或其他设备函数调用,在设备中执行。
  • __host __ 主机段普通的C++函数,在主机中被调用,在主机执行。可省略,存在的意义在于,可以使用**host**和 **device**修饰同一个函数,表示即可在设备端调用也可以在主机端调用,减少代码冗余。
  • 不能使用__global__ 和 __device__修饰统一个函数
  • 不能使用__host__和 __device__修饰统一个函数
  • 编译器决定把设备函数当做内联函数或非内联函数,但可以使用 __noinline__建议一个设备函数为非内联函数,也可以用 __forceinline__建议一个设备为内联函数。

3. 实例代码

3.1 code1 返回值

double __device__ add1_device(const double x, const double y) {return x + y;
}void __global__ add1(const double *x, const double *y, double *z, const int N) {const int n = blockDim.x * blockIdx.x + threadIdx.x;if(n < N) {z[n] = add1_device(x[n], y[n]);}
}

3.2 code2 传指针

void __device__ add2_device(const double x, const double y, double *z) {*z = *x + *y;
}void __global__ add1(const double *x, const double *y, double *z, const int N) {const int n = blockDim.x * blockIdx.x + threadIdx.x;if(n < N) {add1_device(x[n], y[n], &z[n]);}
}

3.3 code3 传引用

void __device__ add3_device(const double x, const double y, double &z) {z = x + y;
}void __global__ add1(const double *x, const double *y, double *z, const int N) {const int n = blockDim.x * blockIdx.x + threadIdx.x;if(n < N) {add1_device(x[n], y[n], z[n]);}
}

CUDA 错误检查

bool checkRuntime(cudaError_t e, const char* call, int line, const char *file){if (e != cudaSuccess) {INFOE("CUDA Runtime error %s # %s, code = %s [ %d ] in file %s:%d", call, cudaGetErrorString(e), cudaGetErrorName(e), e, file, line);return false;}return true;}

错误信息实例:

CUDA Runtime error cudaMemcpy(d_x, h_x, M, cudaMemcpyDeviceToDevice) # invalid argument, code = cudaErrorInvalidValue [ 1 ] in file D:\work_dir\experiment\LearningCUDA\src\add.cu:29

相关内容

热门资讯

电投能源:筹划购买白音华煤电股... 电投能源公告称,公司正在筹划发行股份及支付现金购买资产并募集配套资金暨关联交易事项,拟通过发行股份及...
福莱蒽特(605566.SH)... 格隆汇5月18日丨福莱蒽特(605566.SH)公布,公司于2025年5月16日收到股东笪良宽先生的...
全国助残日|“逗逗”的音乐逐梦... 02:41在安徽安庆,8岁女孩丁心怡(昵称 “逗逗”)用一把特制的反手吉他,弹奏出震撼人心的生命旋律...
民营银行年内降息超40次,“存... “去2进1”成为银行业的整体选择。 近段时间,沉寂了多时的“存款特种兵”话题在多个社交平台再次火热,...
越剑智能实际控制人拟减持,孙剑... 2025年5月19日,浙江越剑智能装备股份有限公司发布实际控制人减持股份计划公告,公司实际控制人之一...
139名复旦上医专家为市民健康... 中国青年报客户端讯(边欣月 张欣弛 中青报·中青网记者王烨捷)在复旦大学即将迎来建校120周年之际,...
东坡文化赋能商旅,海口东坡老码... 约见美好,东坡有请。5月16日,国内首个沉浸式东坡文化生活街区——海南海口东坡老码头焕新亮相试运行。...
突发!“乌克兰遭最大规模无人机...   据路透社5月18日报道,乌克兰官方当天表示,俄罗斯方面对乌克兰境内目标发动了大规模无人机袭击,并...
华润集团:将进一步优化在吉林省... 华润集团消息,5月17日下午,华润集团董事长王祥明在香港会见了到访的吉林省委书记、省人大常委会主任黄...
“80后”北大硕士罗婕履新甘肃... 澎湃新闻从权威渠道获悉,“80后”北京大学法学硕士、原任甘肃省庆阳市合水县副县长的罗婕,现已履新宁县...
福莱蒽特部分董事及高管拟减持不... 5月19日,杭州福莱蒽特股份有限公司发布部分董事及高级管理人减持股份计划公告,公司董事笪良宽、陈望全...
美国政府放行“更快”扳机出售 ...   炒股就看金麒麟分析师研报,权威,专业,及时,全面,助您挖掘潜力主题机会!   美国政府放行“更...
全球城市科技传播能力指数发布 ... 中国青年报客户端讯(中青报·中青网记者 王烨捷)5月17日,上海交通大学文化创新与青年发展研究院首席...
中国华电:点亮“千岛之国”璀璨... 来源:环球网 自“一带一路”倡议提出以来,中国与共建国家紧密合作、携手共建开放包容、互联互通、共同发...
继续加油!莎头组合说希望好好准... 转自:今晚报 【继续加油!#莎头组合说希望好好准备全力以...
在渝外国友人热衷英语脱口秀 台... 来源:中国新闻网 中新社重庆5月18日电 题:在渝外国友人热衷英语脱口秀 台上化身“国际段子手”作者...
当故宫遇见四川残联 联名文创美... 封面新闻记者 秦怡今年5月18日,是第三十五个全国助残日。一天以前, 2025年四川残疾人文创成果展...
熊园:服务消费是后续促消费的关... 事件:2025年4月25日,政治局会议首次提出“大力发展服务消费”。核心观点:服务消费是后续促消费的...
行业有何新特点?最新快递发展指... 2025年4月中国快递发展指数报告经测算,2025年4月中国快递发展指数为443.2,同比提升6.5...
川环科技多位股东拟减持,合计不... 2025年5月18日,四川川环科技股份有限公司发布关于股东股份减持计划的预披露公告,多位股东因个人资...