为什么这个内核会生成不连贯的商店
__global__ void reverseArrayBlock(int *d_out, int *d_in)
{
int inOffset = blockDim.x * blockIdx.x;
int outOffset = blockDim.x * (gridDim.x - 1 - blockIdx.x);
int in = inOffset + threadIdx.x;
int out = outOffset + (blockDim.x - 1 - threadIdx.x);
d_out[out] = d_in[in];
}
而且这个不是
__global__ void reverseArrayBlock(int *d_out, int *d_in)
{
extern __shared__ int s_data[];
int inOffset = blockDim.x * blockIdx.x;
int in = inOffset + threadIdx.x;
// Load one element per thread from device memory and store it
// *in reversed order* into temporary shared memory
s_data[blockDim.x - 1 - threadIdx.x] = d_in[in];
// Block until all threads in the block have written their data to shared mem
__syncthreads();
// write the data from shared memory in forward order,
// but to the reversed block offset as before
int outOffset = blockDim.x * (gridDim.x - 1 - blockIdx.x);
int out = outOffset + threadIdx.x;
d_out[out] = s_data[threadIdx.x];
}
我知道第二个是使用共享内存。但是当我看到d_out的指标时,它们在两个内核中看起来都是一样的。你能帮我理解一下吗?
答案 0 :(得分:3)
合并要求地址遵循warp中的“base + tid”模式,其中tid是线程索引的缩写。换句话说,随着tid增加,地址也增加。您的评论称此为“转发订单”。在第一个内核中,生成地址,使得当tid增加时,地址减少,即访问以“向后顺序”。
答案 1 :(得分:0)
在我们开始之前,您需要了解对共享内存的写入比写入全局内存要便宜得多。
考虑到这一点,我们假设我们正在反转阵列1-> 32
方法一这样做: 写作的同时 线程1从位置x读取,线程2从(x + 1)读取,线程3从位置读取(x + 2)...线程32从位置读取(x + 31)。
你可以在2(如果是对齐的)或3(如果是非对齐的)读取中读取整个内存块,因为操作是在半块(16个线程)的块中完成的。
写作时 线程1写入位置(y + 31),线程2写入(y + 30),线程3写入位置(y + 29)...线程32写入位置(y)。
虽然他们正在写入连续的大块内存,但它们的顺序相反。除非你使用的是一些最新的硬件(即使它使用它我很怀疑),它将需要32次写入来执行操作。
对于第二种情况,您正在对共享内存进行32次反向写入,对共享内存执行32次反向读取,这样做并不昂贵。
现在您已经按相反的顺序读取了数据,您可以按正确的顺序写入全局内存。
线程1写入位置y,线程2写入位置y + 1,依此类推。
最重要的是,您节省了执行32(metod 1)所需的时间 - 3(方法2)= 29次写入全局内存。