我有两个减少算法,都来自docs.nvidia,所以它们应该是正确的,但第一个(非常有效)会给我一个错误的结果。第二个结果更好,但我期望更好的准确性。算法中是否有任何错误,或者我做错了什么?
#include <stdio.h>
#include <cuda.h>
#include <stdlib.h>
#include <math.h>
#include "cuda_error.h"
//Lock definition
#ifndef __LOCK_H__
#define __LOCK_H__
struct Lock {
int *mutex;
Lock( void ) {
CudaSafeCall( cudaMalloc( (void**)&mutex,
sizeof(int) ) );
CudaSafeCall( cudaMemset( mutex, 0, sizeof(int) ) );
}
~Lock( void ) {
cudaFree( mutex );
}
__device__ void lock( void ) {
while( atomicCAS( mutex, 0, 1 ) != 0 );
}
__device__ void unlock( void ) {
atomicExch( mutex, 0 );
}
};
#endif
//-------------------------
const int N = 507904;
const int blockSize = 256;
int blocks = N/blockSize;
template <unsigned int blockSize>
__global__ void reduce(Lock lock, float *g_idata, float *g_odata, int n)
{
extern __shared__ int sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x*(blockSize*2) + tid;
unsigned int gridSize = blockSize*2*gridDim.x;
sdata[tid] = 0;
while (i < n)
{
sdata[tid] += g_idata[i] + g_idata[i+blockSize];
i += gridSize;
}
__syncthreads();
if (blockSize >= 512) { if (tid < 256) { sdata[tid] += sdata[tid + 256]; } __syncthreads(); }
if (blockSize >= 256) { if (tid < 128) { sdata[tid] += sdata[tid + 128]; } __syncthreads(); }
if (blockSize >= 128) { if (tid < 64) { sdata[tid] += sdata[tid + 64]; } __syncthreads(); }
if (tid < 32)
{
if (blockSize >= 64) sdata[tid] += sdata[tid + 32];
if (blockSize >= 32) sdata[tid] += sdata[tid + 16];
if (blockSize >= 16) sdata[tid] += sdata[tid + 8];
if (blockSize >= 8) sdata[tid] += sdata[tid + 4];
if (blockSize >= 4) sdata[tid] += sdata[tid + 2];
if (blockSize >= 2) sdata[tid] += sdata[tid + 1];
}
if (tid == 0)
{
lock.lock();
*g_odata += sdata[0];
lock.unlock();
}
}
__global__ void reduction_sum(Lock lock,float *in, float *out, int N)
{
extern __shared__ float sf_data[];
int tid = threadIdx.x;
int i = blockIdx.x * blockDim.x + threadIdx.x;
sf_data[tid] = (i<N) ? in[i] : 0;
__syncthreads();
for (int s = blockDim.x/2; s>0; s>>=1)
{
if (tid < s)
{
sf_data[tid] += sf_data[tid + s];
}
__syncthreads();
}
if (tid == 0)
{
lock.lock();
*out += sf_data[0];
lock.unlock();
}
}
//initializing function
double los()
{
return (double)rand()/(double)RAND_MAX;
}
//-------------------------------------------
int main (void)
{
//memory allocation
float *a;
float *dev_a, *dev_blocksum1, *dev_blocksum2;
float s1=0, s2=0, spr=0;
a = (float*)malloc( N*sizeof(float) );
CudaSafeCall( cudaMalloc( (void**)&dev_a, N*sizeof(float) ) );
CudaSafeCall( cudaMemset( dev_a, 0, N*sizeof(float) ) );
CudaSafeCall( cudaMalloc( (void**)&dev_blocksum1, sizeof(float) ) );
CudaSafeCall( cudaMalloc( (void**)&dev_blocksum2, sizeof(float) ) );
CudaSafeCall( cudaMemset( dev_blocksum1, 0, sizeof(float) ) );
CudaSafeCall( cudaMemset( dev_blocksum2, 0, sizeof(float) ) );
//--------------------
//drawing, array fill
srand(2403);
int i;
for (i=0; i<N; i++)
{
a[i]=los();
spr+=a[i];
}
printf("CPU sum: %f \n", spr);
//copy HtoD
CudaSafeCall( cudaMemcpy( dev_a, a, N*sizeof(float), cudaMemcpyHostToDevice ) );
//---------------------
Lock lock;
//reduce
reduce<blockSize><<<blocks, blockSize, blockSize*sizeof(float)>>>(lock, dev_a, dev_blocksum1, N);
CudaSafeCall( cudaMemcpy ( &s1, dev_blocksum1, sizeof(float), cudaMemcpyDeviceToHost ) );
printf("GPU sum1: %f \n", s1);
//-----------------------
//reduction_sum
reduction_sum<<<blocks, blockSize, blockSize*sizeof(float)>>>(lock, dev_a, dev_blocksum2, N);
CudaSafeCall( cudaMemcpy ( &s2, dev_blocksum2, sizeof(float), cudaMemcpyDeviceToHost ) );
printf("GPU sum2: %f \n", s2);
//---------------------
return 0;
}
$ CPU sum: 253833.515625
$ GPU sum1: 14021.000000
$ GPU sum2: 253835.906250
答案 0 :(得分:3)
有很多事情需要提及。
我不确定您的错误检查是否有效。您还没有显示实现此功能的文件,当我使用cuda-memcheck
运行您的代码时,我会收到报告的各种错误。它们似乎都与锁定功能有关。
我不确定你为什么要使用锁定功能,我不推荐它。根据您的使用方式,我认为它不会超出您的想象范围。我建议改用atomicAdd
,这应该更快更简单。至少,在析构函数中注释掉cudaFree()
语句。
您正在链接到旧的演示文稿。如果您查看newer version of it,我认为您会看到它现在建议使用volatile
。我不打算为您重写整个代码,也不会再次总结该白皮书,但如果您只是为了演示目的而将volatile
添加到共享内存声明中,它将解决由此产生的问题。
您的共享内存变量声明为int
,但您要汇总float
个数量。那不会按照你想要的方式工作。你可以这样声明:
extern __shared__ volatile float sdata[];
以上更改获得了代码&#34; working&#34;为了我。剩下的项目是CPU和GPU结果之间的差异。我相信这是由于CPU(串行缩减)与GPU(并行缩减)的操作浮点顺序不同。由于差异出现在float
数量的第6位有效数字上,我认为这完全在浮点结果比较的合理范围内。如果您希望获得更高的准确度,可以尝试从float
切换到double
。此外,您可能希望阅读各种浮点文件,以帮助理解此处,例如this one和this one。