#include <stdio.h>__global__ voidstaticReverse(int*d,intn){ __shared__ ints[64];int t =threadIdx.x;int tr = n-t-1;s[t]=d[t];__syncthreads();d[t]=s[tr];}__global__ voiddynamicReverse(int*d,intn){extern __shared__ int s[];int t =threadIdx.x;int tr = n-t-1;s[t]=d[t];__syncthreads();d[t]=s[tr];}intmain(void){constint n =64;inta[n],r[n],d[n];for(int i =0; i < n; i++){a[i]= i;r[i]= n-i-1;d[i]=0;}int*d_d;cudaMalloc(&d_d, n *sizeof(int)); // run version with static shared memorycudaMemcpy(d_d, a, n*sizeof(int), cudaMemcpyHostToDevice); staticReverse<<<1,n>>>(d_d, n);cudaMemcpy(d, d_d, n*sizeof(int), cudaMemcpyDeviceToHost);for(int i =0; i < n; i++)if(d[i]!=r[i])printf("Error: d[%d]!=r[%d] (%d, %d)\n", i, i,d[i],r[i]); // run dynamic shared memory versioncudaMemcpy(d_d, a, n*sizeof(int), cudaMemcpyHostToDevice); dynamicReverse<<<1,n,n*sizeof(int)>>>(d_d, n);cudaMemcpy(d, d_d, n *sizeof(int), cudaMemcpyDeviceToHost);for(int i =0; i < n; i++)if(d[i]!=r[i])printf("Error: d[%d]!=r[%d] (%d, %d)\n", i, i,d[i],r[i]);}
当然,shared memory 也不是完全完美的,其还有可能引发一种新的冲突 ———— bank conflict: bank conflict 发生在 同一个warp中不同的线程访问同一个bank不同的数据 上面四个条件缺一不可造成 bank conflict: 1. 同一个 warp,不同的 warp 一般不考虑 bank conflict,因为 warp 的分发是随机的我们是不可知的。 2. 不同的 thread,同一个 thread 一个 clock cycle 只能存取一个 data 所以这是一句废话,但是要保证在同一个 warp 里面所以不是一句废话 3. 同一个 bank,对于不同的 bank 我们管不着 4. 不同的 data,对于同一个 bank 中,如果 不同的 thread 都访存的是一个 data,cuda 提供了广播的方法,类似于 python 中的 numpy 广播,反而是对性能的提升而不是下降,只有访存不同的 data 的时候才会出现 bank conflict
Figure Explanation
上面的三种访存,左侧数字对应的同一个 warp 中 thread 的编号(在 CC 2.0 以上每个 warp 中都是 32个线程),右侧的黄色是 bank 的编号(在 CC 2.0 以上shared memory 中的 bank 的个数也是32),注意黄色的 bank 内部的橙色小矩形代表的是 data。
在 Left 和 Right 中,每个 thread 都访问的是不同的 bank,所以不满足 bank conflict 的第三个条件,没有发生 bank conflict。 在 Middle 中,相当于是 stride=16 的访问导致了偶数编号的 bank 同时被两个线程所访问,而且访问的是不同的 data,所以出现了 two-way bank conflict。
上面的左中右都没有出现 bank conflict,因为即使有两个线程访问同一个 bank,他们内部访问的都是同一个 data,不满足 bank conflict 的第四个条件,所以都没有发生 bank conflict。
This is only a preference. The runtime will use the requested configuration if possible, but it is free to choose a different configuration if required to execute func.
__global__ void dynamicReverse(int *d, int n)
{
extern __shared__ int s[]; //**** extern 获取核函数分配的动态内存
int t = threadIdx.x;
int tr = n-t-1;
s[t] = d[t];
__syncthreads();
d[t] = s[tr];
}
__global__
void simpleMultiply(float *a, float* b, float *c, int N)
{
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;
for (int i = 0; i < TILE_DIM; i++) {
sum += a[row*TILE_DIM+i] * b[i*N+col];
}
c[row*N+col] = sum;
}
__global__
void coalescedMultiply(float *a, float* b, float *c, int N)
{
__shared__ float aTile[TILE_DIM][TILE_DIM];
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;
aTile[threadIdx.y][threadIdx.x] = a[row*TILE_DIM+threadIdx.x];
__syncwarp(); //***
for (int i = 0; i < TILE_DIM; i++) {
sum += aTile[threadIdx.y][i]* b[i*N+col];
}
c[row*N+col] = sum;
}
__global__
void sharedABMultiply(float *a, float* b, float *c, int N)
{
__shared__ float aTile[TILE_DIM][TILE_DIM],
bTile[TILE_DIM][TILE_DIM];
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;
aTile[threadIdx.y][threadIdx.x] = a[row*TILE_DIM+threadIdx.x];
bTile[threadIdx.y][threadIdx.x] = b[threadIdx.y*N+col];
__syncthreads(); // ***
for (int i = 0; i < TILE_DIM; i++) {
sum += aTile[threadIdx.y][i]* bTile[i][threadIdx.x];
}
c[row*N+col] = sum;
}
__global__
void simpleMultiply(float *a, float *c, int M)
{
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;
for (int i = 0; i < TILE_DIM; i++) {
sum += a[row*TILE_DIM+i] * a[col*TILE_DIM+i];
}
c[row*M+col] = sum;
}
__global__
void coalescedMultiply(float *a, float *c, int M)
{
__shared__ float aTile[TILE_DIM][TILE_DIM];
__shared__ float transposedTile[TILE_DIM][TILE_DIM];
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;
aTile[threadIdx.y][threadIdx.x] = a[row*TILE_DIM+threadIdx.x];
transposedTile[threadIdx.x][threadIdx.y] =
a[(blockIdx.x*blockDim.x + threadIdx.y)*TILE_DIM +
threadIdx.x];
__syncthreads();
for (int i = 0; i < TILE_DIM; i++) {
sum += aTile[threadIdx.y][i]* transposedTile[i][threadIdx.x];
}
c[row*M+col] = sum;
}