【cuda】三、矩阵相乘与coalescing writes(合并写操作)

Matrix Multiplication and Optimization

线程块

功能

  • 并行执行:线程块是一组同时执行的线程。它们共同执行分配给它们的任务
  • 资源共享:线程块内的线程可以共享数据和同步执行。通过共享内存(Shared Memory)和同步原语(如 __syncthreads())实现的。
  • 硬件映射:线程块的设计允许它们被有效地映射到GPU的物理硬件上。这种映射优化了执行效率,减少了线程切换和资源调度的开销。

结构

  • 线程组成:一个线程块由一组线程组成,线程数量可以从1到几千不等,具体取决于CUDA架构的限制(例如,大多数CUDA设备支持每个线程块最多1024个线程)。
  • 维度:线程块可以是一维、二维或三维的,这为不同类型的计算提供了灵活性。例如,二维的线程块适合于处理图像数据
  • 索引:线程块内的每个线程都有其唯一的索引,可以是一维、二维或三维的,这取决于线程块的维度。这些索引允许每个线程识别它在块内的位置,并据此处理数据。

索引和全局地址

那么有

线程索引:线程在其线程块内的二维索引 ****blockIdx的x和y。线程索引(threadIdx)表示一个线程在其所属线程块内的位置。在处理数组或矩阵时,线程索引可以用来计算要处理的元素的位置

块索引:线程块在网格中的二维索引 blockIdx.xblockIdx.y块索引(blockIdx)表示一个线程块在整个网格(Grid)中的位置。用于确定线程块在整个问题空间中的位置

线程块维度blockDim.xblockDim.y 表示线程块的维度。

这样就可以访问所有元素的位置地址,如果需要细节,请查看计算机组成原理课本。

例如,在二维数据处理中,一个线程的全局索引可以通过结合其线程索引和块索引来计算:

int xIndex = blockIdx.x * blockDim.x + threadIdx.x;
int yIndex = blockIdx.y * blockDim.y + threadIdx.y;

这里,blockDim.xblockDim.y 表示线程块在x和y维度上的大小。通过这种方式,我们可以确定每个最小单元(thread)的地址,进行读取操作。

简单的例子:矩阵相加

例如,这里给出一个2 * 2 的线程块(Thread Blocks)

单指令多数据(SIMD)模型

根据矩阵乘法的最基础定义公式,我们知道:结果中的每个元素的计算不依赖于结果中的其他元素。这就说明矩阵乘法任务可以进行并行。然而,我们总不能提前写好每个元素的计算公式,这样太复杂了。这就引入了SIMD模型,用于简化代码。

首先来看如下代码:

if (i < N && j < N) { // 如果这个索引在矩阵的边界内(即 i < N && j < N)int index = i + j * N; // 计算它的全局索引 i 和 jC[index] = A[index] + B[index];//独立地读取 A 和 B 中的元素,计算它们的和,然后将结果写入 C。
}

直观上来看,这就是一个串行编码中的顺序执行循环。但是,如果定义在并行的方法中,这样的串行代码就会被编译器自动转换成M*N条指令。也就是自动翻译成并行的模式。

此时**if** 语句并不是传统意义上的循环,而是一个并行执行的条件判断。

再深入一点

在更底层的层面,CUDA 运行时会将线程块分配给 GPU 上的流处理器(Streaming Multiprocessors, SMs)。SM内部包含多个CUDA核心,用于实际执行线程的计算。

线程块的调度:这个过程由CUDA运行时自动管理的,如果需要插手优化这环节,需要在核函数设计和块大小分配上间接干预。CUDA运行时会根据SM的数量和每个SM的资源情况(如寄存器、共享内存大小)来决定如何分配线程块。如果一个SM的资源不足以处理更多的线程块,新的线程块会被分配到其他SM。

每个 SM 可以同时执行多个线程,具体数量取决于 GPU 的架构和资源可用性。

  • 线程调度:SMs 通过分时复用的方式在物理核心上调度线程的执行。这意味着每个核心在不同时间点可以执行不同的线程。
  • 内存访问:当线程访问全局内存(如矩阵 A、B 和 C)时,存在潜在的延迟。为了最大化效率,CUDA 尝试合并对全局内存的访问,并利用局部性原理优化访问模式。
  • 指令执行:GPU 采用 SIMD 或 SIMT(单指令多线程)的方式执行指令。在 SIMD 模式下,一个指令同时作用于多个数据;在 SIMT 模式下,每个线程虽然执行相同的指令序列,但可以在不同的数据上独立操作。

复杂一点:矩阵乘法

矩阵相乘是一个非常典型的例子,用于展示CUDA编程和线程块(Block)及线程(Thread)的使用。

利用tread,做矩阵乘法。

例如,这里给出一个2 * 2 的线程块(Thread Blocks)

在这个核函数中,每个线程负责计算结果矩阵C中的一个元素。

__global__ void MatrixMultiply(float *A, float *B, float *C, int N) {int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;if (row < N && col < N) {float sum = 0.0f;for (int k = 0; k < N; k++) {//遍历所有需要加法的地方 N 次sum += A[row * N + k] * B[k * N + col]; // 得到一个元素上的结果}C[row * N + col] = sum;}
}

主函数中调用上述核函数的方式如下:

int N = 1024; // 假设矩阵大小为1024x1024
dim3 threadsPerBlock(16, 16);
dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);float *d_A, *d_B, *d_C;
// ... 在这里为 d_A, d_B 和 d_C 分配设备内存,并初始化数据 ...MatrixMultiply<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, N);// ... 处理完成后,从设备内存拷贝数据回主机内存,清理资源 ...

考虑性能

刚才的代码中,可以观察到两个for循环,这里可以进行优化。

“coalescing writes”(合并写操作)

“coalescing writes”(合并写操作)是一种优化内存访问模式的技术,它能显著提高内存带宽的利用效率。这种技术尤其对于全局内存访问非常重要,因为全局内存访问速度相比于核心计算速度要慢得多。

底层原理

  1. 内存事务:当GPU的线程尝试访问全局内存时,这些访问被分组为内存事务。每个事务可以一次性读取或写入多个连续的字节。使用适当大小的数据类型以匹配内存事务的大小。
  2. 内存对齐:为了有效地合并写操作,线程访问的内存地址应该是对齐的,并且连续线程访问的地址也应该是连续的。确保数据结构和数组在内存中对齐。
  3. 线程访问模式:如果一个线程块中的所有线程都按照一定的模式(例如,线程i访问地址i)访问连续的内存地址,则这些访问可以被合并成一个或几个内存事务。设计线程块和线程索引以便线程以线性和连续的顺序访问内存。减少线程内的条件分支,以保持连续的内存访问模式。

代码

__global__ void MatrixMultiplyCoalesced(float *A, float *B, float *C, int N) {// 计算行和列索引int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;if (row < N && col < N) {float sum = 0.0f;for (int k = 0; k < N; k++) {// 累加计算矩阵C中(row, col)位置的值sum += A[row * N + k] * B[k * N + col];}// 写入计算结果到矩阵C中,利用合并写操作优化// 每个线程按照顺序写入连续的内存地址C[row * N + col] = sum;}
}

优化点:

  • 合并写操作:在写入结果到矩阵C时,每个线程写入的是连续的内存位置(C[row * N + col])。这样,当多个线程同时写入时,由于它们访问的是连续的内存地址,这些写操作可以被合并成较少的内存事务。这种访问模式对于全局内存来说是高效的。
  • 线程索引的布局:通过合理的线程索引布局(即rowcol的计算方式),我们确保了线程以线性和有序的方式访问全局内存,这对于实现高效的合并写操作至关重要。

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

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

相关文章

[渗透测试学习] Hospital - HackTheBox

文章目录 信息搜集getshell提权信息搜集 nmap扫描一下端口 发现8080端口和443端口有http服务 然后发现3389端口是启用了ms-wbt-server服务 在对443端口的扫描没有收获,并且只有邮箱登录界面无法注册 接着看向8080端口,我们随便注册用户登录后发现有文件上传功能 getshell …

Python科学计算进阶:数值积分与微分求解算法应用在Python

在Python中进行科学计算时&#xff0c;数值积分和微分是非常常见的操作。下面我将介绍几种常用的数值积分和微分求解算法&#xff0c;并给出Python代码示例。 一、数值积分 矩形法 矩形法是一种简单的数值积分方法&#xff0c;它使用矩形近似代替被积函数。这种方法虽然简单&a…

利用淘宝/天猫API实现商品数据的实时获取、处理与分析

随着电子商务的飞速发展&#xff0c;对电商平台的数据需求越来越高。对于商家而言&#xff0c;实时获取商品数据是关键。淘宝和天猫作为中国最大的电商平台&#xff0c;提供了丰富的API接口&#xff0c;其中包括按关键字搜索商品API。本文将详细介绍如何使用淘宝/天猫提供的API…

【SpringMVC】—— 如何配置使用SpringMVC(详细步骤)

目录 引言 使用 1、新建模块 2、导入坐标 3、创建SpringMVC控制器类 4、初始化SpringMVC环境 5、初始化Servlet容器&#xff0c;加载SpringMVC环境 6、配置运行 引言 SpringMVC是一种基于Java实现MVC模型的轻量级Web框架&#xff0c;SpringMVC是表现层(web层)的框架,也…

.Net6使用SignalR实现前后端实时通信

代码部分 后端代码 &#xff08;Asp.net core web api&#xff0c;用的.net6&#xff09;Program.cs 代码运行逻辑&#xff1a; ​1. 通过 WebApplication.CreateBuilder(args) 创建一个 ASP.NET Core 应用程序建造器。 2. 使用 builder.Services.AddControllers() 添加 MVC 控…

两周掌握Vue3(五):自定义指令、路由、ajax

文章目录 一、自定义指令1.创建和使用自定义指令2.钩子函数3.使用参数 二、路由1.创建一个router实例2.在components目录中创建组件3.将路由实例挂载到应用4.使用路由 三、Ajax 代码仓库&#xff1a;跳转 当前分支&#xff1a;05 一、自定义指令 自定义指令是Vue.js框架提供的…

揭秘返利机器人的工作原理与实现思路

揭秘返利机器人的工作原理与实现思路 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;我将为你揭示返利机器人的工作原理与实现思路&#xff0c;让你…

基于Springboot的善筹网(众筹网-有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的善筹网(众筹网-有报告)。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring S…

人生当努力

"认定一个目标,便专心致志地向那里走,其余一切都置之度外,这是成功的秘诀,也是免除烦恼的秘诀"—朱光潜 最近有和亲友讨论是否要和别人比,是否要赢的问题.我觉得需要.软弱,实力弱的人才不去比较.如果不和别人比,你如何知自己差在哪儿?连差距都不知,又如何进步.不是…

Vue入门七(Vuex的使用|Vue-router|LocalStorage与SessionStorage和cookie的使用)

文章目录 一、Vuex1&#xff09;理解vuex2&#xff09;优点3&#xff09;何时使用&#xff1f;4&#xff09;使用步骤① 安装vuex② 注册vuex③ 引用vuex④ 创建仓库Store五个模块介绍 5&#xff09;基本使用 二、Vue-router三、LocalStorage与SessionStorage、cookie的使用 一…

ChatGPT Plus 经验分享:是否值得花钱升级?

ChatGPT Plus 经验分享&#xff1a;是否值得花钱升级&#xff1f; 五星上将麦克阿瑟曾经说过&#xff1a;“在有钱与没钱面前&#xff0c;我选择了or” ChatGPT 的每月订阅方案- ChatGPT Plus 已经推出一段时间了&#xff0c;目前的费用是$20 USD / 月(约TWD 610 / 月)。 Open…

Spirng MVC见解1

1. SpringMVC概述 1.1 MVC介绍 MVC是一种设计模式&#xff0c;将软件按照模型、视图、控制器来划分&#xff1a; M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为数据承载Bean&#x…

mybatis基本注解、增删改查、结果集映射、一对一、一对多

mybatis注解 基本注解新增删除修改查询 结果映射注解Results结果映射一对一映射一对多映射 基本注解 新增 功能&#xff1a;Insert完成新增操作&#xff0c;类似配置文件的 元素&#xff1b; 说明&#xff1a;新增时所用的参数取值是接口方法的入参&#xff0c;可以是对象&a…

Linux入门级常用命令学习笔记

以下命令是我跟着编程界的大佬鱼皮学习Linux时用的命令&#xff0c;我把它都记下来&#xff0c;权当作笔记&#xff0c;可供自己后期反复练习使用&#xff0c;让我们学习一下最基本的Linux命令吧。 一、Linux实战命令 在dos下 【ssh 服务器ip】可以连接服务器&#xff0c;输入…

运筹说 第80期 | 最小费用最大流问题

前面我们学习了图与网络分析的基础知识及经典问题&#xff0c;大家是否已经学会了呢&#xff1f;接下来小编和大家学习最后一个经典问题——最小费用最大流问题。 最小费用最大流问题是经济学和管理学中的一类典型问题。在一个网络中每段路径都有“容量”和“费用”两个限制的…

Gorm 0值不更新的问题

我有一张用户表&#xff0c;用户表里面的一个字段叫做points 积分&#xff0c;表示用户当前的剩余积分数据的。每当使用积分兑换物品&#xff0c;积分就会减少。 // 用户表 type User struct {BaseModelAccount string json:"account" form:"account"…

MOS管驱动电流计算以及分立器件驱动电路

自记&#xff1a; 1.先根据mos数据手册查找参数&#xff0c;计算电流&#xff1b; 2.分立器件驱动电路图&#xff1b; 3.分立器件选择 仔细学&#xff0c;能看懂&#xff01; 1.计算电流&#xff1a; 2.分立器件驱动电流&#xff1a;两种&#xff0c;第一种反向&#xff0c…

什么是TestNG以及如何创建testng.xml文件?

目录 什么是TestNG&#xff1f; 如何创建testng.xml文件 手动创建testng.xml 通过testng.xml运行整个包 通过testng.xml运行类 使用Eclipse创建testng.xml 本文将讨论TestNG以及如何通过执行testng.xml文件在TestNG中运行第一个测试用例。 什么是TestNG&#xff1f; Te…

new/delete vs malloc/free

new是关键字&#xff0c;最后还是调用malloc->brk malloc是函数&#xff0c;调用brk() new 和 malloc 都是用于在程序运行时动态分配内存的方法&#xff0c;但它们有一些重要的区别&#xff1a; 语法&#xff1a; new 是C关键字&#xff0c;用于在堆上分配内存并同时调用对…

线性表小结

线性表小结 单链表、循环链表和双向链表的时间效率比较 顺序表和链表的比较