有人可以帮助理解Hillis& Steele:内核函数适用于每个线程执行?
__global__ void scan(float *g_odata, float *g_idata, int n)
{
extern __shared__ float temp[]; // allocated on invocation
int thid = threadIdx.x;
int pout = 0, pin = 1;
// load input into shared memory.
// This is exclusive scan, so shift right by one and set first elt to 0
temp[pout*n + thid] = (thid > 0) ? g_idata[thid-1] : 0;
__syncthreads();
for (int offset = 1; offset < n; offset *= 2)
{
pout = 1 - pout; // swap double buffer indices
pin = 1 - pout;
if (thid >= offset)
temp[pout*n+thid] += temp[pin*n+thid - offset];
else
temp[pout*n+thid] = temp[pin*n+thid];
__syncthreads();
}
g_odata[thid] = temp[pout*n+thid1]; // write output
}
从现在起我了解以下内容:首先,我们有pout=0, pin=1 and thid = [1,bockDim.x]
。
因此,在第一次同步之前,我们有一个简单的向右移位,例如,如果我们有数组[1 | 2 | 5 | 7 ]
,则新数组为[0 |1 | 2 | 5 | 7 ]
。
我认为for loop
的执行是多个实例,每个thId
的每个实例。例如,如果thId=0
我们将执行以下操作:
thid=0
offset=1
[0 | 1 | 2 | 5 | 0]
offset=2
pout和pin变量根据for循环内部的信息而不是
更改
考虑在开始时初始化这些变量。用同样的方式
我想象thid=1
的执行。
thid=1
offset=1
任何人都可以直观地举例说明它是如何执行的吗?另外,当要执行最后一个代码语句时,将使用哪个pout值?
答案 0 :(得分:2)
如果您的意思是并行扫描算法,可以在此处看到直观的解释。
http://en.wikipedia.org/wiki/Prefix_sum
我相信这些链接也有帮助。
http://nvlabs.github.io/cub/structcub_1_1_device_scan.html
答案 1 :(得分:-1)
好的,这已经有5年了。但是,我想回答这个问题,因为我只是花了一些时间自己弄清楚了。也许这仍然可以帮助某人...
关于“临时内存超出范围”:您需要分配的共享内存是输入数组g_idata
的两倍。
我认为,代码需要稍作更改才能起作用。我附上了一个有效的例子。使用nvcc -std=c++11
进行编译。
https://github.com/fg91/learning_cuda/blob/master/notes/lesson_3/scanHillisSteele.cu
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdio.h>
#include <iostream>
__global__ void scanHillisSteele(int *d_out, int *d_in, int n) {
int idx = threadIdx.x;
extern __shared__ int temp[];
int pout = 0, pin = 1;
temp[idx] = (idx > 0) ? d_in[idx - 1] : 0;
__syncthreads();
for (int offset = 1; offset < n; offset *= 2) {
// swap double buffer indices
pout = 1 - pout;
pin = 1 - pout;
if (idx >= offset) {
temp[pout*n+idx] = temp[pin*n+idx - offset] + temp[pin*n+idx]; // changed line
} else {
temp[pout*n+idx] = temp[pin*n+idx];
}
__syncthreads();
}
d_out[idx] = temp[pout*n+idx];
}
int main() {
const int ARRAY_SIZE = 10;
const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);
// generate the input array on the host
int h_in[ARRAY_SIZE]{1, 2, 5, 7, 8, 10, 11, 12, 15, 19};
int h_out[ARRAY_SIZE];
// declare GPU memory pointers
int * d_in;
int * d_out;
// allocate GPU memory
cudaMalloc((void **) &d_in, ARRAY_BYTES);
cudaMalloc((void **) &d_out, ARRAY_BYTES);
// transfer the array to the GPU
cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice);
// launch the kernel
scanHillisSteele<<<1, ARRAY_SIZE, 2 * ARRAY_BYTES>>>(d_out, d_in, ARRAY_SIZE);
cudaDeviceSynchronize();
// transfer the resulting array to the cpu
cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);
// print out the input and resulting array
std::cout << "Input:" << std::endl;
for (int i = 0; i < ARRAY_SIZE; ++i) {
std::cout << h_in[i] << " " << std::flush;
}
std::cout << std::endl << "Exclusive scan with operation +:" << std::endl;
for (int i = 0; i < ARRAY_SIZE; ++i) {
std::cout << h_out[i] << " " << std::flush;
}
std::cout << std::endl;
// free GPU memory allocation
cudaFree(d_in);
cudaFree(d_out);
return 0;
}