全局内存_global memory shared memory(共享内存)

1 全局内存 

     GPU的全局内存之所以是全局的,主要是因为GPU与CPU都可以对它进行写操作。任何设备都可以通过PCI-E总线对其进行访问。GPU之间不通过CPU,直接将数据从一块GPU卡上的数据传输到另一个GPU卡上。

 CPU主机端处理器可以通过以下三种方式对GPU上的内存进行访问:

【1】显式地阻塞传输

【2】显式地非阻塞传输

 【3】隐式的使用零内存复制。

 

2 共享内存    

  什么是共享内存:实际上可受用户控制的一级缓存。大小:每个SM中的一级缓存与共享内存共享一个64KB的内存段:

  什么时候使用共享内存:只有当数据重复利用,全局内存合并,或者线程之间有共享的数据使用

 编写代码时候:shared 的共享内存的声明,让一个线程块block 上的多个线程进行通信和协作。

                          那么这些多线程之间的【通信与协作】还需要其他操作:

                          【原因】: 如果线程A将一个值写入到共享内存,并且我们希望线程B对这个值进行一些操作,那么只有当线程A的写入操作完成之后,线程B才能开始执行它的操作。

                          【__syncthread()】完成线程同步【__syncthread()】完成线程同步需要注意的部分:当某些线程需要执行一条指令,而其他线程不需要执行时。

【共享内存的用例】:

【矩阵乘法】CPU实现 a*b = c 的矩阵乘法(矩阵尺寸是n*m的,n和m大于1000)

【优化思路】1. 将矩阵分块进行计算

                     2. 使用share memory进行优化

                     3. 将数据绑定在texture上

【CPU实现的矩阵乘法】

/*
* CPUMatMultiply:CPU下矩阵乘法
 * a:第一个矩阵指针,表示a[M][N]
 * b:第二个矩阵指针,表示b[N][S]
 * result:结果矩阵,表示为result[M][S]
*/
void CUPMatMultiply(const int *a, const int *b, int *result, const int M, const int N,const int S)
{

    for(int i = 0; i

【CUDA】的实现

从CPU上直接移植矩阵乘法到GPU上是非常简单的,不需要for循环,直接通过CUDA线程的id号,即threadIdx.xthreadIdx.y即可操作相应的数据。

/* gpuMatMultKernel:GPU下矩阵乘法核函数
*  a:第一个矩阵指针,表示a[M][N]
*  b:第二个矩阵指针,表示b[N][S]
*  result:结果矩阵,表示result[M][S]
--------------------- 
作者:hackairM 
来源:CSDN 
原文:https://blog.csdn.net/u010335328/article/details/52304688 
版权声明:本文为博主原创文章,转载请附上博文链接!
*/

__global__ void gpuMatMultKernel(const int *a, const int *b, int *result, const int M, const int N, const int S)
{

// 首先是grid, block and thread 的id 关系来的到每个线程的id 
  int threadId = (blockIdx.y * blockDim.y + threadIdx.y) * gridDim.x * blockDim.x 
                    + blockIdx.x * blockDim.x + threadIdx.x;



      if (threadId < M * S)
    {
        int row = threadId / S;
        int column = threadId % S;

        result[threadId] = 0;
        for (int i = 0; i < N; i++)
        {
            result[threadId] += a[row * N + i] * b[i * S + column];
        }
    }
}

【共性内存分块运算】

CUDA 支持共享内存: 关键字__shared__添加到声明中,这使这个变量驻留在共享内存中。

                                    【共享内存延迟低】共享内存缓存区驻留在物理GPU 上,而不是驻留在GPU之外的系统内存中。

【使用共享内存的核函数

/* gpuMatMultWithSharedKernel:GPU下使用shared内存的矩阵乘法
*  a:第一个矩阵指针,表示a[height_A][width_A]
*  b:第二个矩阵指针,表示b[width_A][width_B]
*  result:结果矩阵,表示result[height_A][width_B]
作者:hackairM 
来源:CSDN 
原文:https://blog.csdn.net/u010335328/article/details/52304688 
*/
template
 
__global__ void gpuMatMultWithSharedKernel(const int *a, const int *b, int *result, const int height_A)
{
    int block_x = blockIdx.x;
    int block_y = blockIdx.y;
    int thread_x = threadIdx.x;
    int thread_y = threadIdx.y;
    if ((thread_y + block_y * blockDim.y) * width_B + block_x * blockDim.x + thread_x >= height_A * width_B)
    {
        return;
    }
    const int begin_a = block_y * blockDim.y * width_A;
    const int end_a = begin_a + width_A - 1;
    const int step_a = blockDim.x;
    

    const int begin_b = block_x * blockDim.x;
    const int step_b = blockDim.y * width_B;
    int result_temp = 0;
   
     for (int index_a = begin_a, int index_b = begin_b;
        index_a < end_a; index_a += step_a, index_b += step_b)
    {  
        __shared__ int SubMat_A[BLOCK_SIZE][BLOCK_SIZE];
        __shared__ int SubMat_B[BLOCK_SIZE][BLOCK_SIZE];

        SubMat_A[thread_y][thread_x] = a[index_a + thread_y * width_A + thread_x];
        SubMat_B[thread_y][thread_x] = b[index_b + thread_y * width_B + thread_x];

        __syncthreads();

        for (int i = 0; i < BLOCK_SIZE; i++)
        {
            result_temp += SubMat_A[thread_y][i] * SubMat_B[i][thread_x];
        }

        __syncthreads();
       
         
}
       int begin_result = block_y * blockDim.y * width_B + begin_b;
       result[begin_result + thread_y * width_B + thread_x] = result_temp;
         

}

【纹理内存的运用】

在某个计算应用程序中,这意味着一个线程读取的位置可能与邻近线程读取的位置“非常接近”。

从数学的角度看,这四个地址并非连续的,在一般的CPU缓存模式中,这些地址将不会缓存。但由于GPU纹理缓存是专门为了加速这种访问模式而设计的,因此如果在这种情况中使用纹理内存而不是全局内存,那么将会获得性能提升。

/* gpuMatMultWithTextureKernel:GPU下使用texture内存的矩阵乘法
*  result:结果矩阵,表示为result[M][S];
*  M:表示为矩阵A与矩阵result的行数
*  N:表示矩阵A的列数,矩阵B的行数
*  S:表示矩阵B和矩阵result的列数
--------------------- 
作者:hackairM 
来源:CSDN 
原文:https://blog.csdn.net/u010335328/article/details/52304688 
*/
__global__ void gpuMatMultWithTextureKernel(int * result, const int M, const int N, const int S)
{
    int x = threadIdx.x + blockIdx.x * blockDim.x;
    int y = threadIdx.y + blockIdx.y * blockDim.y;
    int offset = x + y * blockDim.x * gridDim.x;

    if (offset < M * S)
    {
        int a = 0, b = 0;
        int temp_result = 0;
        for (int i = 0; i < N; i++)
        {
            a = tex1Dfetch(texA, y * N + i);
            b = tex1Dfetch(texB, i * S + x);
            temp_result += a * b;
        }
        result[offset] = temp_result;
    }
}

矩阵乘法计算中,运行时间上有:共享内存 < 纹理内存 ≈ 普通GPU移植 < CPU运算。 

【共享内存的局限】与L1 Cache公用一块on-chip内存,用户可以调整L1 cache与共享内存的大小组合。

【bank conflict】在on-chip内存的基础上,共享内存还实现了“并行访存”:共享内存被划分为大小相等的n个部分(每个部分称为一个bank),同一时刻的n个访存请求将分别通过这n个部分进行,从而使共享内存的带宽变为n倍!

当然,n倍带宽是最佳情况,如果这n个访存请求中存在对相同bank的请求,那么相同的请求将变为串行的,即排队进行。如果n个请求均是相同bank的,那么这n个请求就要串行执行,只能获得1倍带宽。这种现象称为bank conflict(bank冲突)。

【Warp_bank】: 一个Warp的 32个线程,在不用bank 中,这样就不会冲突了。

 

 

 

 

 

你可能感兴趣的:(全局内存_global memory shared memory(共享内存))