cuda矩阵乘法(简单理解)

2023-11-18

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


矩阵规模

矩阵A: n行,l列
矩阵B:l行,m列
结果矩阵C=A✖️B: n行,m列


一、一维并行

1.一维线程并行(thread)

一个线程并行一个A1行✖️B1列

  1. 需要线程个数:n✖️m个,一个线程并行一个A1行✖️B1列, 即一个线程对应结果矩阵的C的一个点,e.g. c(1,1)=xid0的计算结果,所以一共需要:
xid0=a1hang✖️b1lie xid1=a1hang✖️b2lie xid(m-1)=a1hang✖️bmlie
xid(m)=a2hang✖️b1lie xid(m+1)=a2hang✖️b2lie xid(2m-1)=a2hang✖️bmlie
.
xid((n-1)*m)=anhang✖️b1lie xid((n-1)*m+1)=anhang✖️b2lie xid((n-1)*(m-1))=anhang✖️bmlie

即可知一共需要n✖️m个线程并行,每个线程处理一个向量✖️向量
2. 总共的第几个线程:xid=block.x*blockDim.x+threadIdx.x;
3. 因为线程总数为n✖️m个,所以当前线程xid对应A的第几行=xid/n,
当前线程xid对应B的第几列=xid%n;

 const int idx = blockIdx.x*blockDim.x+threadIdx.x;
    const int Arow = idx / n;
    const int Bcolumn = idx % n;

    //计算矩阵乘法
    if (Arow < n && Bcolumn < m)
    {
        float t = 0;

        for (i = 0; i < l; i++)
        {
            t += A[Arow * l + i] * B[i * m + Bcolumn];
        }
        C[Arow * m + Bcolumn] = t;
    }

2.一维块线程并行(block)

一个block代表矩阵A1行,块中一个线程代表B1列,B矩阵的列已每块线程数跳步.

  1. 一共需要n个block,如果可以给的最大块数小于A的行数则已gridDim.x为block的跳步.
    线程数为j个,如果j小于B的列数则以blockDim.x为跳步
thread0 thread1 thread2 thread3 thread0+blockDim.x hread1+blockDim.x thread3+m/blockDim.x
block0 a1hang✖️b1lie a1hang✖️b2lie a1hang✖️b3lie a1hang✖️b4lie a1hang✖️b5lie a1hang✖️b6lie a1hang✖️bmlie
block1 a2hang✖️b1lie a2hang✖️b2lie a2hang✖️b3lie a2hang✖️b4lie a2hang✖️b5lie a2hang✖️b6lie a2hang✖️bmlie
.
block(n-1) anhang✖️b1lie anhang✖️b2lie anhang✖️b3lie anhang✖️b4lie anhang✖️b5lie anhang✖️b6lie anhang✖️bmlie

    
    //计算矩阵乘法
    for (int Arow=blockIdx.x;Arow<n;Arow+=gridDim.x)
    {
    for (int Bcolumn=threadIdx.x; Bcolumn < m;Bcolumn+=blockDim.x)
    {
        float num = 0.0;

        for (int i = 0; i < l; i++)
        {
            num += A[Arow * l + i] * B[i * m + Bcolumn];
        }
        C[Arow * m + Bcolumn] = num;
    }
    }

3.一维共享A一行程并行(shared)

上个方法中A的每行一个block,把A的每行给共享存储.

  1. 共享存储速的关键是数据重复利用,因为从全局读一次数据,然后重复多用几次最合适,e.g.矩阵乘法中,A的每一行就成了B的m个列,即A1的每行都用重复用了m次.可将A的每行放进共享存储.
__shared__ shA[l];
int Arow=blockIdx.x;
for(int Acolumn=threadIdx.x; Acolumn < l;Bcolumn+=blockDim.x)
{
    shA[Acolumn]=A[Arow*l+Acolumn];//把A的每行给shared
}
__syncthreads();

for(int Bcolumn=threadIdx.x; Bcolumn<m; Bcolumn+=blockDim.x)//A的每行乘B的每列跳步为没块线程数
{
    float num = 0.0;
    for (int i = 0; i < l; i++)//A1行(已在shared中)乘B1列
    {
        num += shA[i] * B[i * m + Bcolumn];
    }
     C[Arow * m + Bcolumn] = num;
}    
   

二、二维并行

1.共享存储二维分块

  1. 问题与改进
    当A的列数>shared memory最大容量时->采用分块存在shared中;
    一维时只存A矩阵->二维将B矩阵也分块存入;
  2. 解决办法:设置两个二维的share memory : shA[][Tl_size]放A的shared子矩阵,shB[Tl_size][]放B的shared子矩阵,A横着更新shA子矩阵,B竖着更新shB子矩阵

在这里插入图片描述

template <int BLOCK_SIZE> __global__ void
matrixMulCUDA(float *C, float *A, float *B, int m, int n, int l)
{
 // 累加,得到行 * 列的值
 float numsub = 0.0;

 // 循环次数等于widthA / 16,把长向量点积运算转化为两个短向量点积后的和
 //因为B的宽条是竖着的,每宽条中宽是往下移动的,往下移动即代表动一行要增长m个元素,动一块即移动块宽Bsize✖️m个元素,所以步长为每次BLOCK_SIZE * m; 因为A每宽条是横着的,所以宽条中的块是横着移动,当前行每往右移动一块,增长块的块度Bsize的元素,即每宽条每移动一块步长就为Bsize
 for (int i = 0; i <l; i += BLOCK_SIZE)
 {
 // 定义A的共享子矩阵变量,因为__shared__声明,所以同一个block中的所有threads都可见,
 //每个thread填充一个元素,并计算一个行列乘积,减小带宽使用
 __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];

 // 定义A的共享子矩阵变量
 __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];

 // 每个block包含16 * 16 个线程,所以每个线程负责一个矩阵元素的拷贝(注意同步)
 //A的shared块更新方式:行:1.块横着一块一块更新一宽条:l * BLOCK_SIZE * blockIdx.y为前面有的宽条:blockIdx.y表示第几个宽条,宽条的宽度为Bsize,则BLOCK_SIZE * blockIdx.y为前面一共有几行,每行有l列(BLOCK_SIZE * blockIdx.y)*l则为一共前面有多少元素. 2.hreadIdx.y 表示当前宽条的第几行,l * hreadIdx.y表示本块中本行在本块前的前面行一共的元素.所以一共第  (BLOCK_SIZE * blockIdx.y)+hreadIdx.y 行. 列:3.threadIdx.x表示本块第几个元素,本行一共第几个元素为threadIdx.x+i,所以l * BLOCK_SIZE * blockIdx.y + l * hreadIdx.y + threadIdx.x表示前面宽行中元素个数➕本宽条中前几行元素个数➕本行第几个元素
 //第几行:BLOCK_SIZE * blockIdx.y + threadIdx.y。 第几列:threadIdx.x+i
 As[ty][tx] = A[l * (BLOCK_SIZE * blockIdx.y + threadIdx.y) + (threadIdx.x+i)];
 //第几行:threadIdx.y + i.      第几列:BLOCK_SIZE * blockIdx.x + threadIdx.x
 Bs[ty][tx] = B[ m * (threadIdx.y + i) + (BLOCK_SIZE * blockIdx.x + threadIdx.x)];

 // Synchronize to make sure the matrices are loaded
 __syncthreads();

 // 每个线程计算 子矩阵的行列乘积,大循环外边还有累加,累加的是不同子矩阵点积和
 for (int k = 0; k < BLOCK_SIZE; ++k)
 {
 numsub += As[ty][k] * Bs[k][tx];
 }

 // 再次同步
 __syncthreads();
 }

if(BLOCK_SIZE * blockIdx.y + threadIdx.y<n && BLOCK_SIZE * blockIdx.x + threadIdx.x<m)
//C矩阵为n行m列的矩阵:C的行id=A的行id;C的列id=B的列id
//行:之前本行所在块上面块的一共行: blockIdx.y*Bsize,当前块第几行threadIdx.y,一共第几行:blockIdx.y*Bsize+threadIdx.y
//列:本列所在块之前块所有列:blockIdx.x*Bsize,当前块第几列:threadIdx.x;一共第几列:blockIdx.x*Bsize+threadIdx.x
//C中一共第几个元素:C行id✖️C列数+C列id=(blockIdx.y*Bsize+threadIdx.y)*m+blockIdx.x*Bsize+threadIdx.x

 C[(BLOCK_SIZE * blockIdx.y + threadIdx.y)*m + BLOCK_SIZE * blockIdx.x + threadIdx.x] = numsub;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cuda矩阵乘法(简单理解) 的相关文章

随机推荐

  • renren-fast-vue 无法启动成功原因

    renren fast vue 无法启动主要是因为node版本过高 需要使用8x版本 并且安装node sass node版本 https blog csdn net weixin 42713970 article details 8650
  • python实现全排列_python实现全排列代码(回溯、深度优先搜索)

    从n个不同元素中任取m m n 个元素 按照一定的顺序排列起来 叫做从n个不同元素中取出m个元素的一个排列 当m n时所有的排列情况叫全排列 公式 全排列数f n n 定义0 1 1 递归实现全排列 回溯思想 1 1 思想 举个例子 比如你
  • X-admin框架中change事件失效如何实现省市区三级联动

    Uncaught TypeError layui form is not a function的错误 我现在使用的就是X admin1 1版本的后台模板 在实现了三级联动的情况下想套上去结果发现会报上面的错误等等 在html中select标
  • 通俗易懂,带你了解应用面向对象(含例子超简单)

    面向对象的一些基本概念 软件工程第六版 下面都是面向对象的精华 好好吸收 完全可以掌握 拿一个圆做例子 有半径 周长 颜色 位置 我们现在有3个不同的圆 每个圆就是一个不同的的对象 但是他们都有相同的数据 半径 周长 和相同的操作 显示自己
  • ORA-00381: cannot use both new and old parameters for buffer cache size specification

    原创文章 转载请注明出处 作者 Lilge链接 http liglexiner itpub net post 43018 517486 修改了上次的undo tablespace的问题后 工作人员反映说 可以不调一下数据库 感觉查询很慢 于
  • 绑定域名,搭建私人网站

    前言 互联网上充斥着大大小小的网站 你是不是有时候也想拥有一个自己的网站 这其实并不难 你只需要满足这些条件即可 1 一台服务器 所谓的服务器可以简单理解为一台24小时不关机的电脑 并且这台服务器要有一个独立ip 本质上我们的个人电脑都可以
  • 微信小程序中如何获取用户手机号授权登录

    随着微信小程序的普及 许多应用程序需要用户登录才能提供更好的服务 而获取用户手机号码是验证用户身份和确保账户安全的重要步骤之一 因此 在本文中 我们将介绍如何在微信小程序中实现手机号授权登录 步骤一 在小程序后台添加手机号授权 首先 在小程
  • 安装配置nfs服务

    NFS的功能 让不同操作系统之间可以互传文件 Server端 Server端可以关闭防火墙 或放行nfs服务 systemctl stop firewalld 临时关闭防火墙 systemctl disable firewalld 开机自动
  • Spring Boot中yml文件和properties文件的区别?

    Spring Boot中application properties和application yml 1 在properties文件中是以 进行分割的 在yml中是用 进行分割 2 yml文件拥有天然的树状图 看得更舒服 3 yml是支持中
  • protobuf-2.6.1下载和安装

    下载地址 https github com protocolbuffers protobuf archive refs tags v2 6 1 zip autogen sh configure make make check sudo ma
  • 使用wireshark观察SSL/TLS握手过程--双向认证/单向认证

    SSL TLS握手过程可以分成两种类型 1 SSL TLS 双向认证 就是双方都会互相认证 也就是两者之间将会交换证书 2 SSL TLS 单向认证 客户端会认证服务器端身份 而服务器端不会去对客户端身份进行验证 我们知道 握手过程实际上就
  • Linux面试题

    文章目录 Linux 概述 什么是Linux Unix和Linux有什么区别 什么是 Linux 内核 Linux的基本组件是什么 Linux 的体系结构 BASH和DOS之间的基本区别是什么 Linux 开机启动过程 Linux系统缺省的
  • 存储器3-DDR SDRAM双倍速率同步动态存储器

    1 信号电平 采用STTL 2电平 2 5V 标准 VIH AC Vref 0 31 VIH DC Vref 0 15 VIL DC Vref 0 15 VIL AC Vref 0 31 高于VIH AC 为高 纹波只要不低于VIL DC
  • Angular_项目完善搜索功能(表单处理)

    在商品名称和商品价格以及商品类别都输入或者选择合法的情况下才能进行搜索 一 product service ts添加一个新的方法 获取所有商品类别 getAllCategories string return 电子产品 硬件设备 其他 二
  • 57 KVM工具使用指南-制作 LibcarePlus 热补丁

    文章目录 57 KVM工具使用指南 制作 LibcarePlus 热补丁 57 1 概述 57 2 手动制作 57 3 通过脚本制作 57 KVM工具使用指南 制作 LibcarePlus 热补丁 57 1 概述 LibcarePlus 支
  • 常见JDBC连接数据库字符串

    1 Mysql 驱动类 com mysql jdbc Driver 连接字符串 jdbc mysql localhost 3306 dbname 2 Oracle 驱动类 oracle jdbc driver OracleDriver 连接
  • 【数据库】Sqlite数据库

    1 sqlite数据库简介 SQLite是内嵌在Python中的轻量级 基于磁盘文件袋额数据库管理系统 就是一个文件 不需要安装和配置服务 支持使用SQL语句来访问数据库 该数据库使用C语言开发 支持大多数SQL91标准 支持原子的 一致的
  • c++ 共享内存方法实现windows进程通信

    主要逻辑 1 进程1 2通过读写一块共享内存完成这2个进程间的通信 2 进程互斥锁Mutex作用是实现进程同步 防止进程2一直读 实现进程1写一次进程2读一次 进程1代码 发送数据 include
  • keil新工程编译问题

    1 新建工程 找不到first和last 需要在工程中添加相对应芯片的start XXX swenjian 2 移植操作系统 error L6200E Symbol SysTick Handler multiply defined 这是在操
  • cuda矩阵乘法(简单理解)

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 CUDA矩阵乘法 矩阵规模 一 一维并行 1 一维线程并行 thread 2 一维块线程并行 block 3 一维共享A一行程并行 shared 二 二维并行 1 共享存储二