我试图实现三相并行扫描,如编程大规模并行处理器第3版的第8章所述(有任何代码行但只有指令)。 此算法仅允许使用块中具有最大线程数的1个块,并且它受共享内存大小的限制,因为所有元素必须适合共享内存
经过一些调试后,我在第3阶段的总和中遇到了一个问题(参见下面的代码),当我使用大量元素时,例如8192,以及超过1个线程。
下面你可以看到内核代码:
__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而不是float数字,它甚至可以使用带有一个或多个线程的8192个元素。
我也尝试在MATLAB中进行验证,这些是输出比较:
正如我们所看到的,CPU结果也不同于MATLAB,所以在这些结果之后我认为问题是关于浮点加法,但我告诉你我用有序的&#34; x填充了输入数组。 0.00&#34;浮点数(例如{1.00,2.00,3.00,4.00 ..... 8192.00})。
另一个问题是关于性能,主机代码总是比内核代码快,有了这些配置参数和这些输入,这是正常的吗?
如果您需要完整的源代码,可以找到它here
答案 0 :(得分:2)
8192是2 ^ 13
总和(1..8192)接近8192 ^ 2/2:8192 * 8193/2,即略高于2 ^ 25。因此,您需要26位来表示它(参见下面的注释)。
单精度IEEE 754浮点数只有24位有效数,因此,取决于求和的执行方式(以哪种顺序),最终取决于舍入方向(通常是默认舍入到最近,连接到偶数),然后结果可能会有所不同。
注意严格地说,精确的和可以用浮点表示而不用舍入,因为最后12位是零,所以有效位只跨越14位。但是部分金额并非如此。
答案 1 :(得分:1)
第一次扫描可能存在问题:
XY[threadIdx.x * (SUBSECTION_SIZE)+j] += XY[threadIdx.x * (SUBSECTION_SIZE)+j - 1];
这可能导致共享内存中元素的不一致读取。当您读取前一个元素时,不能保证任何其他线程都没有更新该值。
尝试通过将值存储在寄存器中来将此部分分成几部分。例如:
int t = XY[threadIdx.x * (SUBSECTION_SIZE)+j - 1];
__syncthreads();
XY[threadIdx.x * (SUBSECTION_SIZE)+j] += t;