CUDA并行前缀和错误

CUDA并行前缀和错误,cuda,floating-point,prefix-sum,Cuda,Floating Point,Prefix Sum,我正试图实现一个三相并行扫描,如《编程大规模并行处理器第三版》第8章所述(有任何代码行,但只有指令)。 该算法只允许使用1个块,块中线程数最多,并且受共享内存大小的限制,因为所有元素都必须适合共享内存 经过一些调试后,我在第3阶段的求和过程中遇到了一个问题(请参阅下面的代码),当时我使用了大量元素,例如8192和多个线程 算法的图形概念如下: 下面您可以看到内核代码: __global__ void efficient_Kogge_Stone_scan_kernel(float *X, fl

我正试图实现一个三相并行扫描,如《编程大规模并行处理器第三版》第8章所述(有任何代码行,但只有指令)。 该算法只允许使用1个块,块中线程数最多,并且受共享内存大小的限制,因为所有元素都必须适合共享内存

经过一些调试后,我在第3阶段的求和过程中遇到了一个问题(请参阅下面的代码),当时我使用了大量元素,例如8192和多个线程

算法的图形概念如下:

下面您可以看到内核代码:

__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];
    }
}
\uuuu全局\uuuu
无效有效\u Kogge\u Stone\u扫描\u内核(浮点*X,浮点*Y,整数输入大小){
__共享浮点数XY[截面尺寸];
__共享浮动AUS[块尺寸];
//int i=blockIdx.x*blockDim.x+threadIdx.x;
//请记住:将输入划分为blockDim.x子部分:即8个线程-->8个子部分
//以联合方式协同加载
对于(int j=0;j=步幅){
in=AUS[threadIdx.x-stride];
}
__同步线程();
如果(threadIdx.x>=步幅){
AUS[threadIdx.x]+=in;
}
}
__同步线程();
//阶段3:每个线程向其元素添加其前一节最后一个元素的新值
如果(threadIdx.x>0){
对于(无符号整数步长=0;步长<(小节大小);步长++){

XY[threadIdx.x*(小节大小)+步长]+=AUS[threadIdx.x-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; 
8192是2^13

总和(1..8192)接近8192^2/2:8192*8193/2,比2^25多一点

因此,您需要26位来表示它(参见下面的注释)

单精度IEEE 754浮点仅具有24位有效位,因此,根据求和的执行方式(顺序)以及最终的取整方向(通常是默认的四舍五入到最近,平齐到偶数),结果可能会有所不同


注意严格来说,精确和可以用浮点表示,而不需要舍入,因为最后12位是零,所以有效位只跨14位。但部分和不是这样。

不要垃圾标签!CUDA不是C!浮点不是关联的。不能保证用浮点对数字列表求和在其他不同的例子中指出,结果将是相同的。首先使用一个使用整数的实现,不必考虑浮点精度问题(看起来您的总总和可以很好地适应32位整数)。此外-您似乎使用了大量的
\u syncthreads()
在那里。最后-为什么只有一个块?任何东西只有一个块都无法获得好的性能。是的@einpoklum你是对的。事实上,我尝试了整数,结果没有问题。在我可以使用分层算法执行前缀和之后,这个算法只是一个基本的理解多个块。现在我使用整数实现了分层算法,我看到从65000个元素开始,内核比主机更快
是必需的,因为共享向量中的多个线程可以在代码的不同部分同时读写。每个线程在自己的子部分中工作,不接触其他线程的子部分,因此问题仍然存在,我尝试了它。