【发布时间】:2016-01-29 16:38:06
【问题描述】:
我正在尝试为大型一维数组实现自己的归约和。我想出了一个减少内核和一种方法来多次调用内核以逐步减少以达到单个值。现在我知道这不是计算它的最佳方法(如果您看到我的代码,它可能会达到需要进行内核调用以添加 3 个值的地步),但是让我们暂时避免这种情况并尝试工作有了这个。
基本上,简而言之:我调用reduce内核每次reduceMAXTHREADS次,在这个例子中是1024。所以数组的大小每次都会减少1024。当大小小于 1024 时,它似乎可以正常工作,但对于更大的值,它很遗憾地无法得到正确的总和。
我尝试过的所有数组大小都会发生这种情况。我错过了什么?
我也很乐意接受有关代码质量的 cmets,但主要对修复它感兴趣。
下面我发布了整个内核和内核调用的 sn-p。如果我错过了内核调用的某些相关部分,请发表评论,我会修复它。
原始代码有错误检查,所有内核始终运行并且永远不会返回CUDAErrors。
减少内核
__global__ void reduce6(float *g_idata, float *g_odata, unsigned int n){
extern __shared__ float sdata[];
// perform first level of reduction,
// reading from global memory, writing to shared memory
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x*(MAXTREADS*2) + tid;
unsigned int gridSize = MAXTREADS*2*gridDim.x;
//blockSize==MAXTHREADS
sdata[tid] = 0;
float mySum = 0;
if (tid>=n) return;
if ( (gridDim.x>0) & ((i+MAXTREADS)<n)){
while (i < n) {
sdata[tid] += g_idata[i] + g_idata[i+MAXTREADS];
i += gridSize;
}
}else{
sdata[tid] += g_idata[i];
}
__syncthreads();
// do reduction in shared mem
if (tid < 512)
sdata[tid] += sdata[tid + 512];
__syncthreads();
if (tid < 256)
sdata[tid] += sdata[tid + 256];
__syncthreads();
if (tid < 128)
sdata[tid] += sdata[tid + 128];
__syncthreads();
if (tid < 64)
sdata[tid] += sdata[tid + 64];
__syncthreads();
#if (__CUDA_ARCH__ >= 300 )
if ( tid < 32 )
{
// Fetch final intermediate sum from 2nd warp
mySum = sdata[tid]+ sdata[tid + 32];
// Reduce final warp using shuffle
for (int offset = warpSize/2; offset > 0; offset /= 2)
mySum += __shfl_down(mySum, offset);
}
sdata[0]=mySum;
#else
// fully unroll reduction within a single warp
if (tid < 32) {
warpReduce(sdata,tid);
}
#endif
// write result for this block to global mem
if (tid == 0) g_odata[blockIdx.x] = sdata[0];
}
__device__ void warpReduce(volatile float *sdata,unsigned int tid) {
sdata[tid] += sdata[tid + 32];
sdata[tid] += sdata[tid + 16];
sdata[tid] += sdata[tid + 8];
sdata[tid] += sdata[tid + 4];
sdata[tid] += sdata[tid + 2];
sdata[tid] += sdata[tid + 1];
}
为任意大小的total_pixels调用内核:
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#defineMAXTREADS 1024
__global__ void initKernel(float * devPtr, const float val, const size_t nwords)
{
//https://stackoverflow.com/questions/10589925/initialize-device-array-in-cuda
int tidx = threadIdx.x + blockDim.x * blockIdx.x;
int stride = blockDim.x * gridDim.x;
for (; tidx < nwords; tidx += stride)
devPtr[tidx] = val;
}
int main()
{
size_t total_pixels = 5000;
unsigned long int n = (unsigned long int)total_pixels;
float* d_image, *d_aux;
float sum;
cudaMalloc(&d_image, total_pixels*sizeof(float));
cudaMalloc(&d_aux, sizeof(float)*(n + MAXTREADS - 1) / MAXTREADS);
for (int i = 0; i < 10; i++){
sum = 0;
cudaMemset(&d_image, 1, total_pixels*sizeof(float));
int dimblockRed = MAXTREADS;
int dimgridRed = (total_pixels + MAXTREADS - 1) / MAXTREADS;
int reduceCont = 0;
initKernel << < dimgridRed, dimblockRed >> >(d_image, 1.0, total_pixels);
while (dimgridRed > 1) {
if (reduceCont % 2 == 0){
reduce6 << <dimgridRed, dimblockRed, MAXTREADS*sizeof(float) >> >(d_image, d_aux, n);
}
else{
reduce6 << <dimgridRed, dimblockRed, MAXTREADS*sizeof(float) >> >(d_aux, d_image, n);
}
n = dimgridRed;
dimgridRed = (n + MAXTREADS - 1) / MAXTREADS;
reduceCont++;
}
if (reduceCont % 2 == 0){
reduce6 << <1, dimblockRed, MAXTREADS*sizeof(float) >> >(d_image, d_aux, n);
cudaMemcpy(&sum, d_aux, sizeof(float), cudaMemcpyDeviceToHost);
}
else{
reduce6 << <1, dimblockRed, MAXTREADS*sizeof(float) >> >(d_aux, d_image, n);
cudaMemcpy(&sum, d_image, sizeof(float), cudaMemcpyDeviceToHost);
}
printf("%f ", sum);
}
cudaDeviceReset();
return 0;
}
【问题讨论】:
-
这不是MCVE。如果我无法剪切、粘贴和编译您发布的内容并重现您的错误,我无法帮助您。就是这么简单。
-
@talonmies 道歉,你说得对。 letmefixthat
-
@talonmies 为延迟道歉。添加了 MCVE。它现在的行为不同(仍然输出一个值流,有时只有 1 不同,但现在非 eof 值是真正的答案)与从 MATLAB 调用时(这是我正在做的)但我假设错误来自同一个地方:错误的内存访问。
-
你从来没有真正编译和运行过,是吗? (提示:memset 设置字节而不是单词)
-
@talonmies 我确实编译了它。我刚刚解决了这个问题,请参阅编辑。