CUDA - 如何让线程和内存对应

前提:

本文的目的就是设置的程序中,每个线程可以负责一个单独的计算任务。帮助学习和理解线程是如何组织的。

本文处理一个二维数据的加法。

数据在内存中的存储

以线性、行为主的方式存储。

例如,一个16*8的一维数组,在内存中是一段连续的128个地址存储该数据

如下图,每个小格子表示一行数据

想要GPU充分发挥他的优点就是每个线程处理不同的数据,避免同一个线程处理多个数据,或者避免线程没有组织的胡乱访问内存。

组织线程模型

二维网格二维线程块 2D grid \ 2D block

如图,不同颜色的方块表示的是一个线程块。因为数组大小是16*8 =128,先定义每个块的维度是(4,4),所以可以计算得到网格的维度是(4,2)。

定义: 

gridsize(4,2)

blocksize(4,4)

目的是为了让线程和数组内存中的分布一一对应。

线程和二维矩阵映射关系

ix = threadIdx.x + blockIdx.x * blockDim.x;
iy = threadIdx.y + blockIdx.y * blockDim.y;

如下图

线程和二维矩阵映射关系

idx = iy * gridDim.x * blockDim.x + ix;

 编写代码如下:实现二维网格二维线程块进行二维数组的加法

#include "cuda_runtime_api.h"
#include <device_launch_parameters.h>
#include <iostream>

static void CheckCudaErrorAux(const char*, unsigned, const char*, cudaError_t);
#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__,__LINE__, #value, value)

__global__ void addMatrix(int* A, int* B, int* C, const int nx, const int ny)
{
	int ix = threadIdx.x + blockIdx.x * blockDim.x;
	int iy = threadIdx.y + blockIdx.y * blockDim.y;
	int idx = iy * gridDim.x * blockDim.x + ix;

	if (ix < nx && iy < ny)
	{
		C[idx] = A[idx] + B[idx];
	}
}

int main()
{
	const int nx = 16;
	const int ny = 8;
	const int nxy = nx * ny;
	size_t stBytesCount = nxy * sizeof(int);
	int* ipHost_A = new int[nxy];
	int* ipHost_B = new int[nxy];
	int* ipHost_C = new int[nxy];

	for (size_t i = 0; i < nxy; i++)
	{
		ipHost_A[i] = i;
		ipHost_B[i] = i + 1;
	}
	memset(ipHost_C, 0, stBytesCount);
	
	int* ipDevice_A, * ipDevice_B, * ipDevice_C;
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_A, stBytesCount));
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_B, stBytesCount));
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_C, stBytesCount));

	CUDA_CHECK_RETURN(cudaMemcpy(ipDevice_A, ipHost_A, stBytesCount, cudaMemcpyHostToDevice));
	CUDA_CHECK_RETURN(cudaMemcpy(ipDevice_B, ipHost_B, stBytesCount, cudaMemcpyHostToDevice));

	dim3 block(4,4);
	dim3 grid((nx + block.x - 1) / block.x, (ny + block.y - 1) / block.y);

	addMatrix <<<grid, block >>> (ipDevice_A, ipDevice_B, ipDevice_C, nx, ny);

	CUDA_CHECK_RETURN(cudaMemcpy(ipHost_C, ipDevice_C, stBytesCount, cudaMemcpyDeviceToHost));

	for (size_t i = 0; i < nxy; i++)
	{
		if (i % 4 == 0 && i)
			std::cout << std::endl;

		std::cout << ipHost_A[i] << " + " << ipHost_B[i] << " = " << ipHost_C[i] << "\t";

	}

	cudaFree(ipDevice_A);
	cudaFree(ipDevice_B);
	cudaFree(ipDevice_C);

	delete []ipHost_A;
	delete []ipHost_B;
	delete []ipHost_C;

	ipHost_A =nullptr;
	ipHost_B =nullptr;
	ipHost_C =nullptr;


	return 0;
}

static void CheckCudaErrorAux(const char* file, unsigned line, const char* statement, cudaError_t err)
{
	if (err == cudaSuccess)
		return;
	std::cerr << statement << " returned: " << cudaGetErrorName(err) << "  \t : " << cudaGetErrorString(err) << "(" << err << ") at " << file << ":" << line << std::endl;
	exit(1);
}

二维网格一维线程块 2D grid \ 1D block

如图,不同颜色的方块表示的是一个线程块。因为数组大小是16*8 =128,先定义每个块的维度是(4,1),所以可以计算得到网格的维度是(4,8)。

线程和二维矩阵映射关系

这里定义的网格是一维的,所以blockDim.y = 1, threadIdx.y 始终是0

ix = threadIdx.x + blockIdx.x * blockDim.x;
iy = threadIdx.y + blockIdx.y * blockDim.y;

如下图

线程和二维矩阵映射关系

idx = iy * gridDim.x * blockDim.x + ix;

 编写代码如下:实现二维网格一维线程块进行二维数组的加法

#include "cuda_runtime_api.h"
#include <device_launch_parameters.h>
#include <iostream>

static void CheckCudaErrorAux(const char*, unsigned, const char*, cudaError_t);
#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__,__LINE__, #value, value)

__global__ void kernel_addMatrix(int* A, int* B, int* C, const int nx, const int ny)
{
	int ix = threadIdx.x + blockIdx.x * blockDim.x;
	int iy = threadIdx.y + blockIdx.y * blockDim.y;  // 因为block是一维的,所以threadIdx.y始终是0
	int idx = iy * gridDim.x * blockDim.x + ix;

	if (ix < nx && iy < ny)
	{
		C[idx] = A[idx] + B[idx];
	}
}

int main()
{
	const int nx = 16;
	const int ny = 8;
	const int nxy = nx * ny;
	size_t stBytesCount = nxy * sizeof(int);
	int* ipHost_A = new int[nxy];
	int* ipHost_B = new int[nxy];
	int* ipHost_C = new int[nxy];

	for (size_t i = 0; i < nxy; i++)
	{
		ipHost_A[i] = i;
		ipHost_B[i] = i + 1;
	}
	memset(ipHost_C, 0, stBytesCount);

	int* ipDevice_A, * ipDevice_B, * ipDevice_C;
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_A, stBytesCount));
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_B, stBytesCount));
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_C, stBytesCount));

	CUDA_CHECK_RETURN(cudaMemcpy(ipDevice_A, ipHost_A, stBytesCount, cudaMemcpyHostToDevice));
	CUDA_CHECK_RETURN(cudaMemcpy(ipDevice_B, ipHost_B, stBytesCount, cudaMemcpyHostToDevice));

	dim3 block(4, 1);
	dim3 grid((nx + block.x - 1) / block.x, ny);

	kernel_addMatrix <<<grid, block >>> (ipDevice_A, ipDevice_B, ipDevice_C, nx, ny);	

	cudaThreadSynchronize();

	CUDA_CHECK_RETURN(cudaMemcpy(ipHost_C, ipDevice_C, stBytesCount, cudaMemcpyDeviceToHost));

	for (size_t i = 0; i < nxy; i++)
	{
		if (i % 4 == 0 && i)
			std::cout << std::endl;

		std::cout << ipHost_A[i] << " + " << ipHost_B[i] << " = " << ipHost_C[i] << "\t";

	}

	cudaFree(ipDevice_A);
	cudaFree(ipDevice_B);
	cudaFree(ipDevice_C);

	delete[]ipHost_A;
	delete[]ipHost_B;
	delete[]ipHost_C;

	ipHost_A = nullptr;
	ipHost_B = nullptr;
	ipHost_C = nullptr;


	return 0;
}

static void CheckCudaErrorAux(const char* file, unsigned line, const char* statement, cudaError_t err)
{
	if (err == cudaSuccess)
		return;
	std::cerr << statement << " returned: " << cudaGetErrorName(err) << "  \t : " << cudaGetErrorString(err) << "(" << err << ") at " << file << ":" << line << std::endl;
	exit(1);
}

示例结果

一维网格一维线程块 1D grid \ 1D block

之前的GPU线程数和数组的大小是相等的,如果说不相等的情况下,GPU每个线程处理的就不是一个运算,而是多个数据的运算。也就是说在核函数中需要使用循环进行处理。

针对本文示例,网格块定义为(4,1),线程块定义为(4,1)。也就是说每个线程处理的分布如下图:

这个例子中,每个线程需要处理的是一列的数据。

线程和二维矩阵映射关系

ix = threadIdx.x + blockIdx.x * blockDim.x;
iy = threadIdx.y + blockIdx.y * blockDim.y;

这里因为grid\block都是一维的,所以threadIdx.y、blockIdx.y都始终是0.

如下

编码如下:

#include "cuda_runtime_api.h"
#include <device_launch_parameters.h>
#include <iostream>

static void CheckCudaErrorAux(const char*, unsigned, const char*, cudaError_t);
#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__,__LINE__, #value, value)

__global__ void _addMatrix(int* A, int* B, int* C, const int nx, const int ny)
{
	int ix = threadIdx.x + blockIdx.x * blockDim.x;

	int iy = 0; 
	int offset = gridDim.x * blockDim.x;

	if (ix < nx)
	{
		for (size_t i = 0; i < ny; i++)
		{
			int idx = i * offset + ix;
			if (idx < nx*ny)
			{
				C[idx] = A[idx] + B[idx];
			}
		}
	}
}

int main()
{
	const int nx = 16;
	const int ny = 8;
	const int nxy = nx * ny;
	size_t stBytesCount = nxy * sizeof(int);
	int* ipHost_A = new int[nxy];
	int* ipHost_B = new int[nxy];
	int* ipHost_C = new int[nxy];

	for (size_t i = 0; i < nxy; i++)
	{
		ipHost_A[i] = i;
		ipHost_B[i] = i + 1;
	}
	memset(ipHost_C, 0, stBytesCount);

	int* ipDevice_A, * ipDevice_B, * ipDevice_C;
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_A, stBytesCount));
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_B, stBytesCount));
	CUDA_CHECK_RETURN(cudaMalloc((void**)&ipDevice_C, stBytesCount));

	CUDA_CHECK_RETURN(cudaMemcpy(ipDevice_A, ipHost_A, stBytesCount, cudaMemcpyHostToDevice));
	CUDA_CHECK_RETURN(cudaMemcpy(ipDevice_B, ipHost_B, stBytesCount, cudaMemcpyHostToDevice));

	dim3 block(4, 1);
	dim3 grid(4, 1);

	_addMatrix << <grid, block >> > (ipDevice_A, ipDevice_B, ipDevice_C, nx, ny);

	cudaThreadSynchronize();

	CUDA_CHECK_RETURN(cudaMemcpy(ipHost_C, ipDevice_C, stBytesCount, cudaMemcpyDeviceToHost));

	for (size_t i = 0; i < nxy; i++)
	{
		if (i % 4 == 0 && i)
			std::cout << std::endl;

		std::cout << ipHost_A[i] << " + " << ipHost_B[i] << " = " << ipHost_C[i] << "\t";

	}

	cudaFree(ipDevice_A);
	cudaFree(ipDevice_B);
	cudaFree(ipDevice_C);

	delete[]ipHost_A;
	delete[]ipHost_B;
	delete[]ipHost_C;

	ipHost_A = nullptr;
	ipHost_B = nullptr;
	ipHost_C = nullptr;


	return 0;
}

static void CheckCudaErrorAux(const char* file, unsigned line, const char* statement, cudaError_t err)
{
	if (err == cudaSuccess)
		return;
	std::cerr << statement << " returned: " << cudaGetErrorName(err) << "  \t : " << cudaGetErrorString(err) << "(" << err << ") at " << file << ":" << line << std::endl;
	exit(1);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

站在用户视角审视:以太彩光与PON之争

作者:科技作家-郑凯 园区,是企业数字化转型的“中心战场”。 云计算、大数据、人工智能等数智化技术在园区里“战火交织”;高清视频、协同办公,智慧安防等大量创新应用产生的海量数据在园区内“纵横驰骋”;加上大量的IOT和智能化设备涌入“战场”,让园区网络面对着难以抵御的…

查看PyTorch的GPU使用情况的工具

文章目录 torch.cuda APIPyTorch SnapshotPyTorch ProfilerNVIDIA Nsight Systemstorchinfo torch.cuda API torch.cuda.memory_stats&#xff1a;返回给定设备的 CUDA 内存分配器统计信息字典。该函数的返回值是一个统计字典&#xff0c;每个字典都是一个非负整数。torch.cud…

antDesign Form.List下的Form.Item如何通过setFieldsValue设置值

翻了一下antDesign官网只看见了Form可以使用setFieldsValue设置值&#xff0c;却没找到Form.List使用setFieldsValue设置值。 于是研究了一下&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 我的解决方案是&#xff1a; 先设置为空数组, 再设置成…

利用编程思维做题之反转链表

牛客网题目 1. 理解问题 给到我们的是一个单链表的头节点 pHead&#xff0c;要求反转后&#xff0c;返回新链表的头节点。 首先在心里设想能够快速理解的例子&#xff0c;如给你123序列&#xff0c;要你反转此序列如何回答&#xff1f;将最后一个数字3作为头&#xff0c;然后修…

学习threejs,THREE.MeshBasicMaterial网格材质、THREE.MeshLambertMaterial漫反射材质

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.MeshBasicMaterial网…

MATLAB代码解析:利用DCGAN实现图像数据的生成

摘要 经典代码&#xff1a;利用DCGAN生成花朵 MATLAB官方其实给出了DCGAN生成花朵的示范代码&#xff0c;原文地址&#xff1a;训练生成对抗网络 (GAN) - MATLAB & Simulink - MathWorks 中国 先看看训练效果 训练1周期 训练11周期 训练56个周期 脚本文件 为了能让各位…

centos7 Oracle 11g rac 静默安装(NFS配置共享存储)

1.环境信息准备 注意&#xff1a; 在配置网络时&#xff0c;Oracle RAC的每个节点必须具有至少两个以上的网卡&#xff0c;一张网卡对外提供网络服务&#xff0c;另一张网卡用于各个节点间的通信和心跳检测等。在配置RAC集群的网卡时&#xff0c;如果节点1的公共接口是eth0&…

下一代安全:融合网络和物理策略以实现最佳保护

在当今快速发展的技术环境中&#xff0c;网络和物理安全融合变得比以往任何时候都更加重要。随着物联网 (IoT) 和工业物联网 (IIoT) 的兴起&#xff0c;组织在保护数字和物理资产方面面临着独特的挑战。 本文探讨了安全融合的概念、说明其重要性的实际事件以及整合网络和物理安…

本地装了个pytorch cuda

安装命令选择 pip install torch1.13.1cu116 torchvision0.14.1cu116 torchaudio0.13.1 --extra-index-url https://download.pytorch.org/whl/cu116 torch版本查看 python import torch print(torch.__version__) 查看pytorch能否使用cuda import torch# 检查CUDA是否可用…

鸿蒙NEXT开发-动画(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

241014-绿联UGOSPro-通过虚拟机访问主机的用户目录及文件夹

如图所示&#xff0c;两种方式&#xff1b; 方式1: 通过Files中的Other Locations 添加主机ip&#xff0c;随后输入主机的用户名及密码即可系统及文件加载可能需要一段时间&#xff0c;有点卡&#xff0c;加载完应该就可以点击访问了 方式2: 通过命令行直接ssh/sftp userna…

【C++网络编程】(一)Linux平台下TCP客户/服务端程序

文章目录 Linux平台下TCP客户/服务端程序服务端客户端相关头文件介绍 Linux平台下TCP客户/服务端程序 图片来源&#xff1a;https://subingwen.cn/linux/socket/ 下面实现一个Linux平台下TCP客户/服务端程序&#xff1a;客户端向服务器发送&#xff1a;“你好&#xff0c;服务…

网络资源模板--Android Studio 实现简易新闻App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--基于Android studio 实现的简易新闻App 二、项目测试环境 三、项目详情 登录页 用户输入&#xff1a; 提供账号和密码输入框&#xff0c;用户可以输入登录信息。支持“记…

[ComfyUI]Flux:国漫经典!斗破苍穹古熏儿之绮梦流光模型来袭

在数字艺术和创意领域&#xff0c;FLUX以其独特的虚实结合技术&#xff0c;已经成为艺术家和设计师们手中的利器。今天&#xff0c;我们激动地宣布&#xff0c;FLUX推出了一款全新的ComfyUI版本——Flux&#xff0c;它将国漫经典《斗破苍穹》中的古熏儿之绮梦流光模型完美融合&…

第十四章 RabbitMQ延迟消息之延迟队列

目录 一、引言 二、死信队列 三、核心代码实现 四、运行效果 五、总结 一、引言 什么是延迟消息&#xff1f; 发送者发送消息时指定一个时间&#xff0c;消费者不会立刻收到消息&#xff0c;而是在指定时间后收到消息。 什么是延迟任务&#xff1f; 设置在一定时间之后才…

Qt入门教程:创建我的第一个小程序

本章教程&#xff0c;主要介绍如何编写一个简单的QT小程序。主要是介绍创建项目的过程。 一、打开QT软件编辑器 这里使用的是QT5.14.2版本的&#xff0c;安装教程参考以往教程&#xff1a;https://blog.csdn.net/qq_19309473/article/details/142907096 二、创建项目 到这里&am…

使用Docker部署nextjs应用

最近使用nextjs网站开发&#xff0c;希望使用docker进行生产环境的部署&#xff0c;减少环境的依赖可重复部署操作。我采用的是Dockerfile编写应用镜像方式 docker-compose实现容器部署的功能。 Docker Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器…

【大模型问答测试】大模型问答测试脚本实现(第一版)

背景 公司已经做了一段时间的大模型&#xff0c;每次测试或者回归的时候都需要针对问答进行测试回归&#xff0c;耗费大量的时间与精力&#xff0c;因此结合产品特点&#xff0c;开发自动化脚本替代人工的操作&#xff0c;提升测试回归效率 设计 使用pythonrequestExcel进行…

Android笔记(二十四)基于Compose组件的MVVM模式和MVI模式的实现

仔细研究了一下MVI(Model-View-Intent)模式&#xff0c;发现它和MVVM模式非常的相识。在采用Android JetPack Compose组件下&#xff0c;MVI模式的实现和MVVM模式的实现非常的类似&#xff0c;都需要借助ViewModel实现业务逻辑和视图数据和状态的传递。在这篇文章中&#xff0c…

易我数据恢复软件,一键找回你的重要资料!

我们生活在数字时代&#xff0c;数据对我们来说超级重要。工作文件、学习资料&#xff0c;还有照片视频&#xff0c;这些东西要是没了或者不小心删了&#xff0c;那得多烦人啊。幸好现在科技发达&#xff0c;有了数据恢复软件&#xff0c;就像给我们数据上了一把安全锁。市面上…