CUDA在执行期间组合线程无关(??)变量

时间:2011-04-14 00:50:37

标签: c++ visual-studio-2008 cuda

伙计们,如果标题令人困惑,我道歉。我虽然漫长而艰难,却无法用一种正确的方式将问题排成一行。所以这里有更多细节。我正在做一个基本的图像减法,其中第二个图像已被修改,我需要找到对图像进行了多少更改的比率。为此我使用了以下代码。两张图片均为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;

2 个答案:

答案 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/