伙计们,如果标题令人困惑,我道歉。我虽然漫长而艰难,却无法用一种正确的方式将问题排成一行。所以这里有更多细节。我正在做一个基本的图像减法,其中第二个图像已被修改,我需要找到对图像进行了多少更改的比率。为此我使用了以下代码。两张图片均为128x1024。
for(int i = 0; i < 128; i++)
{
for(int j = 0; j < 1024; j++)
{
den++;
diff[i * 1024 + j] = orig[i * 1024 + j] - modified[i * 1024 + j];
if(diff[i * 1024 + j] < error)
{
num++;
}
}
}
ratio = num/den;
以上代码在CPU上工作正常,但我想尝试在CUDA上执行此操作。为此,我可以设置CUDA来完成图像的基本减法(下面的代码),但我无法弄清楚如何使用条件if语句来获得我的比率。
__global__ void calcRatio(float *orig, float *modified, int size, float *result)
{
int index = threadIdx.x + blockIdx.x * blockDim.x;
if(index < size)
result[index] = orig[index] - modified[index];
}
所以,到目前为止它可以工作,但我无法弄清楚如何并行化每个线程中的num和den计数器来计算所有线程执行结束时的比率。对我来说,感觉就像num和den会议员独立于线程一样,每次我尝试使用它们时,它们似乎只会增加一次。
任何帮助都将受到赞赏,因为我刚刚开始使用CUDA,我在网上看到的每个例子似乎都不适用于我需要做的事情。
编辑:修正了我天真的代码。忘了键入代码中的一个主要条件。这是漫长的一天。
for(int i = 0; i < 128; i++)
{
for(int j = 0; j < 1024; j++)
{
if(modified[i * 1024 + j] < 400.0) //400.0 threshold value to ignore noise
{
den++;
diff[i * 1024 + j] = orig[i * 1024 + j] - modified[i * 1024 + j];
if(diff[i * 1024 + j] < error)
{
num++;
}
}
}
}
ratio = num/den;
答案 0 :(得分:4)
在所有线程中执行全局求和所需的操作称为“并行缩减”。虽然你可以使用原子操作来做到这一点,但我不推荐它。有一个简化内核和一篇非常好的论文讨论了CUDA SDK中的技术,值得一读。
如果我正在编写代码来执行您想要的操作,那么它可能看起来像这样:
template <int blocksize>
__global__ void calcRatio(float *orig, float *modified, int size, float *result,
int *count, const float error)
{
__shared__ volatile float buff[blocksize];
int index = threadIdx.x + blockIdx.x * blockDim.x;
int stride = blockDim.x * gridDim.x;
int count = 0;
for(int i=index; i<n; i+=stride) {
val = orig[index] - modified[index];
count += (val < error);
result[index] = val;
}
buff[threadIdx.x] = count;
__syncthreads();
// Parallel reduction in shared memory using 1 warp
if (threadId.x < warpSize) {
for(int i=threadIdx.x + warpSize; i<blocksize; i+= warpSize) {
buff[threadIdx.x] += buff[i];
if (threadIdx.x < 16) buff[threadIdx.x] +=buff[threadIdx.x + 16];
if (threadIdx.x < 8) buff[threadIdx.x] +=buff[threadIdx.x + 8];
if (threadIdx.x < 4) buff[threadIdx.x] +=buff[threadIdx.x + 4];
if (threadIdx.x < 2) buff[threadIdx.x] +=buff[threadIdx.x + 2];
if (threadIdx.x == 0) count[blockIdx.x] = buff[0] + buff[1];
}
}
第一节执行您的序列代码所做的事情 - 计算差异和线程本地总数小于错误的元素。注意我已经编写了这个版本,因此每个线程都设计用于处理输入数据的多个条目。这样做有助于抵消随后的并行缩减的计算成本,并且想法是使用比输入数据集条目更少的块和线程。
第二节是减少本身,在共享内存中完成。它实际上是一个“树状”操作,其中单个线程块中的线程局部小计集的大小首先求和到32个小计,然后小计被组合,直到存在块的最终小计,并且然后存储的是块的总和。您将最终得到一小部分子计数,一个用于您启动的每个块,可以复制回主机,并在那里计算最终结果。
请注意我在浏览器中对此进行了编码并且没有编译它,可能存在错误,但它应该让您了解您尝试做的“高级”版本将如何工作。
答案 1 :(得分:0)
分母非常简单,因为它只是大小。
分子更麻烦,因为它给定线程的值取决于所有先前的值。你将不得不连续进行这项操作。
你正在寻找的东西可能是atomicAdd。但这很慢。
我认为你会发现这个问题很重要。您的num基本上是全局数据。 CUDA array-to-array sum
或者,您可以将错误检查的结果转储到数组中。然后可以对结果进行计数并行化。这会有点棘手,但我认为这样的事情会扩大:http://tekpool.wordpress.com/2006/09/25/bit-count-parallel-counting-mit-hakmem/