计算机视觉 基于CUDA编程的入门与实践 线程及同步一

news2024/11/24 0:09:59

一、并行执行规模

        CUDA关于并行执行具有分层结构。每次内核启动时可以被切分成多个并行执行的块,而每个块又可以进一步地被切分成多个线程。这种并行执行的副本可以通过两种方式完成:一种是启动多个并行的块,每个块具有1个线程;另一种是启动1个块,每个块里具有多个线程。

        通过共享内存1个块中的线程可以相互通信。所以启动1个具有多个线程的块让里面的线程能够相互通信是一个优势。更加理想的则是,我们并不单独启动1个块,里面多个线程;也不启动多个块,每个里面1个线程。我们一次并行启动多个块,每个块里面多个线程(最多可以是maxThread-PerBlock的数量)。

        所以,假设上一章的那个向量加法例子你需要启动N=50000这么多的线程,我们可以这样调用内核:

gpuAdd << <((N+511)/512), 512 >> > (d_a, d_b, d_c);

        这个N最大可以是多少? 从计算能力3.0(目前CUDA能支持的最低计算能力)开始该x方向上的块数量就已经被放开了。因为考虑N过大而不能直接计算块数量的做法已经不需要考虑了。因为当前的限制是如此巨大,在2的31次方减1的块数量和每个块中1024的线程数量,只有非常巨大的N才能超出限制,大约在万亿级别的a,b,c中的元素数量才有可能,所以一般情况下这不会构成任何限制了。

二、大量线程并行示例

        这里的内核的代码值得注意的是:一处是计算初始的tid的时候,另一处则是while循环部分。计算初始的tid的变化,是因为我们现在是启动多个块,每个里面有多个线程,直接看成ID的结构,多个块横排排列,每个块里面有N个线程,那么自然计算tid的时候是用当前块的ID*当前块里面的线程数量+当前线程在块中的ID,即tid=blockIdx.x(当前块的ID)*blockDim.x(当前块里面的线程数量)+threadIdx.x(当前线程在块中的ID)。

        而while部分每次增加现有的线程数量(因为你没有启动到N),直到达到N。这就如同你有一个卡,一次最多只能启动100个块,每个块里有7个线程,也就是一次最多能启动700个线程。但N的规模是8000,远远超过700怎么办?答案是直接启动K个(K≥700),这样就能安全启动。然后里面添加一个while循环,这700个线程第一次处理[0,699),第二次处理[700,1400),第三次处理[1400,2100)……直到这8000个元素都被处理完。

        初始化时候的tid=threadIdx.x+blockDim.x*blockIdx.x,每次while循环的时候tid+=blockDim.x*gridDim.x(注意一个是=,一个是+=,后者是增加的由来)。

        这里的main函数,唯一的不同点在于内核的启动方式。现在我们用512个块,每个块里面有512个线程启动该内核。这样N非常大的问题就得到了解决。

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

//定义数组中的元素数
#define N	50000

//定义向量加法的核函数
__global__ void gpuAdd(int *d_a, int *d_b, int *d_c) {
	//获取当前内核的块索引
	int tid = threadIdx.x + blockIdx.x * blockDim.x;	
	while (tid < N)
	{
		d_c[tid] = d_a[tid] + d_b[tid];
		tid += blockDim.x * gridDim.x;
	}
		
}

int main(void) {
	//定义主机的数组
	int h_a[N], h_b[N], h_c[N];
	//定义设备指针
	int *d_a, *d_b, *d_c;
	// 申请内存
	cudaMalloc((void**)&d_a, N * sizeof(int));
	cudaMalloc((void**)&d_b, N * sizeof(int));
	cudaMalloc((void**)&d_c, N * sizeof(int));
	//初始化数组
	for (int i = 0; i < N; i++) {
		h_a[i] = 2 * i*i;
		h_b[i] = i;
	}
	// 将输入数组从主机复制到设备内存
	cudaMemcpy(d_a, h_a, N * sizeof(int), cudaMemcpyHostToDevice);
	cudaMemcpy(d_b, h_b, N * sizeof(int), cudaMemcpyHostToDevice);
	//用512个块,每个块里面有512个线程启动该内核
	gpuAdd << <512, 512 >> >(d_a, d_b, d_c);
	//将结果从设备内存复制回主机内存
	cudaMemcpy(h_c, d_c, N * sizeof(int), cudaMemcpyDeviceToHost);
	cudaDeviceSynchronize();
	int Correct = 1;
	printf("Vector addition on GPU \n");
	//Printing result on console
	for (int i = 0; i < N; i++) {
		if ((h_a[i] + h_b[i] != h_c[i]))
		{
			Correct = 0;
		}
		
	}
	if (Correct == 1)
	{
		printf("GPU has computed Sum Correctly\n");
	}
	else
	{
		printf("There is an Error in GPU Computation\n");
	}
	//Free up memory
	cudaFree(d_a);
	cudaFree(d_b);
	cudaFree(d_c);
	return 0;
}

三、存储器架构

        在GPU上的代码执行被划分为流多处理器、块和线程。GPU有几个不同的存储器空间,每个存储器空间都有特定的特征和用途以及不同的速度和范围。这个存储空间按层次结构划分为不同的组块,比如全局内存、共享内存、本地内存、常量内存和纹理内存,每个组块都可以从程序中的不同点访问。

        如图所示,每个线程都有自己的本地存储器和寄存器堆。与处理器不同的是,GPU核心有很多寄存器来存储本地数据。当线程使用的数据不适合存储在寄存器堆中或者寄存器堆中装不下的时候,将会使用本地内存。寄存器堆和本地内存对每个线程都是唯一的。寄存器堆是最快的一种存储器。同一个块中的线程具有可由该块中的所有线程访问的共享内存。全局内存可被所有的块和其中的所有线程访问。它具有相当大的访问延迟,但存在缓存这种东西来给它提速。 

        GPU有一级和二级缓存(即L1缓存和L2缓存)。常量内存则是用于存储常量和内核参数之类的只读数据。最后,存在纹理内存,这种内存可以利用各种2D和3D的访问模式。

        所有存储器特征总结如下 

        上表表述了各种存储器的各种特性。作用范围栏定义了程序的哪个部分能使用该存储器。而生存期定义了该存储器中的数据对程序可见的时间。除此之外,L1和L2缓存也可以用于GPU程序以便更快地访问存储器。

        总之,所有线程都有一个寄存器堆,它是最快的。共享内存只能被块中的线程访问,但比全局内存块。全局内存是最慢的,但可以被所有的块访问。常量和纹理内存用于特殊用途。 

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

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

相关文章

项目实战之旅游网(五)后台角色管理(下) 后台权限管理

目录 一.后台角色管理&#xff08;下&#xff09; 1.查询角色权限 2.修改角色权限 3.优化侧边栏菜单 二.后台权限管理 1.权限列表 2.新增权限 3.修改权限 4.删除权限 一.后台角色管理&#xff08;下&#xff09; 1.查询角色权限 先新建一个bean类型的实体类&#xf…

单商户商城系统功能拆解53—数据分析

单商户商城系统&#xff0c;也称为B2C自营电商模式单店商城系统。可以快速帮助个人、机构和企业搭建自己的私域交易线上商城。 单商户商城系统完美契合私域流量变现闭环交易使用。通常拥有丰富的营销玩法&#xff0c;例如拼团&#xff0c;秒杀&#xff0c;砍价&#xff0c;包邮…

一文清晰带你弄清楚Spring IOC 循环依赖问题是如何解决的

什么是循环依赖 循环依赖又被成为循环引用,即两个或者多个bean相互之间的持有对方,比如A 引用B,B引用C,C 又引用A,则它们最终反映为一个环,如下图所示: 循环依赖是对象之间的相互依赖关系,循环依赖就是一个死循环,除非有终结条件,否则就是死循环,最终导致内存溢出错误. 解决…

【Java 数据结构】优先级队列

篮球哥温馨提示&#xff1a;编程的同时不要忘记锻炼哦&#xff01;谁是你的优先级呢&#xff1f; 目录 1、优先级队列 1.1 优先级队列概念 1.2 堆的概念 1.3 堆的存储结构 2、模拟实现优先级队列 2.1 成员变量的设定 2.2 根据数组构造出一个堆 2.3 向下调整 2.4 creat…

电压放大器如何测试线性稳压器

有不少的电子工程师咨询电压放大器如何测试线性稳压器&#xff0c;那么这种要怎么做呢&#xff0c;下面让安泰电子来为大家介绍。 一、什么是低压差线性稳压器&#xff1f; 低压差线性稳压器是集成电路稳压器&#xff0c;经常用来电流主通道控制&#xff0c;芯片上集成导通电阻…

SQL 事务基础

事务基础 1 事务概念 所谓事务就是用户定义的一个数据库操作序列&#xff0c;这些操作要么全做&#xff0c;要不全不做&#xff0c;是一个不可分割的工作单位。 2 事务的特性&#xff08;ACID&#xff09; 原子性&#xff08;atomicity&#xff09; 事务是数据库工作的逻辑…

数据,信息,知识,智慧

数据&#xff0c;信息&#xff0c;知识&#xff0c;智慧 知识管理的对象有数据、信息、知识、智慧&#xff0c;而不仅仅是知识。将这些联系起来处理&#xff0c;就能期待综合效果。 作为知识资产的知识 传统的资源以人、物、钱为代表。但是&#xff0c;在经济活动的现场&…

2023年全国管理类联考英语二真题及解析

Section Ⅰ Use of English Here’s a common scenario that any number of entrepreneurs face today: you’re the CEO of a small business and though youre making a nice 1 , you need to find a way to take it to the next level. what you need to do is 2 growth by …

MobPush:社交app硝烟再起,如何突出重围?

推送&#xff0c;能够在产品和用户之间建立有效的连接。好的推送能够传达有价值的信息和提供好用的功能&#xff0c;让企业和用户沟通&#xff0c;把准确的信息第一时间传达。然而很多企业都没有意识到这一点&#xff0c;对于推送的频率&#xff0c;内容&#xff0c;以及针对各…

辗转相除法求最大公因数-C语言

辗转相除法&#xff0c;又名欧几里德算法&#xff0c;是求最大公约数的一种方法。以除数和余数反复做除法运算&#xff0c;最终当余数为0时&#xff0c;取当前算式除数为最大公约数。 例1&#xff1a;求2015和15的最大公因数。 2015 15 * 134 5 15 5 * 3 0 因此&#xff0…

亚马逊云科技 Build On - 咖啡厅Demo学习stepfunction serverless应用

荣幸参与和csdn和aws联合举办的buildon实验活动&#xff0c;主要目的还是学习stepfucntion的使用&#xff0c;这个服务能够集成大量aws service感觉可以出现很多有趣的用法。官方给出的文档已经非常详细了&#xff0c;这里只是对一些比较难理解的点进行了记录和解释&#xff0c…

restricted isometry property 稀疏 (CSDN_0002_20220908)

目录 1. 稀疏问题的引出 2. RIP 说明&#xff1a; 1. 由于参考多篇文献&#xff0c;所以本文的符号与原文略有不同。 2. 由于原文公式较多&#xff0c;所以本文采用了截图的形式&#xff0c;如需要电子版文档&#xff0c;可私信或留言。 1. 稀疏问题的引出 2. RIP 关于1-…

MySQL存储过程高级SQL语句总结

MySQL高级SQL语句&#xff08;存储过程&#xff09; 一、存储过程的概述 1.1 什么是存储过程 存储过程是一组为了完成特定功能的SQL语句集合。 存储过程在使用过程中是将常用或者复杂的工作预先使用SQL语句写好并用一个指定的名称存储起来&#xff0c;这个过程经编译和优化后…

Quarkus构建一个原生可执行文件

先决条件 大概15分钟 编辑器 安装GraalVM&#xff0c;并正确配置 Apache Maven 3.8.1 可以工作的容器 (Docker或者Podman) 一个 C语言工作开发环境 Quarkus应用程序代码 支持在C语言中进行原生编译 拥有一个C语言工作开发者环境意味着什么&#xff1f; 在Linux上&#xf…

华为手机恢复出厂设置后如何恢复数据

当您恢复出厂设置时&#xff0c;手机上存储的所有数据都会被清空。这是因为恢复出厂设置基本上是您从头开始设置手机的一种方式。 众所周知&#xff0c;重置手机会清除手机上的现有数据。如果这种强制删除让你丢失了重要数据&#xff0c;那么恢复出厂设置后数据还能恢复吗&…

企企通:如何利用数字化之道,赋能汽车行业供应链创新?

汽车是国民经济的支柱性企业&#xff0c;产业链长&#xff0c;涉及面广、带动性强&#xff0c;国际化程度高&#xff0c;在全球主要经济大国的产业体系中一直占据着重要地位。 我国汽车行业通过几十年的高速发展之后&#xff0c;从量变到质变&#xff0c;逐渐向低速增长的模式开…

把TeamTalk(即时通讯项目)中的线程池连接池拆出来单独测试。

研究过Teamtalk的伙伴会发现它的线程池和连接池与很多文件有关联&#xff0c; 这篇文章主要写&#xff0c;把它的线程池连接池拆出来需要用到哪些文件。 其实我本来只想测试它的连接池的&#xff0c;但发现连接池里套的有线程池&#xff0c;于是就一起拆出来了。 整个工程的树…

基于SpringBoot的社区小型图书管理系统的设计与实现

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

有效操作:Ubuntu上已经安装最新node但是node -v返回的版本号确实错的;ubuntu第一次启动vue项目报npm版本错误

** 如已经安装过最新版的node话可直接跳到操作6&#xff1a; 1.查看node版本&#xff0c;没安装的请先安装&#xff1b; node -v 如果安装成功的话会返回版本号&#xff1a; 2.如果nodejs包出错需要重新安装的话&#xff0c;删除不干净会有可能出现问题&#xff0c;下面就介…

应用出海活跃,开发教程

移动应用行业在国内显现出用户增量放缓的趋势&#xff0c;多种类型的应用渗透率也渐趋饱和。随着政策支持力度的加大&#xff0c;越来越多移动应用走向了海外市场&#xff0c;拓宽用户群。 根据艾瑞咨询在《2022年移动应用出海趋势洞察白皮书》中指出&#xff0c;游戏类占出海…