GPU的内存按照所属对象大致分为三类:线程独有的、block共享的、全局共享的。细分的话,包含global, local, shared, constant, and texture memoey, 我们重点关注以下两类内存

  • Global memory

Global memory resides in device memory and device memory is accessed via 32-, 64-, or 128-bytes memory transactions

  • Shared memory

Because it is on-chip, shared memory has much higher bandwidth and much lower latency than local or global memory

简单理解就是,Shared memory更快。以下是内存按照所属对象分类示意图

有了对Global memory、Shared memory的印象之后,我们通过矩阵相乘的例子要谈谈这两种内存的运用,并对比他们的优劣(老规矩,先代码,后解释)

// Matrices are stored in row-major order:
// M(row, col) = *(M.elements + row * M.width + col)
typedef struct {
  int width;
  int height;
  float* elements;
} Matrix;
// Thread block size
#define BLOCK_SIZE 16
// Forward declaration of the matrix multiplication kernel
__global__ void MatMulKernel(const Matrix, const Matrix, Matrix);
// Matrix multiplication - Host code
// Matrix dimensions are assumed to be multiples of BLOCK_SIZE
void MatMul(const Matrix A, const Matrix B, Matrix C)
{
  // Load A and B to device memory
  Matrix d_A;
  d_A.width = A.width; d_A.height = A.height;
  size_t size = A.width * A.height * sizeof(float);
  cudaMalloc(&d_A.elements, size);
  cudaMemcpy(d_A.elements, A.elements, size,
  cudaMemcpyHostToDevice);
  Matrix d_B;
  d_B.width = B.width; d_B.height = B.height;
  size = B.width * B.height * sizeof(float);
  cudaMalloc(&d_B.elements, size);
  cudaMemcpy(d_B.elements, B.elements, size,
  cudaMemcpyHostToDevice);
  // Allocate C in device memory
  Matrix d_C;
  d_C.width = C.width; d_C.height = C.height;
  size = C.width * C.height * sizeof(float);
  cudaMalloc(&d_C.elements, size);
  // Invoke kernel
  dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
  dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);
  MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);
  // Read C from device memory
  cudaMemcpy(C.elements, Cd.elements, size,
  cudaMemcpyDeviceToHost);
  // Free device memory
  cudaFree(d_A.elements);
  cudaFree(d_B.elements);
  cudaFree(d_C.elements);
}
// Matrix multiplication kernel called by MatMul()
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)
{
  // Each thread computes one element of C
  // by accumulating results into Cvalue
  float Cvalue = ;
  int row = blockIdx.y * blockDim.y + threadIdx.y;
  int col = blockIdx.x * blockDim.x + threadIdx.x;
  for (int e = ; e < A.width; ++e)
    Cvalue += A.elements[row * A.width + e]
      * B.elements[e * B.width + col];
  C.elements[row * C.width + col] = Cvalue;
}

计算原理如下

host端代码很常规,我们重点关注__global__标记的这个device端代码,她完成的功能很简单就是去A矩阵的一行、B矩阵的一列。行列对应元素相乘累加,也就是向量的点击运算。当运算结束的时候矩阵C=AB。这是很常规的一种思路。

那么,如何用Shared memory完成上述功能呢?这样的好处又是什么呢?(老规矩,先代码,后解释)

// Matrices are stored in row-major order:
// M(row, col) = *(M.elements + row * M.stride + col)
typedef struct {
  int width;
  int height;
  int stride;
  float* elements;
} Matrix;
// Get a matrix element
__device__ float GetElement(const Matrix A, int row, int col)
{
  return A.elements[row * A.stride + col];
}
// Set a matrix element
__device__ void SetElement(Matrix A, int row, int col,
  float value)
{
  A.elements[row * A.stride + col] = value;
}
// Get the BLOCK_SIZExBLOCK_SIZE sub-matrix Asub of A that is
// located col sub-matrices to the right and row sub-matrices down
// from the upper-left corner of A
__device__ Matrix GetSubMatrix(Matrix A, int row, int col)
{
  Matrix Asub;
  Asub.width = BLOCK_SIZE;
  Asub.height = BLOCK_SIZE;
  Asub.stride = A.stride;
  Asub.elements = &A.elements[A.stride * BLOCK_SIZE * row
    + BLOCK_SIZE * col];
  return Asub;
}
// Thread block size
#define BLOCK_SIZE 16
// Forward declaration of the matrix multiplication kernel
__global__ void MatMulKernel(const Matrix, const Matrix, Matrix);
// Matrix multiplication - Host code
// Matrix dimensions are assumed to be multiples of BLOCK_SIZE
void MatMul(const Matrix A, const Matrix B, Matrix C)
{
  // Load A and B to device memory
  Matrix d_A;
  d_A.width = d_A.stride = A.width; d_A.height = A.height;
  size_t size = A.width * A.height * sizeof(float);
  cudaMalloc(&d_A.elements, size);
  cudaMemcpy(d_A.elements, A.elements, size,
  cudaMemcpyHostToDevice);
  Matrix d_B;
  d_B.width = d_B.stride = B.width; d_B.height = B.height;
  size = B.width * B.height * sizeof(float);
  cudaMalloc(&d_B.elements, size);
  cudaMemcpy(d_B.elements, B.elements, size,
  cudaMemcpyHostToDevice);
  // Allocate C in device memory
  Matrix d_C;
  d_C.width = d_C.stride = C.width; d_C.height = C.height;
  size = C.width * C.height * sizeof(float);
  cudaMalloc(&d_C.elements, size);
  // Invoke kernel
  dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
  dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);
  MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);
  // Read C from device memory
  cudaMemcpy(C.elements, d_C.elements, size,
  cudaMemcpyDeviceToHost);
  // Free device memory
  cudaFree(d_A.elements);
  cudaFree(d_B.elements);
  cudaFree(d_C.elements);
}
// Matrix multiplication kernel called by MatMul()
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)
{
  // Block row and column
  int blockRow = blockIdx.y;
  int blockCol = blockIdx.x;
  // Each thread block computes one sub-matrix Csub of C
  Matrix Csub = GetSubMatrix(C, blockRow, blockCol);
  // Each thread computes one element of Csub
  // by accumulating results into Cvalue
  float Cvalue = ;
  // Thread row and column within Csub
  int row = threadIdx.y;
  int col = threadIdx.x;
  // Loop over all the sub-matrices of A and B that are
  // required to compute Csub
  // Multiply each pair of sub-matrices together
  // and accumulate the results
  for (int m = ; m < (A.width / BLOCK_SIZE); ++m) {
  // Get sub-matrix Asub of A
    Matrix Asub = GetSubMatrix(A, blockRow, m);
  // Get sub-matrix Bsub of B
  Matrix Bsub = GetSubMatrix(B, m, blockCol);
  // Shared memory used to store Asub and Bsub respectively
  __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
  __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
  // Load Asub and Bsub from device memory to shared memory
  // Each thread loads one element of each sub-matrix
  As[row][col] = GetElement(Asub, row, col);
  Bs[row][col] = GetElement(Bsub, row, col);
  // Synchronize to make sure the sub-matrices are loaded
  // before starting the computation
  __syncthreads();   // Multiply Asub and Bsub together
  for (int e = ; e < BLOCK_SIZE; ++e)
    Cvalue += As[row][e] * Bs[e][col];
  // Synchronize to make sure that the preceding
  // computation is done before loading two new
  // sub-matrices of A and B in the next iteration
  __syncthreads();
  }
  // Write Csub to device memory
  // Each thread writes one element
  SetElement(Csub, row, col, Cvalue);
}

计算原理如下

__device__标记的函数只能由__device__、__global__标记的函数调用。GetElement函数就是得到矩阵A(row,col)这一坐标上的值,SetElement函数就是将矩阵A(row,col)的值设置为value。GetSubMatrix函数就是得到矩阵A的子矩阵,用matlab的语法表示就是Asub=A[row:row+BLOCK_SIZE,col:col+BLOCK_SIZE]。

host端代码还是很常规的,下面重点分析__global__标记的函数。这个函数是以block为单位组织的,她首先获取矩阵C的一个子矩阵Csub,然后用该block内的线程ID索引Csub矩阵的所有元素。每一次for循环,获取A的子矩阵Asub、B的子矩阵Bsub(请参考上述示意图)。然后将Asub、Bsub的有global memory搬迁到shared memory。__syncthreads()的作用是,等所有的线程都将数据搬迁完了,再向下执行。之后的一个for循环完成的功能是Asub、Bsub对应元素向量点击运算。沿A的宽度方向、B的高度方向迭代,即可完成Csub内所有点的向量点击运算。

总结:引入shared memory的好处可以概括为“不要把时间浪费在路上,尤其是路途遥远的路上”。将Global memory的数据搬迁到thread比较费时。

GPU编程--Shared Memory(4)的更多相关文章

  1. GPU 编程入门到精通(五)之 GPU 程序优化进阶

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...

  2. CUDA ---- Shared Memory

    CUDA SHARED MEMORY shared memory在之前的博文有些介绍,这部分会专门讲解其内容.在global Memory部分,数据对齐和连续是很重要的话题,当使用L1的时候,对齐问题 ...

  3. GPU编程自学6 —— 函数与变量类型限定符

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  4. 【并行计算-CUDA开发】CUDA shared memory bank 冲突

    CUDA SHARED MEMORY shared memory在之前的博文有些介绍,这部分会专门讲解其内容.在global Memory部分,数据对齐和连续是很重要的话题,当使用L1的时候,对齐问题 ...

  5. GPU编程和流式多处理器

    GPU编程和流式多处理器 流式多处理器(SM)是运行CUDA内核的GPU的一部分.本章重点介绍SM的指令集功能. 流式多处理器(SM)是运行我们的CUDA内核的GPU的一部分.每个SM包含以下内容. ...

  6. C++: Virtual Table and Shared Memory

    See at: 补充栏3: C++对象和共享内存 (叙述内容和Link1的内容基本一致) <C++网络编程 卷1:运用ACE和模式消除复杂性> <C++ Network Progra ...

  7. GPU编程自学4 —— CUDA核函数运行参数

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  8. GPU 编程相关 简要摘录

    GPU 编程可以称为异构编程,最近由于机器学习的火热,很多模型越来越依赖于GPU来进行加速运算,所以异构计算的位置越来越重要:异构编程,主要是指CPU+GPU或者CPU+其他设备(FPGA等)协同计算 ...

  9. 使用shared memory 计算矩阵乘法 (其实并没有加速多少)

    #include "cuda_runtime.h" #include "device_launch_parameters.h" #include "d ...

随机推荐

  1. 【stm32中断优先级--珍藏版】

    看了这么久,一直不理解中断优先级,还有中断嵌套.stm32提供了多种嵌套方式,搞的我真是头昏脑涨. 今天终于看到了一个通俗解释中断优先级的博客.算是理解了一点吧. 原文地址:http://blog.s ...

  2. 【Windows 10 应用开发】输入模拟

    ---恢复内容开始--- Input Injection 直译为:输入注入.通俗的译法为:模拟输入.此注入行为可以模拟以下几种输入行为: 1.键盘按键. 2.鼠标. 3.触控. 4.书写笔输入. 5. ...

  3. redis多端口,多实例。

    本屌的redis装载/usr/local/redis了 cp    一份配置 vim  配置文件 port 修改 bind 绑定ip,默认0.0.0.0所有用户允许访问. dbfilename cac ...

  4. Linux环境下用户空间与内核空间数据的交换方式

    在linux环境开发过程中,经常会需要在用户空间和内核空间之间进行数据交换. 介绍了 Linux 系统下用户空间与内核空间数据交换的几种方式 第一节:使用procfs实现内核交互简明教程(1) 第二节 ...

  5. 1018 Big Number

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...

  6. Bootstrap之折叠(Collapse)插件

    学习资料:Bootstrap折叠(Collapse)插件 大家可能常见的都是类似: 这种的效果,小颖今天要给大家分享一个不一样的效果嘻嘻.铛铛铛铛........................... ...

  7. Mirantis MCP 1.0:OpenStack 和 Kubernetes 整合的第一步

    1.前言 Mirantis 公司在2014年9月14日宣布收购 TCPCloud,然后宣布在2017年第一季度会推出全新的私有云产品.从那时候开始,我就一直满怀期待.终于,今年4月19日,Mirant ...

  8. 开源框架是如何通过JMX来做监控的(一) - JMX简介和Standard MBean

    相关文章目录: 开源框架是如何通过JMX来做监控的(一) - JMX简介和Standard MBean 开源框架是如何通过JMX来做监控的(二) - Druid连接池的监控 相信很多做Java开发的同 ...

  9. HotSpot的算法实现

    1.枚举根节点 可达性分析中从GC Roots节点找引用,可作为GC Roots的节点主要是全局性的引用与执行上下文中,如果要逐个检查引用,必然消耗时间.另外可达性分析对执行时间的敏感还体现在GC停顿 ...

  10. 也许是目前实现最好的js模拟滚动插件

    finger-mover 是一个集成 Fingerd(移动端以手指为单位的事件管理方案) 和 Moved(微型运动方案) 为一体的移动端动效平台,finger-mover 本身并不能为你做什么,但是附 ...