我有一个存储在GPU全局内存中的双精度数组,我需要在其中找到最大值。我已经阅读了一些关于并行缩减的文本,所以我知道应该在块之间划分数组并使它们找到它们的“全局最大值”,依此类推。 但他们似乎从未解决线程试图同时写入相同内存位置的问题。
假设在块执行开始时local_max = 0.0。然后每个线程从输入向量中读取它们的值,确定它大于local_max,然后尝试将它们的值写入local_max。如果所有这些都在同一时间发生(至少在同一个warp内),那么它如何工作并以此块中的实际最大值结束呢?
我认为需要一个原子功能或某种锁定或关键部分,但我没有在我找到的答案中看到这个问题。 (ex http://developer.download.nvidia.com/compute/cuda/1_1/Website/projects/reduction/doc/reduction.pdf)
答案 0 :(得分:3)
您的问题的答案包含在您链接到的文档中,SDK缩减示例显示了缩减概念的具体实现。
为了完整起见,这是减少内核的具体示例:
template <typename T, int BLOCKSIZE>
__global__ reduction(T *inputvals, T *outputvals, int N)
{
__shared__ volatile T data[BLOCKSIZE];
T maxval = inputvals[threadIdx.x];
for(int i=blockDim.x + threadIdx.x; i<N; i+=blockDim.x)
{
maxfunc(maxval, inputvals[i]);
}
data[threadIdx.x] = maxval;
__syncthreads();
// Here maxfunc(a,b) sets a to the minimum of a and b
if (threadIdx.x < 32) {
for(int i=32+threadIdx.x; i < BLOCKSIZE; i+= 32) {
maxfunc(data[threadIdx.x], data[i]);
}
if (threadIdx.x < 16) maxfunc(data[threadIdx.x], data[threadIdx.x+16]);
if (threadIdx.x < 8) maxfunc(data[threadIdx.x], data[threadIdx.x+8]);
if (threadIdx.x < 4) maxfunc(data[threadIdx.x], data[threadIdx.x+4]);
if (threadIdx.x < 2) maxfunc(data[threadIdx.x], data[threadIdx.x+2]);
if (threadIdx.x == 0) {
maxfunc(data[0], data[1]);
outputvals[blockIdx.x] = data[0];
}
}
}
关键是使用warp中隐式的同步来执行共享内存的减少。结果是每个块的最大值。需要第二次减少传递以将块最大值的集合减少到全局最大值(通常在主机上更快)。在此示例中,maxvals
是“比较和设置”功能,可以像
template<T>
__device__ void maxfunc(T & a, T & b)
{
a = (b > a) ? b : a;
}
答案 1 :(得分:2)
不要'自己编写代码,使用一些thrust(包含在Cuda sdk的4.0版本中):
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <thrust/copy.h>
#include <iostream>
int main(void)
{
thrust::host_vector<int> h_vec(10000);
thrust::sequence(h_vec.begin(), h_vec.end());
// show hvec
thrust::copy(h_vec.begin(), h_vec.end(),
std::ostream_iterator<int>(std::cout, "\n"));
// transfer to device
thrust::device_vector<int> d_vec = h_vec;
int max_dvec_value = *thrust::max_element(d_vec.begin(), d_vec.end());
std::cout << "max value: " << max_dvec_value << "\n";
return 0;
}
注意thrust :: max_element返回一个指针。
答案 2 :(得分:1)
您链接到的文档中已清楚地回答了您的问题。我认为你只需花一些时间阅读它并理解其中使用的CUDA概念。特别是,我将专注于共享内存,__syncthreads()方法,以及如何在内核中唯一地标识线程。此外,您应该尝试理解为什么减少可能需要在2次传递中运行才能找到全局最大值。