使用CUDA的共享内存互斥-添加到项目列表

使用CUDA的共享内存互斥-添加到项目列表,cuda,mutex,Cuda,Mutex,我的问题是:我有一个图像,其中我检测到一些兴趣点使用GPU。就处理而言,检测是一项重量级测试,但平均而言,25分中只有1分通过测试。算法的最后一步是建立一个点列表。在CPU上,这将实现为: forall pixels x,y { if(test_this_pixel(x,y)) vector_of_coordinates.push_back(Vec2(x,y)); } 在GPU上,我让每个CUDA块处理16x16像素。问题是我需要做一些特殊的事情,最终在全局内存中有一个

我的问题是:我有一个图像,其中我检测到一些兴趣点使用GPU。就处理而言,检测是一项重量级测试,但平均而言,25分中只有1分通过测试。算法的最后一步是建立一个点列表。在CPU上,这将实现为:

forall pixels x,y
{
    if(test_this_pixel(x,y))
        vector_of_coordinates.push_back(Vec2(x,y));
}
在GPU上,我让每个CUDA块处理16x16像素。问题是我需要做一些特殊的事情,最终在全局内存中有一个统一的点列表。目前,我正试图在每个块的共享内存中生成一个点的本地列表,这些点最终将写入全局内存。我试图避免将任何东西发送回CPU,因为在这之后有更多的CUDA阶段

我希望我可以使用原子操作在共享内存上实现push_-back功能。然而,我无法让这个工作。有两个问题。第一个恼人的问题是,我经常遇到以下编译器崩溃:“nvcc错误:'ptxas'在使用原子操作时死亡,状态为0xC0000005(访问\u冲突)”。我是否能编译一些东西是碰运气的。有人知道这是什么原因吗

以下内核将重现该错误:

__global__ void gpu_kernel(int w, int h, RtmPoint *pPoints, int *pCounts)
{
    __shared__ unsigned int test;
    atomicInc(&test, 1000);
}
其次,我的代码在共享内存上包含一个互斥锁,挂起了GPU,我不明白为什么:

__device__ void lock(unsigned int *pmutex)
{
    while(atomicCAS(pmutex, 0, 1) != 0);
}

__device__ void unlock(unsigned int *pmutex)
{
    atomicExch(pmutex, 0);
}

__global__ void gpu_kernel_non_max_suppress(int w, int h, RtmPoint *pPoints, int *pCounts)
{
    __shared__ RtmPoint localPoints[64];
    __shared__ int localCount;
    __shared__ unsigned int mutex;

    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    int threadid = threadIdx.y * blockDim.x + threadIdx.x;
    int blockid = blockIdx.y * gridDim.x + blockIdx.x;

    if(threadid==0)
    {
        localCount = 0;
        mutex = 0;
    }

    __syncthreads();

    if(x<w && y<h)
    {
        if(some_test_on_pixel(x,y))
        {
            RtmPoint point;
            point.x = x;
            point.y = y;

            // this is a local push_back operation
            lock(&mutex);
            if(localCount<64) // we should never get >64 points per block
                localPoints[localCount++] = point;
            unlock(&mutex);
        }
    }

    __syncthreads();

    if(threadid==0)
        pCounts[blockid] = localCount;
    if(threadid<localCount)
        pPoints[blockid * 64 + threadid] = localPoints[threadid];
}
\uuuuu设备\uuuuuu无效锁(无符号int*pmutex)
{
而(原子团(pmutex,0,1)!=0);
}
__设备无效解锁(未签名int*pmutex)
{
原子exch(pmutex,0);
}
__全局无效gpu内核非最大值抑制(整数w、整数h、RtmPoint*P点、整数*P点)
{
__共享的_uurtmpoint本地点[64];
__共享\ int localCount;
__共享的无符号整数互斥;
intx=blockIdx.x*blockDim.x+threadIdx.x;
int y=blockIdx.y*blockDim.y+threadIdx.y;
int-threadid=threadIdx.y*blockDim.x+threadIdx.x;
int blockid=blockIdx.y*gridDim.x+blockIdx.x;
if(threadid==0)
{
localCount=0;
互斥量=0;
}
__同步线程();

如果(x,我建议使用前缀和来实现该部分以提高并行性。为此,您需要使用共享数组。基本上,前缀和将数组(1,1,0,1)转换为(0,1,2,2,3),即,将计算一个就地运行的排他和,以便获得每个线程的写入索引

__shared__ uint8_t vector[NUMTHREADS];

....

bool emit  = (x<w && y<h);
     emit  = emit && some_test_on_pixel(x,y);
__syncthreads();
scan(emit, vector);
if (emit) {
     pPoints[blockid * 64 + vector[TID]] = point;
}
\uuuuuuuuuuuuuuuuuuuuuuuuuuu8\ut向量[NUMTHREADS];
....

布尔发射=(x根据这里的建议,我将最后使用的代码包括在内。它使用16x16像素块。请注意,我现在将数据写入一个全局数组中,而不将其拆分。我使用全局atomicAdd函数为每组结果计算基址。因为每个块只调用一次,所以我没有发现太多的错误一个慢下来,而我通过这样做获得了更多的便利。我还避免了前缀_sum的输入和输出的共享缓冲区。GlobalCount在内核调用之前设置为零

#define BLOCK_THREADS 256

__device__ int prefixsum(int threadid, int data)
{
    __shared__ int temp[BLOCK_THREADS*2];

    int pout = 0;
    int pin = 1;

    if(threadid==BLOCK_THREADS-1)
        temp[0] = 0;
    else
        temp[threadid+1] = data;

    __syncthreads();

    for(int offset = 1; offset<BLOCK_THREADS; offset<<=1)
    {
        pout = 1 - pout;
        pin = 1 - pin;

        if(threadid >= offset)
            temp[pout * BLOCK_THREADS + threadid] = temp[pin * BLOCK_THREADS + threadid] + temp[pin * BLOCK_THREADS + threadid - offset];
        else
            temp[pout * BLOCK_THREADS + threadid] = temp[pin * BLOCK_THREADS + threadid];

        __syncthreads();
    }

    return temp[pout * BLOCK_THREADS + threadid];
}

__global__ void gpu_kernel(int w, int h, RtmPoint *pPoints, int *pGlobalCount)
{
    __shared__ int write_base;

    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    int threadid = threadIdx.y * blockDim.x + threadIdx.x;
    int valid = 0;

    if(x<w && y<h)
    {
        if(test_pixel(x,y))
        {
            valid = 1;
        }
    }

    int index = prefixsum(threadid, valid);

    if(threadid==BLOCK_THREADS-1)
    {
        int total = index + valid;
        if(total>64)
            total = 64; // global output buffer is limited to 64 points per block
        write_base = atomicAdd(pGlobalCount, total); // get a location to write them out
    }

    __syncthreads(); // ensure write_base is valid for all threads

    if(valid)
    {
        RtmPoint point;
        point.x = x;
        point.y = y;
        if(index<64)
            pPoints[write_base + index] = point;
    }
}
#定义块线程256
__设备\uuuint前缀sum(int-threadid,int-data)
{
__共享线程温度[块线程*2];
int-pout=0;
int引脚=1;
if(threadid==BLOCK_THREADS-1)
温度[0]=0;
其他的
temp[threadid+1]=数据;
__同步线程();

对于(int offset=1;offset)这很有趣。谢谢。我刚刚尝试实现了这一点,我发现扫描函数在以下行中不正确:“temp[poutn+thid]+=temp[pinn+thid-offset];”。实际上应该是“temp[poutn+thid]=temp[pinn+thid]+temp[pin*n+thid-offset];”好的,我基本上实现了您所拥有的,稍后我将发布最终代码。非常感谢。您可以在库的源代码中找到更高效的扫描代码。顺便说一句,使用共享原子来实现它(它很慢,所以您不应该)如果你的atomicInc是导致PTXAS崩溃的,那是个bug,我们想知道它的问题,请把这个问题发布到英伟达GPU计算论坛上。使用推力::转换迭代器sing-推力::copy_if.@harrism,你能写一个伪代码来演示如何在这个例子中使用CUDPP吗?使用atomicAdd来协调结果的写入的唯一问题是,结果以随机顺序结束,在不同的运行中会发生变化。不过这没什么大不了的,再加上它很容易排序结果放向量。
#define BLOCK_THREADS 256

__device__ int prefixsum(int threadid, int data)
{
    __shared__ int temp[BLOCK_THREADS*2];

    int pout = 0;
    int pin = 1;

    if(threadid==BLOCK_THREADS-1)
        temp[0] = 0;
    else
        temp[threadid+1] = data;

    __syncthreads();

    for(int offset = 1; offset<BLOCK_THREADS; offset<<=1)
    {
        pout = 1 - pout;
        pin = 1 - pin;

        if(threadid >= offset)
            temp[pout * BLOCK_THREADS + threadid] = temp[pin * BLOCK_THREADS + threadid] + temp[pin * BLOCK_THREADS + threadid - offset];
        else
            temp[pout * BLOCK_THREADS + threadid] = temp[pin * BLOCK_THREADS + threadid];

        __syncthreads();
    }

    return temp[pout * BLOCK_THREADS + threadid];
}

__global__ void gpu_kernel(int w, int h, RtmPoint *pPoints, int *pGlobalCount)
{
    __shared__ int write_base;

    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    int threadid = threadIdx.y * blockDim.x + threadIdx.x;
    int valid = 0;

    if(x<w && y<h)
    {
        if(test_pixel(x,y))
        {
            valid = 1;
        }
    }

    int index = prefixsum(threadid, valid);

    if(threadid==BLOCK_THREADS-1)
    {
        int total = index + valid;
        if(total>64)
            total = 64; // global output buffer is limited to 64 points per block
        write_base = atomicAdd(pGlobalCount, total); // get a location to write them out
    }

    __syncthreads(); // ensure write_base is valid for all threads

    if(valid)
    {
        RtmPoint point;
        point.x = x;
        point.y = y;
        if(index<64)
            pPoints[write_base + index] = point;
    }
}