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. Dubbo的使用简介

    Dubbo是什么 官方定义 DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000, ...

  2. commitProperties方法

    自定义的组件,如果重写commitProperties方法,那么在该方法内部一定要注意super.commitProperties()的调用.

  3. 内嵌的Component调用外部的方法

    如果一个内嵌的Component控件需要调用外部定义的方法,用outerDocument.方法名来调用,前提是该方法是public的.如:<mx:DataGridColumn headerTex ...

  4. Java数据结构和算法

    首先,本人自学java,但是只学习了java的基础知识,所以想接下来学习一下数据结构和算法,但是找了很多教材,大部分写的好的都是用c语言实现的,虽然知道数据结构和算法,跟什么语言实现的没有关系,但是我 ...

  5. centos7安装httpd和php

    centos7许多命令都变了,又要重新记了. centos7默认安装了httpd吧?记不清了,看一下: rpm -qa |grep httpd 没有的话,安装一下吧. yum -y install h ...

  6. 一个使用openGL渲染的炫丽Android动画库

    android-magic-surface-view 这是一个 android 动画特效库, 可以实现各种炫酷动画. github地址: https://github.com/gplibs/andro ...

  7. OC中的私有变量和私有方法

    在类的实现即.m文件中也可以声明成员变量,但是因为在其他文件中通常都只是包含头文件而不会包含实现文件,所以在.m文件中声明的成员变量是@private得.在 .m中定义的成员变量不能和它的头文件.h中 ...

  8. Hadoop技术在商业智能BI中的应用

    Hadoop是个很流行的分布式计算解决方案,是Apache的一个开源项目名称,核心部分包括HDFS及MapReduce.其中,HDFS是分布式文件系统,MapReduce是分布式计算引擎.时至今日,H ...

  9. 浏览器存储(cookie、localStorage、sessionStorage)

    互联网早期浏览器是没有状态维护,这个就导致一个问题就是服务器不知道浏览器的状态,无法判断是否是同一个浏览器.这样用户登录.购物车功能都无法实现,Lou Montulli在1994年引入到web中最终纳 ...

  10. 学习笔记:javascript内置对象:字符串对象

    1.字符串的创建   var str = "Hello Microsoft!";   2.字符串属性   constructor  返回创建字符串属性的函数   length    ...