CUDA C编程权威指南:2.1-CUDA编程模型
本文主要通过例子介绍了CUDA异构编程模型,需要说明的是Grid、Block和Thread都是逻辑结构,不是物理结构。实现例子代码参考文献[2],只需要把相应章节对应的CMakeLists.txt文件拷贝到CMake项目根目录下面即可运行。
1.Grid、Block和Thread间的关系
GPU中最重要的2种内存是全局内存和共享内存,前者类似于CPU系统内存,而后者类似于CPU缓存,然后GPU共享内存可由CUDA C内核直接控制。GPU简化的内存结构,如下所示:
[外链图片转存中…(img-zjlJfwmi-1696733256161)]
由一个内核启动所产生的所有thread统称为一个grid,同一个grid中的所有thread共享相同的全局内存空间。一个grid由多个block构成,一个block包含一组thread,同一block内的thread通过同步、共享内存方式进行线程协作,不同block内的thread不能协作。由block和grid构成的2层的thread层次结构,如下所示:
[外链图片转存中…(img-eGUKo819-1696733256163)]
CUDA可以组织3维的grid和block。blockIdx表示线程块在线程格内的索引,threadIdx表示块内的线程索引;blockDim表示每个线程块中的线程数,gridDim表示网格中的线程块数。这些变量允许开发人员在编写CUDA代码时,从逻辑上管理和组织线程块和网格的大小,从而优化并行执行的效率。如下所示:
[外链图片转存中…(img-wcgjwV8R-1696733256164)]
2.检查网格和块的索引和维度(checkDimension.cu)
确定grid和block的方法为先确定block的大小,然后根据实际数据大小和block大小的基础上计算grid维度,如下所示:
// 检查网格和块的索引和维度
# include <cuda_runtime.h>
# include <stdio.h>__global__ void checkIndex(void) {// gridDim表示grid的维度,blockDim表示block的维度,grid维度表示grid中block的数量,block维度表示block中thread的数量printf("threadIdx:(%d, %d, %d) blockIdx:(%d, %d, %d) blockDim:(%d, %d, %d) ""gridDim:(%d, %d, %d)\n", threadIdx.x, threadIdx.y, threadIdx.z,blockIdx.x, blockIdx.y, blockIdx.z, blockDim.x, blockDim.y, blockDim.z,gridDim.x, gridDim.y, gridDim.z); // printf函数只支持Fermi及以上版本的GPU架构,因此编译的时候需要加上-arch=sm_20编译器选项
}int main(int argc, char** argv) {// 定义全部数据元素int nElem = 6;// 定义grid和block的结构dim3 block(3); // 表示一个block中有3个线程dim3 grid((nElem + block.x - 1) / block.x); // 表示grid中有2个block// 检查grid和block的维度(host端)printf("grid.x %d grid.y %d grid.z %d\n", grid.x, grid.y, grid.z);printf("block.x %d block.y %d block.z %d\n", block.x, block.y, block.z);// 检查grid和block的维度(device端)checkIndex<<<grid, block>>>();// 离开之前重置设备cudaDeviceReset();return 0;
}
输出结果如下所示:
threadIdx:(0, 0, 0) blockIdx:(1, 0, 0) blockDim:(3, 1, 1) gridDim:(2, 1, 1)
threadIdx:(1, 0, 0) blockIdx:(1, 0, 0) blockDim:(3, 1, 1) gridDim:(2, 1, 1)
threadIdx:(2, 0, 0) blockIdx:(1, 0, 0) blockDim:(3, 1, 1) gridDim:(2, 1, 1)
threadIdx:(0, 0, 0) blockIdx:(0, 0, 0) blockDim:(3, 1, 1) gridDim:(2, 1, 1)
threadIdx:(1, 0, 0) blockIdx:(0, 0, 0) blockDim:(3, 1, 1) gridDim:(2, 1, 1)
threadIdx:(2, 0, 0) blockIdx:(0, 0, 0) blockDim:(3, 1, 1) gridDim:(2, 1, 1)
grid.x 2 grid.y 1 grid.z 1
block.x 3 block.y 1 block.z 1
3.在主机上定义网格和块的大小(defineGridBlock.cu)
接下来通过一个1维网格和1维块讲解当block大小变化时,gird的size也随之变化,如下所示:
#include <cuda_runtime.h>
#include <stdio.h>int main(int argc, char** argv) {// 定义全部数据元素int cElem = 1024;// 定义grid和block结构dim3 block(1024);dim3 grid((cElem + block.x - 1) / block.x);printf("grid.x %d grid.y %d grid.z %d\n", grid.x, grid.y, grid.z);// 重置blockblock.x = 512;grid.x = (cElem + block.x - 1) / block.x;printf("grid.x %d grid.y %d grid.z %d\n", grid.x, grid.y, grid.z);// 重置blockblock.x = 256;grid.x = (cElem + block.x - 1) / block.x;printf("grid.x %d grid.y %d grid.z %d\n", grid.x, grid.y, grid.z);// 重置blockblock.x = 128;grid.x = (cElem + block.x - 1) / block.x;printf("grid.x %d grid.y %d grid.z %d\n", grid.x, grid.y, grid.z);// 离开前重置devicecudaDeviceReset();return 0;
}
输出结果,如下所示:
grid.x 1 grid.y 1 grid.z 1
grid.x 2 grid.y 1 grid.z 1
grid.x 4 grid.y 1 grid.z 1
grid.x 8 grid.y 1 grid.z 1
4.基于GPU的向量加法(sumArraysOnGPU-small-case.cu)
#include <cuda_runtime.h>
#include <stdio.h>#define CHECK(call)
//{
// const cudaError_t error = call;
// if (error != cudaSuccess)
// {
// printf("Error: %s:%d, ", __FILE__, __LINE__);
// printf("code:%d, reason: %s\n", error, cudaGetErrorString(error));
// exit(1);
// }
//}void checkResult(float *hostRef, float *gpuRef, const int N)
{double epsilon = 1.0E-8;bool match = 1;for (int i = 0; i < N; i++){if (abs(hostRef[i] - gpuRef[i]) > epsilon){match = 0;printf("Arrays do not match!\n");printf("host %5.2f gpu %5.2f at current %d\n", hostRef[i], gpuRef[i], i);break;}}if (match) printf("Arrays match.\n\n");
}void initialData(float *ip, int size)
{// generate different seed for random numbertime_t t;srand((unsigned int) time(&t));for (int i = 0; i < size; i++){ip[i] = (float) (rand() & 0xFF) / 10.0f;}
}void sumArraysOnHost(float *A, float *B, float *C, const int N)
{for (int idx = 0; idx < N; idx++){C[idx] = A[idx] + B[idx];}
}__global__ void sumArraysOnGPU(float *A, float *B, float *C)
{// int i = threadIdx.x; // 获取线程索引int i = blockIdx.x * blockDim.x + threadIdx.x; // 获取线程索引printf("threadIdx.x: %d, blockIdx.x: %d, blockDim.x: %d\n", threadIdx.x, blockIdx.x, blockDim.x);C[i] = A[i] + B[i]; // 计算
}int main(int argc, char** argv) {printf("%s Starting...\n", argv[0]);// 设置设备int dev = 0;cudaSetDevice(dev);// 设置vectors数据大小int nElem = 32;printf("Vector size %d\n", nElem);// 分配主机内存size_t nBytes = nElem * sizeof(float);float *h_A, *h_B, *hostRef, *gpuRef; // 定义主机内存指针h_A = (float *) malloc(nBytes); // 分配主机内存h_B = (float *) malloc(nBytes); // 分配主机内存hostRef = (float *) malloc(nBytes); // 分配主机内存,用于存储host端计算结果gpuRef = (float *) malloc(nBytes); // 分配主机内存,用于存储device端计算结果// 初始化主机数据initialData(h_A, nElem);initialData(h_B, nElem);memset(hostRef, 0, nBytes); // 将hostRef清零memset(gpuRef, 0, nBytes); // 将gpuRef清零// 分配设备全局内存float *d_A, *d_B, *d_C; // 定义设备内存指针cudaMalloc((float **) &d_A, nBytes); // 分配设备内存cudaMalloc((float **) &d_B, nBytes); // 分配设备内存cudaMalloc((float **) &d_C, nBytes); // 分配设备内存// 从主机内存拷贝数据到设备内存cudaMemcpy(d_A, h_A, nBytes, cudaMemcpyHostToDevice); // d_A表示目标地址,h_A表示源地址,nBytes表示拷贝字节数,cudaMemcpyHostToDevice表示拷贝方向cudaMemcpy(d_B, h_B, nBytes, cudaMemcpyHostToDevice); // d_B表示目标地址,h_B表示源地址,nBytes表示拷贝字节数,cudaMemcpyHostToDevice表示拷贝方向// 在host端调用kerneldim3 block(nElem); // 定义block维度dim3 grid(nElem / block.x); // 定义grid维度sumArraysOnGPU<<<grid, block>>>(d_A, d_B, d_C); // 调用kernel,<<<grid, block>>>表示执行配置,d_A, d_B, d_C表示kernel参数printf("Execution configuration <<<%d, %d>>>\n", grid.x, block.x); // 打印执行配置// 拷贝device结果到host内存cudaMemcpy(gpuRef, d_C, nBytes, cudaMemcpyDeviceToHost); // gpuRef表示目标地址,d_C表示源地址,nBytes表示拷贝字节数,cudaMemcpyDeviceToHost表示拷贝方向// 在host端计算结果sumArraysOnHost(h_A, h_B, hostRef, nElem);// 检查device结果checkResult(hostRef, gpuRef, nElem);// 释放设备内存cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// 释放主机内存free(h_A);free(h_B);free(hostRef);free(gpuRef);return 0;
}
输出结果如下所示:
[外链图片转存中…(img-yLMkSnjk-1696733256164)]
threadIdx.x: 0, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 1, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 2, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 3, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 4, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 5, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 6, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 7, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 8, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 9, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 10, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 11, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 12, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 13, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 14, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 15, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 16, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 17, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 18, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 19, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 20, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 21, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 22, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 23, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 24, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 25, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 26, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 27, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 28, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 29, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 30, blockIdx.x: 0, blockDim.x: 32
threadIdx.x: 31, blockIdx.x: 0, blockDim.x: 32
L:\20200706_C++\C++Program\20231003_ClionProgram\cmake-build-debug\20231003_ClionProgram.exe Starting...
Vector size 32
Execution configuration <<<1, 32>>>
Arrays match.
5.其它知识点
(1)host和device同步
核函数的调用和主机线程是异步的,即核函数调用结束后,控制权立即返回给主机端,可以调用cudaDeviceSynchronize(void)函数来强制主机端程序等待所有的核函数执行结束。当使用cudaMemcpy函数在host和device间拷贝数据时,host端隐式同步,即host端程序必须等待数据拷贝完成后才能继续执行程序。需要说明的是,所有CUDA核函数的启动都是异步的,当CUDA内核调用完成后,控制权立即返回给CPU。
(2)函数类型限定符
函数类型限定符指定一个函数在host上执行还是在device上执行,以及可被host调用还是被device调用,函数类型限定符如下所示:
[外链图片转存中…(img-dj0ukz7N-1696733256165)]
说明:__device__和__host__限定符可以一起使用,这样可同时在host和device端进行编译。
参考文献:
[1]《CUDA C编程权威指南》
[2]2.1-CUDA编程模型概述:https://github.com/ai408/nlp-engineering/tree/main/20230917_NLP工程化/20231004_高性能计算/20231003_CUDA编程/20231003_CUDA_C编程权威指南/2-CUDA编程模型/2.1-CUDA编程模型概述
相关文章:
CUDA C编程权威指南:2.1-CUDA编程模型
本文主要通过例子介绍了CUDA异构编程模型,需要说明的是Grid、Block和Thread都是逻辑结构,不是物理结构。实现例子代码参考文献[2],只需要把相应章节对应的CMakeLists.txt文件拷贝到CMake项目根目录下面即可运行。 1.Grid、Block和Thread间的…...
两条记录合并成一条记录
两条记录合并成一条记录 两条记录 val4,type_idlevel 和 val6,type_idtypeId 合并成一条记录 level4,typeId6 可以使用 条件聚合语句 CASE WHEN … 和 MAX 函数来实现。假设有以下 my_table 表: --------------------- | id | val | type_id | --------------…...
vue3 + typescript + vite + naive ui + tailwindcss + jsx 仿苹果桌面系统
基于 vue3.x typescript vite naive ui tailwindcss jsx vue-router pinia,项目使用 tsx 作为模版输出,全程没有使用vue提供的SFC, 仿macos桌面前端项目,开源免费模版,希望减少工作量和学习新技术,希…...
揭秘,用软件一秒识别纸质表格数字,找到你想要的一串数字
要将纸质表格的数字快速用软件识别并找出特定的一串数字,以下是三种常用的方案: 方案一:使用OCR软件识别和搜索功能 1. 扫描纸质表格并保存为图像或PDF格式。 2. 使用OCR(光学字符识别)软件,如金鸣表格文字…...
解析图片文件格式
图片文件幻数 关于JPEG格式 二进制形式打开文件,文件开始字节为FF D8,文件结束两字节为FF D9 JPEG 文件有两种不同的元数据格式:JFIF 和 EXIF。 JFIF 以 ff d8 ff e0 开头,EXIF 以 ff d8 ff e1 开头。 代码示例 private static…...
新的“HTTP/2 快速重置”零日攻击打破了 DDoS 记录
自 8 月份以来,一种名为“HTTP/2 快速重置”的新 DDoS(分布式拒绝服务)技术已被作为零日漏洞积极利用,其规模打破了之前的所有记录。 Amazon Web Services、Cloudflare 和 Google 今天联合发布了有关零日技术的消息,他…...
现代化战机之路:美国空军U-2侦察机基于Jenkins和k8s的CI/CD架构演进
▲ 点击上方"DevOps和k8s全栈技术"关注公众 华为北京研究所Q27大楼 随着技术的不断进步,军事领域也在积极采纳现代化工具来提高战备水平和效率。美国空军的U-2侦察机项目是一个鲜明的例子,它成功地借助Jenkins和Kubernetes(k8s&…...
Linux中常用的软件:Squid
在Linux系统中,有许多不同的代理软件可供选择。本文将比较两个常用的代理软件: Squid。我们将介绍它们的特点、使用场景和优缺点,帮助您选择适合自己需求的代理软件。 - 支持多种加密协议,包括AES、ChaCha20和RC4等,可…...
Ali MaxCompute SDK
ALI MC 文件读写 public abstract BufferedInputStream readResourceFileAsStream(String var1) throws IOException;LocalExecutionContext.java Overridepublic BufferedInputStream readResourceFileAsStream(String resourceName) throws IOException {try {return wareHou…...
解决element中table在页面切换时候表格底部出现空白
activated(){ this.$refs.table.doLayout() } activated()是Vue中一个很重要的生命周期函数,它是在组件大概率会被复用时调用的。当组件被复用时,原来的组件的数据和状态必须得到保留。activated()函数能够保持组件在别处被活化时的状态数据。 activat…...
Vue中对路由的进阶学习
路由进阶 文章目录 路由进阶1、路由的封装抽离2、声明式导航2.1、导航链接2.2、高亮类名2.3、跳转传参2.4、动态路由参数可选符 3、Vue路由--重定向4、Vue路由--4045、Vue路由–模式设置6、编程式导航6.1、基本跳转6.2、跳转传参 路由基础入门 1、路由的封装抽离 问题&#x…...
Vuex的同步存值与取值及异步请求
前言 1.概念 Vuex是一个用于管理Vue.js应用程序中状态的状态管理模式和库。Vue.js是一个流行的JavaScript框架,用于构建用户界面,而Vuex则专门用于管理应用程序的状态,以确保状态在整个应用程序中保持一致和可维护。 2.Vuex的特点…...
【Python爬虫 js渲染思路一】
Python爬虫 破解js渲染思路一 当我们在谈论网页js渲染的时候,我们在谈论什么 js渲染网页,从某种程度来说,是指单纯的http请求,返回的文本数据,与我们在浏览器看到的内容,相距甚远.其可包括为以下几点&…...
智慧安防AI视频智能分析云平台EasyCVR加密机授权小tips
视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、…...
C# Windows 窗体控件中的边距和填充
可以将 Margin 属性、Left、Top、Right、Bottom 的每个方面设置为不同的值,也可以使用 All 属性将它们全部设置为相同的值。 在代码中设置Margin,元素的左边设置为5个单位、上边设置为10个单位、右边设置为15个单位和下边设置为20个单位。 TextBox myT…...
腾讯云2核4G轻量服务器5M带宽支持多少人同时在线?
腾讯云轻量2核4G5M带宽服务器支持多少人在线访问?5M带宽下载速度峰值可达640KB/秒,阿腾云以搭建网站为例,假设优化后平均大小为60KB,则5M带宽可支撑10个用户同时在1秒内打开网站,从CPU内存的角度,网站程序效…...
01 初识FPGA
01 初识FPGA 一.FPGA是什么 FPGA(Filed Programmable Gate Array),现场可编程门阵列,一种以数字电路为主的集成芯片,属于可编程逻辑器件PLD的一种。 1.1 两大巨头 Xilinx(赛灵思)Altera(阿尔特拉&#…...
设备巡检管理系统与隐患排查治理
如何才能将设备巡检做细做规范呢? 1.制定巡检制度和流程:通过建立明确的巡检制度和流程,并将其纳入企业的安全管理体系中。利用凡尔码平台制定一个详细的巡检计划,包括巡检的时间、地点、内容、检查方法和注意事项等,帮…...
linux之cpu模拟负载程序
工作中我们经常会遇到这样的问题,需要模拟cpu的负载程序,例如模拟cpu占有率抬升10%、20%、50%、70%等,那这样的程序应该如何实现呢?它的原理是什么样的呢? 思想 创建一个应用程序,该应用程序的作用可以根…...
zookeeper节点数据类型介绍及集群搭建
一、zookeeper介绍 zookeeper官网:Apache ZooKeeper zookeeper是一个分布式协调框架,保证的是CP,即一致性和分区容错性;zookeeper是一个分布式文件存储系统,文件节点可以存储数据,监听子文件节点等可以实…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
