【发布时间】:2017-09-25 09:55:40
【问题描述】:
我正在尝试实现三相并行扫描,如 Programming Massively Parallel Processors 第 3 版的第 8 章所述(有任何代码行,但只有指令)。 该算法只允许使用 1 个块,该块中的线程数最多,并且受限于共享内存的大小,因为所有元素都必须适合共享内存
经过一些调试,当我使用大量元素(例如 8192 和 1 个以上的线程)时,在第 3 阶段求和期间遇到了一个问题。
下面你可以看到内核代码:
__global__
void efficient_Kogge_Stone_scan_kernel(float *X, float *Y, int InputSize) {
__shared__ float XY[SECTION_SIZE];
__shared__ float AUS[BLOCK_DIM];
//int i = blockIdx.x * blockDim.x + threadIdx.x;
// Keep mind: Partition the input into blockDim.x subsections: i.e. for 8 threads --> 8 subsections
// collaborative load in a coalesced manner
for (int j = 0; j < SECTION_SIZE; j += blockDim.x) {
XY[threadIdx.x + j] = X[threadIdx.x + j];
}
__syncthreads();
// PHASE 1: scan inner own subsection
// At the end of this phase the last element of each subsection contains the sum of all alements in own subsection
for (int j = 1; j < SUBSECTION_SIZE; j++) {
XY[threadIdx.x * (SUBSECTION_SIZE)+j] += XY[threadIdx.x * (SUBSECTION_SIZE)+j - 1];
}
__syncthreads();
// PHASE 2: perform iterative kogge_stone_scan of the last elements of each subsections of XY loaded first in AUS
AUS[threadIdx.x] = XY[threadIdx.x * (SUBSECTION_SIZE)+(SUBSECTION_SIZE)-1];
float in;
for (unsigned int stride = 1; stride < blockDim.x; stride *= 2) {
__syncthreads();
if (threadIdx.x >= stride) {
in = AUS[threadIdx.x - stride];
}
__syncthreads();
if (threadIdx.x >= stride) {
AUS[threadIdx.x] += in;
}
}
__syncthreads();
// PHASE 3: each thread adds to its elements the new value of the last element of its predecessor's section
if (threadIdx.x > 0) {
for (unsigned int stride = 0; stride < (SUBSECTION_SIZE); stride++) {
XY[threadIdx.x * (SUBSECTION_SIZE)+stride] += AUS[threadIdx.x - 1]; // <--
}
}
__syncthreads();
// store the result into output vector
for (int j = 0; j < SECTION_SIZE; j += blockDim.x) {
Y[threadIdx.x + j] = XY[threadIdx.x + j];
}
}
如果我在块中使用一个线程和 8192 个元素,它可以完美运行,但如果我使用多个线程,则 XY[5793](或 X[5793] 完成并存储结果时)的结果是错误的. 它有 4096 个元素和一个或多个线程,最多 1024 个线程。 如果我使用 int 而不是浮点数,它甚至适用于具有一个或多个线程的 8192 个元素。
我也尝试在 MATLAB 中进行验证,这些是输出比较:
- X[5973] = 16788115 ---- MATLAB
- X[5973] = 16788114 ---- CPU
- X[5973] = 16788116 ---- GPU
正如我们所看到的,CPU 结果也与 MATLAB 不同,所以在这些结果之后,我认为问题出在浮点加法上,但我告诉你,我用有序的“x.00”填充了输入数组浮点数(例如 {1.00, 2.00, 3.00, 4.00 ..... 8192.00})。
另一个问题是关于性能的,宿主代码总是比内核代码快,有这些配置参数和这些输入,正常吗?
如果你需要完整的源代码,你可以找到它here
【问题讨论】:
-
不要垃圾标签! CUDA 不是 C!
-
浮点不是关联的。不能保证您将不同其他浮点数的数字列表相加,结果将是相同的
-
首先考虑使用使用整数的实现,而不必考虑浮点精度问题(看起来您的总和可以很好地适合 32 位整数)。另外 - 你似乎在那里使用了很多
__syncthreads()。最后 - 为什么只有 1 个街区?你永远不可能只用一个块来获得好的性能。 -
是的,你是对的@einpoklum 实际上,我尝试使用整数,结果没有问题。在分层算法如何在我可以使用多个块的情况下执行前缀和之后,该算法只是一个要理解的原语。现在我使用整数实现了分层算法,我看到从 65000 个元素开始,内核变得比主机更快。
__syncthreads()是必需的,因为在共享向量中,多个线程可以同时在代码的不同部分读取和写入。
标签: cuda floating-point prefix-sum