5 线程网格、线程块以及线程(2)

news2024/11/13 8:04:15

5.3 线程块

即使有512个线程,也不能让你在GPU上斩获颇丰。对很多在CPU上编程的编程人员来说,这似乎是一个很大的数量,但其实在GPU上编程的时候,512个线程并不一定会让你获得很高的效益,对于GPU而言,通常我们可能会创建成千上万个并发线程来实现设备上的高吞吐量。   

与前面线程一节所讲的一样,num_blocks 是内核调用中<<<和>>>的第一个参数:

kernel_function<<<num blocks,num_threads>>>(paraml,param2,…)

如果我们将这个参数从1修改成2,那么就是告诉GPU硬件,我们将启动两倍于之前线程数量的线程,例如:

some_kernel_func<<<2,128>>>(a,b,c);

这将会调用名为some_kernel_func的GPU函数共2x128次,每次都是不同的线程。然而,这样做通常会使 thread_idx参数的计算变得更加复杂,而thread_idx通常又用来表示数组的位置下标。因此,我们之前简单的内核就要稍作调整:  

__global__ void some_kernel_func(int* const a, constint* const b, const int * const c)

{

const unsigned int thread idx = (blockIdx.x * blockDim.x)+ threadIdx.x;

a[thread_idx] = b[thread_idx] * c[thread_idx];

    }

为了计算 thread_idx这个参数,我们必须考虑线程块的数量。对第一个线程块而言blockIdx.x是0,因此 thread_idx直接就等于之前使用过的 threadIdx.x,然而,对于第二个线程块而言,它的blockIdx.x的值是1,blockDim.x表示本例中所要求的每个线程块启动的线程数量,它的值是128,那么对第二个线程块而言,在计算thread_idx时,要在threadIdx.x的基础上加上一个1x128 线程的基地址。

不知道你有没有注意到,在介绍线程块加一个基地址的时候有一个错误?现在我们一共启动了256个线程,数组的下标是0~255,如果不更改数组的大小,那么第128个元素~256个元素,将会出现元素访问和写人越界的问题。这种数组越界错误是不会被编译器发现的,程序代码也会根据数组a边界之外的内容来正常执行,因此在调用内核函数的时候要尽量小心,避免这种内存越界访问错误。

对这个例子而言,我们使用一个128字节大小的数组,并将启动的两个线程块中的每个线程块的线程数量改成 64:

some_kernel_func<<<2,64 >>>(a,b,c);

你可以从图 5-8中看到它的表示。

图5-8 线程块映射的地址空间

注意,尽管我们启动了两个线程块,但thread_idx这个参数依然同之前一样等于数组的下标。那么,我们使用线程块的意义究竟在哪?在这个简单的例子中,很明显,没有什么意义。但是在很多现实问题中,我们将不仅仅只处理512个元素,很可能更多。事实上,查看线程块的数量限制,你会发现可以使用65536个线程块

如果使用65536个线程块,每个线程块启动512个线程,那么我们一共可以调度33 554432(大约3350万)个线程。如果每个线程块启动512个线程,那么每个SM最多可以处理3个线程块。事实上,这个限制是基于每个SM最多能处理的线程数量。在最新的费米架构的硬件上,每个SM每次最多能执行1536个线程,而在G80的硬件上,只能执行768 个线程。

如果你打算在费米架构的硬件上每个线程块调度1024个线程,那么65536个线程块-共就能调度接近6400万个线程,但很不幸的是,如果每个线程块是1024个线程,那每个SM 每次最多运行一个线程块。造成的结果是,除非每个SM 分配执行的线程块数量是一个以上,否则在单个GPU上你将需要65536个SM来执行你的程序。目前,在任何GPU上SM 的最大数目都是30。因此,在线程块需要的SM 数量超出硬件支持的 SM 数量之前CUDA 提供了一定的处理机制。这正是CUDA的迷人之处--它能扩展为上千个执行单元。并行的极限仅受限于应用程序可以分解的并行程度。

假定现在有6400万个线程,每个线程处理数组的一个元素,那么一共可以处理6400万个数组元素。假定数组的每个元素都是一个单精度的浮点数,那么每个元素占4个字节,总共大约需要2亿5600万个字节,即约 256MB 数据存储空间。而几乎所有的GPU都至少支持这个大小的内存空间。因此,仅使用线程和线程块就可以达到相当大量的并行性和数据覆盖。

很多人会担心大规模数据集问题。它们可能是 GB 级、TB 级,甚至 PB级的大规模数据。对于这类问题,这里提供多个解决方案。我们通常会选择一个线程处理多个元素或者使用线程块的其他维度来处理。接下来的小节我们将会进行详细介绍。

线程块的分配

为了确保能够真正地了解线程块的分配,接下来我们写一个简短的内核程序来输出线程块线程线程束线程全局标号到屏幕上。现在,除非你使用的是3.2版本以上的SDK,否则内核中是不支持printf的。因此,我们可以将数据传送回CPU端然后输出到控制台窗口,内核的代码如下:

__g1obal__ void what_is_my_id(unsigned int*const block.
unsigned int*const thread.
unsigned int*const warp,
unsigned int*const calc thread)
{
    /* Thread id is block index * block size + thread offset into the block */
    const unsigned int thread_idx = (blockIdx.x * blockDim.x) + threadIdx.x;

    block[thread_idx] = blockIdx.x;
    thread[thread_idx] = threadIdx.x;

    /* Calculate warp using built in variable warpSize */
    warp[thread_idx] = threadIdx.x/warpSize;
    calc_thread[thread_idx] = thread_idx;
}

在CPU端,我们需要执行下面的一部分代码来为数组在 GPU上分配内存以及将算好的数组数据从 GPU 端复制回来并在 CPU 端显示。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

__global__ void what_is_my_id(unsigned int * const block,
    unsigned int*const thread,
    unsigned int*const warp,
    unsigned int*const calc thread)
{

    /* Thread id is block index * block size + thread offset into the block */
    const unsigned int thread_idx = (blockIdx.x * blockDim.x) + threadIdx.x;

    block[thread_idx] = blockIdx.x;
    thread[thread_idx] = threadIdx.x;

    /* Calculate warp using built in variable warpSize */
    warp[thread_idx] = threadIdx.x/warpSize:

    calc_thread[thread_idx] = thread_idx;
}

#define ARRAY_SIZE 128
#define ARRAY_SIZE_IN_BYTES(sizeof(unsigned int)*(ARRAY_SIZE))

/* Declare statically four arrays of ARRAY_SIZE each */
unsigned int cpu_blocK[ARRAY_SIZE];
unsigned int cpu_thread[ARRAY_SIZE]:
unsigned int cpu_warp[ARRAY_SIZE];
unsigned int cpu_calc_thread[ARRAY_SIZE];

int main(void)
{
    /*Total thread count = 2 * 64 = 128 */
    const unsigned int num_blocks = 2:
    const unsigned int num_threads =64:
    char ch;

    /* Declare pointers for GPU based params */
    unsigned int *gpu_block;
    unsigned int *gpu_thread;
    unsigned int *gpu_warp;// 线程束
    unsigned int *gpu_calc_thread;

    /* Declare loop counter for use later */
    unsigned int i:

    /* Allocate four arrays on the GPu */
    cudaMalloc((void **)&gpu_block, ARRAY_SIZE_IN_BYTES);
    cudaMa11oc((void **)&gpu_thread, ARRAY_SIZE_IN_BYTES);
    cudaMa11oc((void **)&gpu_warp, ARRAY_SIZE_IN_BYTES);
    cudaMa11oc((void **)&gpu_calc_thread, ARRAY_SIZE_IN_BYTES);

    /*Execute our kernel*/
    what_is_my_id<<<num_blocks, num_threads>>>(gpu_block, gpu_thread,             gpu_warpgpu_calc_thread);

    /*Copy back the gpu results to the CPU */
    cudaMemcpy(cpu_block,gpu_block,ARRAY_SIZE_IN_BYTES,cudaMemcpyDeviceToHost);
    cudaMemcpy(cpu_thread,gpu_thread,ARRAY_SIZE_IN_BYTES,cudaMemcpyDeviceToHost);
    cudaMemcpy(cpu_warp,gpu_warp,ARRAY_SIZE_IN_BYTEScudaMemcpyDeviceToHost);
    cudaMemcpy(cpu_calc_thread, gpu_ calc_thread,         ARRAY_SIZE_IN_BYTEScudaMemcpyDeviceToHost);

    /* Free the arrays on the GPU as now we're done with them */
    cudaFree(gpu_block);
    cudaFree(gpu_thread);
    cudaFree(gpu_warp);
    cudaFree(gpu_calc_thread);


    /*Iterate through the arrays and print */
    for(i = 0;i < ARRAY_SIZE; ++i)
    {
        printf("Calculated Thread:%3u-Block:%2u-Warp %2u- Thread %3u\n"
        cpu_calc_thread[i],cpu_block[i], cpu_warp[i], cpu_thread[i]);


        ch = getch();
    }
}

在这个例子中,我们可以看到线程块按照线程块的编号紧密相连。由于处理的是一维数组,所以我们对线程块采用相同的布局便可简单解决问题。以下是此程序的输出结果:

正如我们计算的那样,线程索引是0~127。一共有两个线程块,每个线程块包含64个线程,每个线程块内部线程的索引为0~63。一个线程块包含两个线程束。

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

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

相关文章

PTA L2-026 小字辈

本题给定一个庞大家族的家谱&#xff0c;要请你给出最小一辈的名单。 输入格式&#xff1a; 输入在第一行给出家族人口总数 N&#xff08;不超过 100 000 的正整数&#xff09; —— 简单起见&#xff0c;我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号&#xff0c;…

使用阿里CICD流水线打包Java项目到阿里的docker镜像私仓,并自动部署到服务器启动服务

文章目录 使用阿里CICD流水线打包Java项目到阿里的docker镜像私仓&#xff0c;并自动部署到服务器启动服务1、功能原理实现2、将自己的Java项目通过Git上传到阿里的代码仓库中&#xff0c;也可以通过绑定Gitee或者GitHub账号进行导入3、创建自己的阿里云镜像私仓3、进入阿里的C…

【SpringBoot】如何定义接口

定义get接口 使用GetMapping定义一个基本get接口 RestController //表示定义一个json格式返回给前端 public class test {private Map<String,Object> map new HashMap<>();GetMapping(value "/test") //定义接口路径public Object userInfo(Strin…

C语言自定义类型结构体

variable adj.易变的&#xff0c;多变的&#xff1b;时好时坏的&#xff1b;可变的&#xff0c;可调节的&#xff1b; &#xff08;数&#xff09;&#xff08;数字&#xff09;变量的&#xff1b;&#xff08;植&#xff0c;动&#xff09;变异的&#xff0c;变型的&#xff1…

普发Pfeiffer分子泵TPH2101PUP/PCCT安装使用维护说明

普发Pfeiffer分子泵TPH2101PUP/PCCT安装使用维护说明

代码随想录算法训练营第31天| 455.分发饼干、376. 摆动序列、53. 最大子序和

455.分发饼干 题目链接&#xff1a;分发饼干 题目描述&#xff1a;假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最…

FloodFill算法——图像渲染

文章目录 题目解析题目内容解读 算法解析代码解析 题目解析 首先我们先来看看题目&#xff1a;图像渲染 题目内容解读 我们来解读一下题目内容这个题目的意思其实就是有一个如下图所示的二维矩阵 这个题目的意思在这类题目中也是非常标准的&#xff0c;就是给我们一个二维数…

docker的部署与安装以及部署一个docker(容器)应用及docker容器常出现的问题

docker 架构图 一、docker的部署与安装 1、在 CentOS 上安装 Docker 移除旧版本&#xff08;如果有的话&#xff09;&#xff1a;sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-…

用vscode调试cpp程序相关操作记录

需要在服务器上用vscode调试cpp程序&#xff0c;写此记录launch.json配置和相关步骤错误导致的问题 1.在需要运行程序的服务器上安装C/C Extension Pack&#xff08;之前只在本地装了&#xff09;&#xff0c;可以支持调试C/C应用程序(设置断点&#xff0c;单步执行&#xff0c…

爱帮供应链邀您参观2024杭州快递物流供应链与技术装备展览会

2024年7月8-10日 | 杭州国际博览中心 同期举办&#xff1a;2024中国数字物流技术与应用展 2024国际电商物流包装产业展 2024新能源商用车、物流车展 展会介绍 本届展会致力于全面展示快递物流上下游领域的创新解决方案&#xff0c;涵盖快递物流供应链、智能装备、AGV机器人…

正信晟锦:多年不联系的好友借钱怎么办

多年不见的老友突然出现&#xff0c;带着迫切的求助信息——借钱。面对这样的请求&#xff0c;我们该如何应对? 当一个多年未联络的朋友突然出现请求借款时&#xff0c;这确实是一个棘手的问题。一方面&#xff0c;我们可能对旧日友情存有怀念与不舍;另一方面&#xff0c;时间…

Windows10安装SSH

Linux运维工具-ywtool 目录 1. 打开设置2. 应用3.管理可选功能4.添加功能5.安装OpenSSH服务器6.测试是否安装成功 1. 打开设置 windows桌面按下"win l"键调出"设置"2. 应用 点击"应用"3.管理可选功能 点击"管理可选功能"4.添加功能…

【系统架构师】-计算机网络

1、网络的划分 网络性能指标&#xff1a;速率、带宽(频带宽度或传送线路速率)、吞吐量、时延、往返时间、利用率。 网络非性能指标&#xff1a;费用、质量、标准化、可靠性、可扩展性、可升级性、易管理性和可维护性。 总线型(利用率低、干扰大、价格低)、 星型(交换机转发形…

【笔记】以论文发表形式通俗理解 TCP/IP模型

【笔记】以论文发表形式通俗理解 TCP/IP模型 前言TCP/IP模型理论通俗理解 前言 在网络基础学习过程中&#xff0c;以前只对TCP/IP理解个字面&#xff0c;网上查一下能知道个字面意思&#xff0c;但是连起来到底是什么意思&#xff0c;还是一知半解的&#xff0c;停留在表面&am…

阿里云4核8G服务器多少钱一年?

阿里云4核8G服务器优惠价格955元一年&#xff0c;配置为ECS通用算力型u1实例&#xff08;ecs.u1-c1m2.xlarge&#xff09;4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选&#xff0c;CPU采用Intel(R) Xeon(R) Platinum处理器&#xff0c;阿里云活动链接 aliyunfuwuq…

document.documentElement.clientHeight与document.body.clientHeight的区别

网页可见区域高&#xff1a;document.body.clientHeight 网页正文全文高&#xff1a;document.body.scrollHeight clientHeight&#xff1a; 表示可视区域高度&#xff0c; 包括padding但不包括border、水平滚动条、margin的元素的高度 offsetHeight&#xff1a;表示可视区域高…

Jackson 2.x 系列【1】概述

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 前言2. 什么是 JSON3. 常用 Java JSON 库4. Jackson4.1 简介4.2 套件4.3 模块4.…

RIPGeo代码理解(五)utils.py( 辅助函数)第一部分

​ 代码链接:RIPGeo代码实现 ├── lib # 包含模型(model)实现文件 │ |── layers.py # 注意力机制的代码。 │ |── model.py # TrustGeo的核心源代码。 │ |── sublayers.py # layer.py的支持文件。 │ |── utils.p…

【MySQL】2.MySQL数据库的基本操作

目录 数据库基本操作 查看数据库信息 查看数据库结构 显示数据表的结构&#xff08;字段&#xff09; 常用的数据类型 数据库管理操作 SQL语句概述 SQL分类 1.DDL&#xff1a;数据定义语言 1.1创建数据库和表 创建数据库 创建数据表 1.2删除数据库和表 删除数据表…

2024年【化工自动化控制仪表】考试试卷及化工自动化控制仪表模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 化工自动化控制仪表考试试卷是安全生产模拟考试一点通总题库中生成的一套化工自动化控制仪表模拟考试题&#xff0c;安全生产模拟考试一点通上化工自动化控制仪表作业手机同步练习。2024年【化工自动化控制仪表】考试…