我有以下内核来获取一堆向量的大小:
__global__ void norm_v1(double *in, double *out, int n)
{
const uint i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n)
{
double x = in[3*i], y = in[3*i+1], z = in[3*i+2];
out[i] = sqrt(x*x + y*y + z*z);
}
}
然而,由于将in
打包为[x0,y0,z0,...,xn,yn,zn]
,因此使用分析器表示32%的全局负载效率表现不佳。将数据重新打包为[x0, x1, ..., xn, y0, y1, ..., yn, z0, z1, ..., zn]
会大大改善事情(x
,y
和z
的偏移会相应地改变。运行时间缩短,效率高达100%。
然而,这种包装对我的应用来说根本不实用。因此,我希望调查共享内存的使用。我的想法是让一个块中的每个线程从全局内存中复制三个值(blockDim.x
) - 产生合并访问。在最大blockDim.x = 256
的假设下,我提出了:
#define BLOCKDIM 256
__global__ void norm_v2(double *in, double *out, int n)
{
__shared__ double invec[3*BLOCKDIM];
const uint i = blockIdx.x * blockDim.x + threadIdx.x;
invec[0*BLOCKDIM + threadIdx.x] = in[0*BLOCKDIM+i];
invec[1*BLOCKDIM + threadIdx.x] = in[1*BLOCKDIM+i];
invec[2*BLOCKDIM + threadIdx.x] = in[2*BLOCKDIM+i];
__syncthreads();
if (i < n)
{
double x = invec[3*threadIdx.x];
double y = invec[3*threadIdx.x+1];
double z = invec[3*threadIdx.x+2];
out[i] = sqrt(x*x + y*y + z*z);
}
}
然而,n % blockDim.x != 0
显然存在缺陷,需要提前知道最大blockDim
,并在使用out[i > 255]
进行测试时为n = 1024
生成错误的结果。我该如何最好地解决这个问题呢?
答案 0 :(得分:1)
我认为这可以解决out[i > 255]
问题:
__shared__ double shIn[3*BLOCKDIM];
const uint blockStart = blockIdx.x * blockDim.x;
invec[0*blockDim.x+threadIdx.x] = in[ blockStart*3 + 0*blockDim.x + threadIdx.x];
invec[1*blockDim.x+threadIdx.x] = in[ blockStart*3 + 1*blockDim.x + threadIdx.x];
invec[2*blockDim.x+threadIdx.x] = in[ blockStart*3 + 2*blockDim.x + threadIdx.x];
__syncthreads();
double x = shIn[3*threadIdx.x];
double y = shIn[3*threadIdx.x+1];
double z = shIn[3*threadIdx.x+2];
out[blockStart+threadIdx.x] = sqrt(x*x + y*y + z*z);
至于n % blockDim.x != 0
我建议用0填充输入/输出数组以匹配要求。
如果您不喜欢BLOCKDIM
宏 - 请使用extern __shared__ shArr[]
进行探索,然后将第3个参数传递给内核配置:
norm_v2<<<gridSize,blockSize,dynShMem>>>(...)
dynShMem
是动态共享内存使用量(以字节为单位)。这是额外的共享内存池,其大小在运行时指定,其中所有extern __shared__
变量将最初分配给。
你使用的是什么GPU? Fermi或Kepler 可以通过L1缓存来帮助您的原始代码。
如果您不想填充in
数组,或者您最终在其他地方执行类似技巧,则可能需要考虑实施设备端memcopy
,如下所示:< / p>
template <typename T>
void memCopy(T* destination, T* source, size_t numElements) {
//assuming sizeof(T) is a multiple of sizeof(int)
//assuming one-dimentional kernel (only threadIdx.x and blockDim.x matters)
size_t totalSize = numElements*sizeof(T)/sizeof(int);
int* intDest = (int*)destination;
int* intSrc = (int*)source;
for (size_t i = threadIdx.x; i < totalSize; i += blockDim.x) {
intDest[i] = intSrc[i];
}
__syncthreads();
}
它基本上将任何数组视为int
- s的数组,并将数据从一个位置复制到另一个位置。如果您只使用64位类型,则可能希望将基础int
类型替换为double
- s或long long int
。
然后您可以用以下内容替换复制行:
memCopy(invec, in+blockStart*3, min(blockDim.x, n-blockStart));