1.最简单的 kernel 函数

__global__ void MatrixMulKernel( float* Md, float* Nd, float* Pd, int Width)
{
int tx = threadIdx.x; // cloumn
int ty = threadIdx.y; // row float Pvalue = 0; for (int k = 0; k<Width; k++)
{
float Mdele = Md[ty*Width + k];
float Ndele = Nd[k*Width + tx];
Pvalue += Mdele * Ndele;
} Pd[ty*Width + tx] = Pvalue;
}

2.适用更大矩阵

第一节中例子缺点是,假如使用更多的块时,每个块中会计算相同的矩阵。而且矩阵元素不能超过512个线程(块最大线程限制)。

改进方法是,假设每个块维度都是方阵形式,且其维度由变量 TILE_WIDTH 指定。矩阵 Pd 的每一维都划分为部分,每个部分包含 TILE_WIDTH 个元素。

#define TILE_WIDTH 4
#define Width 8 __global__ void MatrixMulKernel( float* Md, float* Nd, float* Pd, int Width)
{
int Col = blockId.x*TILE_WIDTH + threadIdx.x; // cloumn
int Row = blockId.y*TILE_WIDTH + threadIdx.y; // row float Pvalue = 0; for (int k = 0; k<Width; k++)
{
Pvalue += Md[Row*Width + k] * Nd[k*Width + Col];
} Pd[ty*Width + tx] = Pvalue;
}
dim3 dimGrid(Width/TILE_WIDTH, Width/TILE_WIDTH);
dim3 dimBlock(TILE_WIDTH, TILE_WIDTH);

CUDA 硬件相关概念

针对 GT200而言

  • 每个 SM 有最多8个块
  • 每个 SM 有最多1024个线程

应当注意每个块中分配的线程数,以便能够充分利用SM

  • 以32个线程为一个 warpwarp 为线程调度单位

3.通过改变储存器提到访问效率

CGMA(Compute to Global Memory Access),尽量提高CGMA比值。

对前面来说,每个for循环内需要两次访问全局内存(Md[Row*Width + k]Nd[k*Width + Col]),两次浮点计算(加法与乘法)。因此 CGMA 为2:2 = 1。

G80 全局存储器带宽为 86.4 GB/s,计算峰值性能 367 Gflops。(每秒取 21.6G 个变量,由于CGMA=1,会进行 21.6G 次浮点计算)

若每个单精度浮点数为 4 字节,那么由于存储器限制,最大浮点操作不会超过 21.6 Gflops。

| 存储器类型 | 变量 | 周期 | 特点 | 访问速度 |

| --- | --- | --- | --- |

| 共享存储器 | 共享变量 | kernel函数 | 每个块中所有线程都可以访问,用于线程间协作高效方式 | 相当快,高度并行访问 |

| 常数存储器 | 常数变量 | 所有网格 | 相当快,并行访问 |

| 寄存器 | 自动变量 | 线程 | 寄存器具有储存容量限制 | 非常块

| 全局存储器 | 全局变量 | | 用于调用不同kernel函数时传递信息 | 慢 |

减少全局存储器流量策略

各个存储器特点:

  • 全局存储器,容量大,访问慢
  • 共享存储器,容量小,访问块

新算法要点:

  • 将共享存储器上数据划分子集,每个子集满足共享存储器容量限制
  • 通过线程协作将 MN 中的元素加载到共享存储器中,每个线程负责块中一个元素赋值(Mds[threadIdx.y][threadIdx.x]Nds[threadIdx.y][threadIdx.x]
  • 通过加载到共享存储器,使得每个块访问全局内存的次数减小为原来的 TILE_WIDTH 分之一(加载到共享内存时读取一次,每个块中使用 TILE_WIDTH 次)
__global__ void kernel_tile(float* M, float* N, float* P){
int i, k;
float Pvalue = 0; __shared__ float Mds[tile_width][tile_width];
__shared__ float Nds[tile_width][tile_width]; int Row = threadIdx.y + blockIdx.y*tile_width;
int Col = threadIdx.x + blockIdx.x*tile_width; for( i = 0; i< width/tile_width; i++ ){
Mds[threadIdx.y][threadIdx.x] = M[Row*width + i*tile_width + threadIdx.x];
Nds[threadIdx.y][threadIdx.x] = N[(threadIdx.y + i*tile_width)*width + Col];
__syncthreads();
for (k = 0; k<tile_width; k++){
Pvalue += Mds[threadIdx.y][k]*Nds[k][threadIdx.x];
}
__syncthreads();
}
P[Row*width + Col] = Pvalue;
}

存储器容量限制

G80 硬件中

  • 每个 SM 寄存器大小为 8 KB(8192 B)
  • 每个 SM 共享内存大小为 16 KB
  • 若每个 SM 中容纳线程数为 768,那么每个线程可用寄存器不超过 8 KB/768=10 Bytes(两个单精度变量占用 8 Bytes)
  • 若每个线程占用了多余 10 Bytes,那么就会减少 SM 上线程数,且以块为单位减少
  • 若每个 SM 中容纳 8 个块,那么每个块不能使用超过 2 KB 的存储器

4.数据预取

CUDA 中,当某些线程在等待其存储器访问结果时,CUDA 线程模型可以通过允许其他 warp 继续运行,这样就能容许长时间的访问延时。

为了充分利用此特性,需要当使用当前数据元素时预取下一个数据元素,这样就可以正价在储存器访问和已访问的数据使用指令之间的独立指令的数目。

预取技术经常和分块技术结合,解决带宽限制和长时间延迟问题。

例如在第三节中矩阵乘法问题中,使用预取技术后函数流程变为

对应程序为

__global__ void kernel_prefetch(float* M, float* N, float* P){
int i;
float Pvalue = 0;
float Mc, Nc;
int Row = threadIdx.y + tile_width*blockIdx.y;
int Col = threadIdx.x + tile_width*blockIdx.x; __shared__ float Mds[tile_width][tile_width];
__shared__ float Nds[tile_width][tile_width]; Mc = M[Row*width + threadIdx.x];
Nc = N[Col + threadIdx.y*width]; for (i = 1; i<width/tile_width+1; i++){ Mds[threadIdx.y][threadIdx.x] = Mc;
Nds[threadIdx.y][threadIdx.x] = Nc; __syncthreads(); Mc = M[Row*width + threadIdx.x + i*tile_width];
Nc = N[Col + (threadIdx.y + i*tile_width)*width]; for (int k = 0; k<tile_width; k++){
Pvalue += Mds[threadIdx.y][k]*Nds[k][threadIdx.x];
}
__syncthreads();
}
P[Col + Row*width] = Pvalue;
}

McNc 为增加的两个储存在寄存器内的变量。

CUDA计算矩阵相乘的更多相关文章

  1. STL模板之_map,stack(计算矩阵相乘的次数)

    #include <map>#include <stack>#include <iostream>using namespace std; struct Node ...

  2. Java实现矩阵相乘问题

    1 问题描述 1.1实验题目 设M1和M2是两个n×n的矩阵,设计算法计算M1×M2 的乘积. 1.2实验目的 (1)提高应用蛮力法设计算法的技能: (2)深刻理解并掌握分治法的设计思想: (3)理解 ...

  3. CUDA编程-(2)其实写个矩阵相乘并不是那么难

    程序代码及图解析: #include <iostream> #include "book.h" __global__ void add( int a, int b, i ...

  4. 编程计算2×3阶矩阵A和3×2阶矩阵B之积C。 矩阵相乘的基本方法是: 矩阵A的第i行的所有元素同矩阵B第j列的元素对应相乘, 并把相乘的结果相加,最终得到的值就是矩阵C的第i行第j列的值。 要求: (1)从键盘分别输入矩阵A和B, 输出乘积矩阵C (2) **输入提示信息为: 输入矩阵A之前提示:"Input 2*3 matrix a:\n" 输入矩阵B之前提示

    编程计算2×3阶矩阵A和3×2阶矩阵B之积C. 矩阵相乘的基本方法是: 矩阵A的第i行的所有元素同矩阵B第j列的元素对应相乘, 并把相乘的结果相加,最终得到的值就是矩阵C的第i行第j列的值. 要求: ...

  5. cuda计算的分块

    gpu的架构分为streaming multiprocessors 每个streaming multiprocessors(SM)又能分步骤执行很多threads,单个SM内部能同时执行的thread ...

  6. 利用Hadoop实现超大矩阵相乘之我见(二)

    前文 在<利用Hadoop实现超大矩阵相乘之我见(一)>中我们所介绍的方法有着“计算过程中文件占用存储空间大”这个缺陷,本文中我们着重解决这个问题. 矩阵相乘计算思想 传统的矩阵相乘方法为 ...

  7. 利用Hadoop实现超大矩阵相乘之我见(一)

    前记 最近,公司一位挺优秀的总务离职,欢送宴上,她对我说“你是一位挺优秀的程序员”,刚说完,立马道歉说“对不起,我说你是程序员是不是侮辱你了?”我挺诧异,程序员现在是很低端,很被人瞧不起的工作吗?或许 ...

  8. POJ 2246 Matrix Chain Multiplication(结构体+栈+模拟+矩阵相乘)

    题意:给出矩阵相乘的表达式,让你计算需要的相乘次数,如果不能相乘,则输出error. 思路: 参考的网站连接:http://blog.csdn.net/wangjian8006/article/det ...

  9. MapReduce实现矩阵相乘

    矩阵相乘能够查看百度百科的解释http://baike.baidu.com/view/2455255.htm?fr=aladdin 有a和b两个矩阵 a:                1   2   ...

随机推荐

  1. JAVA String、StringBuffer、StringBuilder类解读

    JAVA String.StringBuffer.StringBuilder类解读 字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作 ...

  2. 整理一下在 npmjs.com 上面发布资源包踩过的坑

    正常流程就不说了,网上有很多,比如写代码.打包.注册.登录.发布等. 邮箱要激活 在 npmjs.com 上面注册账号的时候需要填写邮箱,然后登录网址的时候并没有强制要求你去邮箱激活. 但是到了发布资 ...

  3. 关于java socket中的read方法阻塞问题

    客户端: public class TCPClient { public static void main(String[] args) throws IOException { FileInputS ...

  4. [Git系列] Git 基本概念

    版本控制系统 版本控制系统是一种帮助软件开发者实现团队合作和历史版本维护的软件,一个版本控制系统应具备以下列出的这几个基本功能: 允许开发者并发工作: 不允许一个开发者覆写另一个开发者的修改: 保存所 ...

  5. FastAPI 学习之路(三十三)操作数据库

    通过创建pydantic模型进行验证提交数据 from pydantic import BaseModel class UserBase(BaseModel): email: str class Us ...

  6. Noip模拟29(瞎眼忌) 2021.8.3

    T1 最长不下降子序列 在此记录自己的瞎眼... 考场上像一个傻$der$,自己为了防范上升序列和不下降序列的不同特意的造了一组$hack$数据来卡自己:(第一行是序列长度,第二行是序列) 6 1 5 ...

  7. AOP源码解析:AspectJAwareAdvisorAutoProxyCreator类的介绍

    AspectJAwareAdvisorAutoProxyCreator 的类图 上图中一些 类/接口 的介绍: AspectJAwareAdvisorAutoProxyCreator : 公开了Asp ...

  8. 决策树 机器学习,西瓜书p80 表4.2 使用信息增益生成决策树及后剪枝

    使用信息增益构造决策树,完成后剪枝 目录 使用信息增益构造决策树,完成后剪枝 1 构造决策树 1 根结点的选择 色泽 信息增益 根蒂 信息增益 敲声 信息增益 纹理 信息增益 脐部 信息增益 触感 信 ...

  9. 集合栈 牛客网 程序员面试金典 C++ Python

    集合栈 牛客网 程序员面试金典 C++ Python 题目描述 请实现一种数据结构SetOfStacks,由多个栈组成,其中每个栈的大小为size,当前一个栈填满时,新建一个栈.该数据结构应支持与普通 ...

  10. 如何抓取直播源及视频URL地址-疯狂URL(教程)

    直播源介绍 首先,我们来快速了解一下什么是直播源,所谓的直播源,其实就说推流地址,推流地址可能你也不知道是什么,那么我再简单说一下,推流地址就是,当某个直播开播的时候,需要将自己的直播状态实时的展示给 ...