CUDA 12.4文档2 内核线程架构

news2024/11/27 17:47:23

本博客参考官方文档进行介绍,全网仅此一家进行中文翻译,走过路过不要错过。

官方网址:https://docs.nvidia.com/cuda/cuda-c-programming-guide/

本文档分成多个博客进行介绍,在本人专栏中含有所有内容:

https://blog.csdn.net/qq_33345365/category_12610860.html

CUDA 12.4为2024年3月2日发表,本专栏开始书写日期2024/4/8,当时最新版本4.1

本人会维护一个总版本,一个小章节的版本,总版本会持续更新,小版本会及时的调整错误和不合理的翻译,内容大部分使用chatGPT 4翻译,部分内容人工调整


开始编辑时间:2024/4/8;最后编辑时间:2024/4/10

5.1 内核Kernels

CUDA C++通过允许程序员定义C++函数,称为内核,当被调用时,这些函数由N个不同的CUDA线程并行执行N次,而不是像常规的C++函数那样只执行一次。

内核使用 global 声明说明符定义,并且给定内核调用的执行该内核的CUDA线程数使用新的 <<<...>>>执行配置语法指定。执行内核的每个线程都被赋予一个在内核内通过内置变量可以访问的唯一线程ID。

作为示例,以下样本代码使用内置变量 threadIdx,将两个大小为N的向量A和B相加,并将结果存储到向量C中:

__global__ void VecAdd(float* A, float* B, float* C)
{
    int i = threadIdx.x;
    C[i] = A[i] + B[i];
}
int main()
{
    ...
    ∕∕ Kernel invocation with N threads
    VecAdd<<<1, N>>>(A, B, C);
    ...
}

在这里,执行VecAdd()的N个线程中的每一个都执行一次成对的加法。

5.2 线程架构

为了方便,threadIdx是一个三组件向量,因此可以使用一维、二维或三维的线程索引来标识线程,形成一个一维、二维或三维的线程块,称为线程块。这为在像向量、矩阵或体积等域中的元素之间调用计算提供了一种自然的方式。

线程的索引和线程ID之间的关系很直接:对于一维的块,它们是相同的;对于大小为(Dx, Dy)的二维块,索引为(x, y)的线程的线程ID为(x + y Dx);对于大小为(Dx, Dy, Dz)的三维块,索引为(x, y, z)的线程的线程ID为(x + y Dx + z Dx Dy)。

下面的代码就是一个例子,它将两个NxN大小的矩阵A和B加在一起,并将结果存储到矩阵C中:

__global__ void MatAdd(float A[N][N], float B[N][N],
                       float C[N][N])
{
    int i = threadIdx.x;
    int j = threadIdx.y;
    C[i][j] = A[i][j] + B[i][j];
}
int main()
{
    ...
    ∕∕ Kernel invocation with one block of N * N * 1 threads
    int numBlocks = 1;
    dim3 threadsPerBlock(N, N);
    MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
    ...
}

每个块的线程数量是有限的,因为所有的线程都应该存在于同一个流式多处理器核心上,并且必须共享这个核心的有限的内存资源。在当前的GPU上,一个线程块可能包含多达1024个线程。

然而,一个内核可以被多个形状相同的线程块执行,这样总的线程数量等于每个块的线程数量乘以块的数量。

块被组织成一个一维、二维或三维的线程块网格,如图4所示。网格中线程块的数量通常由被处理的数据的大小决定,这通常超过了系统中的处理器数量。

在这里插入图片描述

图4:线程块的网格

<<<...>>>语法中指定的每个块的线程数量和每个网格的块数量可以是int类型或dim3类型。可以像上面的例子那样指定二维的块或网格。

网格内的每个块都可以通过一个一维、二维或三维的唯一索引来标识,这个索引在内核中可以通过内置的blockIdx变量访问。线程块的维度在内核中可以通过内置的blockDim变量访问。

下面的代码是将前面的MatAdd()示例扩展为处理多个块的情况:

// 内核定义
__global__ void MatAdd(float A[N][N], float B[N][N],
                       float C[N][N])
{
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    int j = blockIdx.y * blockDim.y + threadIdx.y;
    if (i < N && j < N)
        C[i][j] = A[i][j] + B[i][j];
}
int main()
{
    ...
    ∕∕ Kernel invocation
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N ∕ threadsPerBlock.x, N ∕ threadsPerBlock.y);
    MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
    ...
}

线程块的大小为16x16(256个线程),尽管在这种情况下是任意的,但这是一个常见的选择。如前所述,创建足够的块,每个矩阵元素有一个线程。为了简单起见,这个例子假设每个维度的网格线程数可以被该维度的块线程数整除,尽管并非一定是这种情况。

线程块需要独立执行:它们必须能够按任何顺序执行,可以并行也可以串行。这种独立性要求允许线程块按任何顺序在任何数量的核心上调度,如图3所示,使程序员能写出随核心数量扩展的代码。

块内的线程可以通过共享一些共享内存和同步它们的执行来协调内存访问来合作。更精确地说,可以在内核中通过调用__syncthreads()内在函数来指定同步点;__syncthreads()充当一个屏障,所有的块内线程都必须在这里等待,直到允许任何线程继续。共享内存章节给出了一个使用共享内存的例子。除了__syncthreads(),协作组API章节还提供了一整套丰富的线程同步原语。

为了有效的合作,预计共享内存是接近每个处理器核心的低延迟内存(很像L1缓存),并且__syncthreads()预计是轻量级的。

5.2.1 线程块集群 Thread Block Clusters

设计线程数:共享内存上同步 < 线程块集群内同步 < 全局同步

随着NVIDIA计算能力9.0的引入,CUDA编程模型引入了一个叫做线程块群集的可选等级层次,这些都是由线程块构成的。与线程块中的线程保证在流式多处理器上被并行调度类似,线程块群集中的线程块也保证在GPU处理集群(GPC)上进行并行调度。

与线程块类似,群集也以一维、二维或三维的方式组织,如图5所示。群集中的线程块数量可以由用户定义,CUDA中支持以8个线程块为单位的群集大小作为最大限制。注意,在GPU硬件或MIG配置中,如果太小以致不能支持8个多处理器,那么最大群集大小将相应减小。识别这些较小的配置,以及支持线程块群集大小超过8的较大配置,是架构特定的,并可以使用cudaOccupancyMaxPotentialClusterSize API进行查询。

在这里插入图片描述

图5:线程块集群的网格

在使用群集支持启动的内核中,为了兼容性,gridDim变量仍然表示线程块数量的大小。可以通过使用Cluster Group API找到群集中块的等级。

线程块群集可以通过使用__cluster_dims__(X,Y,Z)的编译器时间内核属性或使用CUDA内核启动API cudaLaunchKernelEx在内核中启用。下面的例子展示了如何使用编译器时间内核属性启动一个群集。使用内核属性的群集大小在编译时固定,然后可以使用经典的<<<,>>>来启动内核。如果内核使用编译时群集大小,那么在启动内核时,群集大小不能被修改。

∕∕ 内核定义, Compile time cluster size 2 in X-dimension and 1 in Y and Z dimension
__global__ void __cluster_dims__(2, 1, 1) cluster_kernel(float *input, float* output)
{ }
int main()
{
    float *input, *output;
    ∕∕ Kernel invocation with compile time cluster size
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N ∕ threadsPerBlock.x, N ∕ threadsPerBlock.y);
    ∕∕ The grid dimension is not affected by cluster launch, and is still enumerated
    ∕∕ using number of blocks.
    ∕∕ The grid dimension must be a multiple of cluster size.
    cluster_kernel<<<numBlocks, threadsPerBlock>>>(input, output);
}

线程块群集大小也可以在运行时设置,并通过CUDA内核启动API cudaLaunchKernelEx启动内核。下面的代码示例展示了如何使用可扩展API启动一个群集内核。

∕∕ 内核定义
∕∕ No compile time attribute attached to the kernel
__global__ void cluster_kernel(float *input, float* output)
{ }
int main()
{
    float *input, *output;
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N ∕ threadsPerBlock.x, N ∕ threadsPerBlock.y);
    ∕∕ Kernel invocation with runtime cluster size
    {
        cudaLaunchConfig_t config = {0};
        ∕∕ The grid dimension is not affected by cluster launch, and is still enumerated
        ∕∕ using number of blocks.
        ∕∕ The grid dimension should be a multiple of cluster size.
        config.gridDim = numBlocks;
        config.blockDim = threadsPerBlock;
        cudaLaunchAttribute attribute[1];
        attribute[0].id = cudaLaunchAttributeClusterDimension;
        attribute[0].val.clusterDim.x = 2; ∕∕ Cluster size in X-dimension
        attribute[0].val.clusterDim.y = 1;
        attribute[0].val.clusterDim.z = 1;
        config.attrs = attribute;
        config.numAttrs = 1;
        cudaLaunchKernelEx(&config, cluster_kernel, input, output);
    }
}

在具有9.0计算能力的GPU中,群集中的所有线程块都保证在单个GPU处理集群(GPC)上进行协同调度,并允许群集中的线程块使用Cluster Group API cluster.sync()执行硬件支持的同步。群集组还提供成员函数,使用num_threads()num_blocks()API分别查询群集组大小,以线程数量或块数量表示。群集组中的线程或块的排名可以使用dim_threads()dim_blocks()API分别进行查询。

属于群集的线程块可以访问分布式共享内存。群集中的线程块具有读取、写入和执行分布式共享内存中任何地址的原子操作的能力。分布式共享内存章节给出了在分布式共享内存中执行直方图的示例。

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

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

相关文章

分享|如何知道自己是不是大数据信用黑名单?

随着大数据技术在金融贷前审核环节中的运用&#xff0c;早在多年前都形成了大数据信用&#xff0c;大数据信用作为辅助的风控工具&#xff0c;作用变得十分重要&#xff0c;其中大数据黑名单就是大数据差的重要一种&#xff0c;那如何知道自己是不是大数据信用黑名单呢?本文详…

Linux:gcc

Linux&#xff1a;gcc gcc概述语言发展史gcc的编译过程预处理编译汇编 gcc的链接过程动态库与静态库 gcc概述 GCC&#xff08;英文全拼&#xff1a;GNU Compiler Collection&#xff09;是 GNU 工具链的主要组成部分&#xff0c;是一套以 GPL 和 LGPL 许可证发布的程序语言编译…

python--正则表达式,元字符,反义符,转义符,位数问题

正则表达式&#xff08;regular expression&#xff09;&#xff1a; 为什么使用正则表达式&#xff1a; 在软件开发过程中&#xff0c;经常会涉及到大量的关键字等各种字符串的操作&#xff0c;使用正则表达式能很大程度的简化开发的复杂度和开发的效率&#xff0c;所以pytho…

麒麟V10安装Redis6.2.6

1、下载redis安装包 Redis各版本下载&#xff1a;https://download.redis.io/releases/ 2、将下载后的.tar.gz压缩包上传到到服务器自定义文件夹下 3、 解压文件 tar -zxvf redis-6.2.6.tar.gzmv redis-6.2.6 redis4、安装redis 在redis文件夹下输入make指令 cd /opt/redi…

【React】react 初学增删改查购物车案例

界面 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>react-购物车案例</title><…

华为OD机试 - 机器人搬砖 - 二分查找(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

centos 7.9 nginx本地化安装,把镜像改成阿里云

1.把centos7.9系统切换到阿里云的镜像源 1.1.先备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2.下载新的CentOS-Base.repo配置文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo特别…

css面试题--定位与浮动

1、为什么需要清除浮动&#xff1f; 在非IE浏览器下&#xff0c;容器不设高度且子元素浮动时&#xff0c;容器高度不能被内容撑开&#xff0c;内容会溢出到容器外面而影响布局。这种现象被称为浮动。 浮动的原理&#xff1a;浮动元素脱离文档流&#xff0c;不占用空间&#xff…

39-性能分析(下):APIServer性能测试和调优实战

在API上线之前&#xff0c;我们需要知道API的性能&#xff0c;以便知道API服务器所能承载的最大请求量、性能瓶颈&#xff0c;再根据业务对性能的要求&#xff0c;来对API进行性能调优或者扩缩容。通过这些&#xff0c;可以使API稳定地对外提供服务&#xff0c;并且让请求在合理…

java算法day49 | 动态规划part10 ● 121. 买卖股票的最佳时机 ● 122.买卖股票的最佳时机II

121. 买卖股票的最佳时机 class Solution {public int maxProfit(int[] prices) {//1、定义dp数组&#xff0c;表示第i天持股票的状态dp[i][0]表示持有股票dp[i][1]表示不持有股票int[][] dpnew int[prices.length][2];//3、初始化数组dp[0][1]0;dp[0][0]-prices[0];//4、遍历顺…

Linux--进程的概念(二)

目录 一、进程的优先级1.1 基本概念1.2 查看进程优先级1.3 PRI&NI1.4 如何更改进程的优先级1.4.1 用top命令更改进程的nice1.4.2 用renice命令更改进程的nice 1.5 其他概念 二、环境变量2.1 基本概念2.2 常见的环境变量2.3 查看环境变量2.3.1 测试PATH2.3.2 测试HOME2.3.3 …

Android14之智能指针的弱引用、强引用、弱指针、强指针用法区别及代码实例(二百零五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

LTC4054 充电指示灯转灯电路

由于这个芯片只有CHRG# 引脚&#xff0c;不像4056 那样两个引脚能分别接一个LED&#xff0c;要实现充电指示就必须自己整整外围电路。先说明一下&#xff0c;网上常见的这种接法&#xff1a; 一个LED 直连CHRG# 引脚&#xff0c;我试了是不行的&#xff0c;即使充满电&#xff…

【国际会议火热征稿】2024年应用经济学、管理科学与社会国际学术会议(ICAEMSS 2024)

会议简介 2024年应用经济学、管理科学与社会国际学术会议将聚焦应用经济学和管理科学的前沿问题&#xff0c;深入探讨社会变革中的经济管理与科学应用。参会者将分享最新研究成果&#xff0c;交流学术观点&#xff0c;共同探索经济、管理与社会的融合发展之路。本次会议旨在推…

Zotero + Markdown论文工作流

文章目录 关键步骤Zotero Better BibTeXObsidian Citekey Plugin & WrittingPandoc Export 关键步骤 在Zotero中&#xff0c;使用Better BibTex生成.bib文件&#xff0c;用于提取索引信息。由于后续需要使用pandoc将markdown转换为word或者LaTeX&#xff0c;所以Better Bi…

记Kubernetes(k8s):访问 Prometheus UI界面:Warning: Error fetching server time

记Kubernetes&#xff08;k8s&#xff09;&#xff1a;访问 Prometheus UI界面:Warning: Error fetching server time 1、报错详情2、解决3、再次访问 PrometheusUI界面 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、报错详情 Warning:…

Linux启动过程、启动脚本目录介绍及检测思路分析

一、Linux系统启动过程 1、启动流程&#xff1a; Linux系统的启动过程可以分为5个阶段&#xff1a;内核的引导、运行init、系统初始化、建立终端、用户登录系统。 2、init程序的类型&#xff1a; 1&#xff09;SysV&#xff1a;init&#xff0c;CentOS 5之前&#xff0c;配置文…

socuretree远程分支没有同步问题

1、选择命令行模式 2、输入git remote update origin --prune 并回车 git remote update origin --prune 是 Git 命令&#xff0c;用于从远程仓库更新本地分支&#xff0c;并删除本地已经不存在于远程仓库的远程跟踪分支

ADP-2-20+ 信号调节 20MHz-2GHzRF功分器 合路器

ADP-2-20 是一款由Mini-Circuits公司出产的功分器&#xff08;power divider&#xff09;。这款功分器的工作温度规模为-40C至85C&#xff0c;贮存温度规模为-55C至100C。作为分路器&#xff0c;它的电源输入最高可达1W&#xff0c;内部功耗最大为0.125W。假如超越这些限制&…

【Cesium学习笔记】一、加载Cesium并更换天地图底图

【Cesium学习笔记】一、加载Cesium 一、加载Cesium二、用Viewer显示地球三、更换天地图底图 Ps:本教程所有代码于同一个工程中&#xff0c;运行npm run dev默认首页为App.vue&#xff0c;只需替换App.vue的内容即可切换不同页面。 一、加载Cesium 本项目使用nvm管理node版本&…