cuda学习笔记4——cuda 核函数

news2025/3/3 18:44:26

cuda学习笔记4——cuda 核函数

  • 一、CUDA规范
  • 二、核函数内部线程的使用
    • 2.1 如何启动核函数
  • demo 1:起16个线程来计算,四个线程块,每个块内四个线程例子
  • demo2

核函数是指在GPU端运行的代码,核函数内部主要干了什么?简而言之,就是规定GPU的各个线程访问哪个数据并执行什么计算。

一、CUDA规范

1、编写核函数必须遵循CUDA规范,CUDA规范如下:

  • 必须写在*.cu文件中
  • 必须以__global__限定符声明定义;
  • 返回类型必须是void;
  • 不支持可变数量的参数;
  • 核函数内部只能访问设备内存
  • 核函数内部不能使用静态变量

2、函数声明中,global、device、__host__三者区别

  • __global__修饰的函数是核函数,在设备端执行,可以从主机端调用,也可以在sm3以上的设备端调用(比如动态并行);

  • __device__修饰的函数是设备函数,在设备端执行,只能从设备端调用;

  • __host__修饰的函数是主机函数,在主机端执行,只能从主机端调用;

  • __device__和__host__可以一起使用,来表示该函数可以同时在主机端和设备端执行;

  • nvcc编译选项中添加-dc(相当于–relocatable-device-code=true --compile)时,__global__函数可以调用其它文件中的__device__函数,否则只能调用同文件中的__device__函数。

__global__描述的函数就是“被CPU调用,在GPU上运行的代码”,同时它也打通了__host__和__device__修饰的函数。

在这里插入图片描述

二、核函数内部线程的使用

CUDA从逻辑上将GPU线程分成了三个层次——线程格(grid)、线程块(block)和线程(thread)。

每个核函数对应一个线程格,一个线程格中有一个或多个线程块,一个线程块中有一个或多个线程。在一维的情况下,三者关系如图所示。
在这里插入图片描述
在这里插入图片描述

CUDA核函数中为什么将线程分为三个层次,其实是与GPU的硬件组成相关联的。在GPU硬件中本身就存在三个层次——核心、流多处理器、设备,这是一种类似于计算机集群的层次结构,而我们编写的核函数正是运行在这种层次结构上,所以核函数必须支持这三个层次,否则任务无法顺利分解,也就无法从高层次向低层次传递。

我们可以将Grid想象为一栋楼,将Block想象为楼里面的房间,而Thread就是房间里面的工作人员。这样,启动一个核函数就像将一项任务交给一栋楼来完成,楼将任务分解给各个房间,房间再将任务分解给各个工作人员。

使用线程时需要弄清楚两个值——线程全局id和核函数的线程总数。

在核函数内部有四个非常有用的内置变量——threadIdx、blockIdx、blockDim和gridDim。我们可以通过blockIdx索引到线程块,通过threadIdx索引到某个块内的线程,通过blockDim得到一个块内线程总数,通过gridDim得到一个格内块总数。

所以,在一维的情况下,计算线程全局id公式为:

线程全局id = blockIdex.x * blockDim.x + threadIdx.x

在一维的情况下,核函数内的线程总数为:

核函数的线程总数 = gridDim.x * blockDim.x

在二维的情况下,两个值的计算公式为:

线程全局id = (blockIdex.x + blockIdx.y * gridDim.x) * (blockDim.x * blockDim.y) + threadIdx.x + threadIdx.y * blockDim.x
核函数的线程总数 = gridDim.x * gridDim.y * blockDim.x * blockDim.y

以一维的方式实现两个数组逐元素相加为例,展示核函数编写方法:

__global__ void kernelAdd(float *a, float *b, float *c, unsigned int n)
{
	unsigned int tx = threadIdx.x;
	unsigned int bx = blockIdx.x;
	unsigned int index = bx*blockDim.x + tx;
	unsigned int stride = gridDim.x*blockDim.x;
	while(index<n)
	{
		c[index] = a[index] + b[index];
		index += stride;
	}
}

在kernel函数中,grid size 和block size都被存储在内置预定义变量gridDim.x 和 blockDim.x中。相应地,线程唯一id被以下两个内置预定义变量所制定:

blockIdx.x: 指定了线程在几个网格(grid)的第几个块(block),值在0到gridDim.x - 1之间。
threadIdx.x:指定了线程在第几个块(block)中的第几个线程,值在0到blockDim.x - 1之间。

2.1 如何启动核函数

启动CUDA核函数与启动C/C++函数很相似,只是额外添加了<<<>>>尖括号配置信息,尖括号内的配置信息并不是传递给核函数的,而是传递给CUDA运行时系统,告诉运行时系统如何启动核函数。
尖括号中包括四种信息,<<<块个数,线程个数,动态分配共享内存,流>>>,其中动态分配共享内存和流不是必填项。确定块个数和线程个数的一般步骤为:

先根据GPU设备的硬件资源确定一个块内的线程个数
再根据数据大小和每个线程处理数据个数确定块个数
参考代码如下:

//每个块内有256个线程
unsigned int threads = 256;
//每个线程处理4个数据,注意这4个数不是相邻的
unsigned int unroll = 4;
//根据数据量计算出块的个数
//为了保证线程数足够,在数据量的基础上加了threads-1,相当于向上取整
unsigned int blocks = (dataNum + threads -1)/threads/unroll;
cudaKernel<<<blocks, threads>>>(***);

demo 1:起16个线程来计算,四个线程块,每个块内四个线程例子

test3.cu

#include "cuda.h" 
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>

__global__ void hello_from_gpu()
{
    const int bid = blockIdx.x;
    const int tid = threadIdx.x;
    printf("Hello World from block %d and thread %d!\n", bid, tid);
}

int main(void)
{
    hello_from_gpu<<<4, 4>>>();
    cudaDeviceSynchronize();
    return 0;
}

编译

nvcc test3.cu -o test3

运行
在这里插入图片描述
因为每个网格(grid)互相独立,所以上述输出并不确定。

demo2

#include "cuda.h" 
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>

__global__ void run_on_gpu() {
	printf("GPU thread info X:%d Y:%d Z:%d\t block info X:%d Y:%d Z:%d\n",
		threadIdx.x, threadIdx.y, threadIdx.z, blockIdx.x, blockIdx.y, blockIdx.z);
}
 
int main() {
	dim3 threadsPerBlock(2, 3, 4);
	int blocksPerGrid = 1;
	run_on_gpu<<<blocksPerGrid, threadsPerBlock>>>();
	cudaDeviceReset();
	return 0;
}

在这里插入图片描述

参考:
https://blog.csdn.net/jr_Peng/article/details/125188778
https://blog.csdn.net/weixin_38346042/article/details/127155195
https://blog.csdn.net/breaksoftware/article/details/79302590
https://blog.csdn.net/xiangxianghehe/article/details/91870957

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/93903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一个《跳动的爱心》代码,纯HTML+JS,双击直接运行

HTMLJS实现的一个跳动的爱心。集合了web动画库GSAP JS、OBJ 文件加载器OBJLoader、WebGL第三方库Three.js等。效果非常棒&#xff01; 目录实际效果&#xff1a;目录结构&#xff1a;HTML代码CSS代码js代码&#xff1a;简单的修改完整文件下载实际效果&#xff1a; 由于是纯前端…

学会IDEA这些断点操作,生产问题解决的越来越快了

文章目录IDEA断点高级用法1、断点类型1&#xff09;行断点&#xff08;line breakpoints&#xff09;2&#xff09;字段断点&#xff08;field breakpoints&#xff09;3&#xff09;方法断点&#xff08;method breakpoints&#xff09;1> 加载类名上的断点2> 正常方法断…

xss-labs(WriteUp)

xss-labs 先讲讲什么是跨站脚本攻击XSS(Cross Site Scripting) XSS原理 本质上是针对html的一种注入攻击&#xff0c;没有遵循数据与代码分离的原则&#xff0c;把用户输入的数据当作代码来执行 xss跨站脚本攻击是指恶意攻击者往Web页面里插入恶意脚本代码&#xff08;包括当…

redis之codis和redis cluster对比

写在前面 codis和Redis cluster 都是Redis的集群方案&#xff0c;本文就一起来看下。 1&#xff1a;codis的组件和架构 codis的组件有4个&#xff0c;如下&#xff1a; codis server&#xff1a;基于redis进行了二次开发的组件&#xff0c;负责数据的读写 codis proxy&…

Halcon图像拼接

图像拼接在实际的应用场景很广&#xff0c;比如无人机航拍&#xff0c;遥感图像等等&#xff0c;图像拼接是进一步做图像理解基础步骤&#xff0c;拼接效果的好坏直接影响接下来的工作&#xff0c;所以一个好的图像拼接算法非常重要。 如按下图是将两张楼房图片拼接成一个图像。…

QT 学习笔记(九)

文章目录一、事件的接收和忽略1. 准备工作2. 接收和忽略二、event() 函数1. 简介2. 实例演示3. 总结三、事件过滤器四、总结&#xff08;细看&#xff09;1. 知识点汇总2. QT 的事件处理五、事件、事件的接收和忽略、event() 函数和事件过滤器代码1. 主窗口头文件 mywidget.h2.…

英语文本转语音软件哪个好?分享三个新手也能学会的工具

大家平时都是怎么学习英语的呢&#xff1f;课上老师让我们熟悉单词意思、巩固语法、多练阅读理解&#xff1b;其实通过练习听力来加强语感也很重要。很多小伙伴的阅读理解很好&#xff0c;但是听力却跟不上。这里教大家一个小技巧&#xff0c;就是在做阅读理解的时候&#xff0…

第十章TomCat详解

文章目录Tomcat的部署和启动Tomcat扮演的角色①对外&#xff1a;Web服务器②对内&#xff1a;Servlet容器深入理解为什么需要TomCat从目的开始出发遇到的问题总过程部署前提解压TomCat的目录文件启动Tomcat并访问首页如何部署一个项目访问对应的web资源专业版IDEA创建一个JavaW…

力扣(718.1143)补9.12

718.最长重复子数组 这题真的想不到。 看图的话会好懂很多。 class Solution { public int findLength(int[] nums1, int[] nums2) { int nnums1.length; int n2nums2.length; int[][] dpnew int[n1][n21]; int result0; for(int…

【区块链-智能合约工程师】第二篇:Solidity入门

文章目录Solidity极简入门HelloWorld数值类型三种函数类型函数输出变量作用域引用类型参考文章&#xff1a;一文速览2022十大智能合约开发工具 资料地址&#xff1a;WTF学院 Solidity极简入门 HelloWorld remix&#xff1a;在线智能合约开发IDE&#xff08;Integrated Deve…

DBCO-PEG-Aminooxy, Aminooxy-PEG-DBCO,氨甲基聚乙二醇环辛炔

DBCO-PEG-Aminooxy &#xff0c; Aminooxy-PEG-DBCO&#xff0c;二苯并环辛炔-聚乙二醇-氨甲基&#xff0c;氨甲基聚乙二醇环辛炔 Product specifications&#xff1a; 1.CAS No&#xff1a;N/A 2.Molecular weightMV&#xff1a;1000&#xff0c;2000&#xff0c;34000&#x…

小侃设计模式(十八)-发布订阅模式

1.概述 发布订阅模式又叫观察者模式&#xff08;Observer Pattern&#xff09;&#xff0c;它是指对象之间一对多的依赖关系&#xff0c;每当那个特定对象改变状态时&#xff0c;所有依赖于它的对象都会得到通知并被自动更新&#xff0c;它是行为型模式的一种。观察者模式内部…

被吹爆的JVM笔记,一招教会什么是JVM调优,资深架构师强推!

面试经常被问 JVM 如何调优&#xff1f;这个问题该怎么回答&#xff1f;没有实际调优经验怎么办&#xff1f; 一般面试时问JVM调优&#xff0c;主要是因为&#xff0c;这个技术并不是懂了Java就能自然懂的&#xff0c;需要明白一些底层原理&#xff0c;有一些深度。所以比较适合…

传奇客户端文件介绍注解教程,GM必备知识

传奇客户端文件介绍注解教程&#xff0c;GM必备知识 很多朋友架设微端&#xff0c;或者说修改传奇版本素材方面的内容的时候对于客户端很懵逼&#xff01; 尤其是新手朋友他并不知道传奇客户端里面哪个文件是对应什么内容的&#xff01; 今天我们将这些发出来分享给支持奇速的朋…

高压放大器在IDE压电元件及其在仿生翼中的应用

实验名称&#xff1a;IDE压电元件及其在仿生翼中应用研究 研究方向&#xff1a;仿生学 测试目的&#xff1a; 优化IDE压电元件结构和组分出发&#xff0c;目的是为了获得大驱动位移、综合性能良好的IDE驱动件。着重研究IDE压电元件的力学和电学性能、驱动特性和在仿生翼上的集成…

LVGL学习笔记(一)--- 环境搭建

LVGL全程LittleVGL&#xff0c;是一个轻量化的&#xff0c;开源的&#xff0c;用于嵌入式GUI设计的图形库。并且配合LVGL模拟器&#xff0c;可以在电脑对界面进行编辑显示&#xff0c;测试通过后再移植进嵌入式设备中&#xff0c;可以高效地进行开发。 一.嵌入式设备的移植 L…

企业请体育冠军明星代言,为何要在年前邀请

行业形势好&#xff0c;要把握住消费升级的机会&#xff1b;行业调整时&#xff0c;要抓住结构性增长的机会。实力背书、冠军代言、让品牌成为品类创新&#xff0c;中小型这几个概念与品牌自身的卖点&#xff0c;每一个放在光中小型企业的品牌上都能独当一面&#xff0c;当一款…

C/C++ 内存分布---变量所在哪个内存区域以及变量所占空间大小是多少?

C/C内存区域划分&#xff1a; 变量所在哪个内存区域以及变量所占空间大小是多少&#xff1f; int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const …

基于JavaFX+Mysql实现(PC)足球联赛评分系统【100010048】

一、引言 1. 编写目的 本文档是概要设计文档的组成部分&#xff0c;编写数据库设计文档的目的是&#xff1a;明确数据库的表名、字段名等数据信息&#xff0c;用来指导后期数据库脚本的开发。本文档的读者对象是需求人员、系统设计人员、开发人员、测试人员。 2. ### 术语表 …

GPT3动口,RT-1动手,智能机器人开卷; 代码开源!

机器学习 (ML) 研究的多个子领域&#xff08;例如计算机视觉和自然语言处理&#xff09;的最新重大进展是通过一种共享的通用方法实现的&#xff0c;该方法利用大型、多样化的数据集和能够有效吸收所有数据的表达模型。尽管已经有各种尝试将这种方法应于机器人技术&#xff0c;…