CUDA中的软阈值处理

时间:2014-07-12 06:26:10

标签: cuda

我有一个包含1024元素的数组,我有一个阈值t>0来执行以下操作

A[i] = A[i] - t  // if  A[i] > t
A[i] = A[i] + t  // if A[i] < -t
A[i] = 0         // otherwise

所以,我写了一个简单的内核:

void __global__  Kernel_Shrinkage(float* A, float t, int n){
    int i = blockIdx*blockDim.x + threadIdx.x;
    while( i < n)
    { 
        float temp = A[i];
        if (fabs(temp) > t)
        {
            if (temp < 0)
            {
                A[i] += t;
            } 
            else
                A[i] -= t;
        } 
        else
            A[i] = 0;
        id += gridDim.x * blockDim.x;}}

与CPU实现相比,它显示速度提高了6倍。因为这个内核有很多控制语句,所以我怀疑有一种优化这个内核的方法。有什么帮助吗?

如果我有先决条件,数组是稀疏的,这意味着其中的大多数元素都是0

3 个答案:

答案 0 :(得分:1)

这可能会更快:

__global__ void Kernel_Shrinkage(float* A, float t, int n){
    int i = blockIdx.x*blockDim.x + threadIdx.x;
    while( i < n)
    { 
      float temp = A[i];
      if (temp > t)
        temp -= t;
      else if (-temp > t)
        temp += t;
      else 
        temp = 0;
      A[i] = temp;
    i += gridDim.x * blockDim.x;
    }
}

在假设使用如此简单的代码进行扭曲分歧之前,我会检查编译器在预测指令时所做的工作。可能根本没有扭曲分歧。您可以通过直接检查代码(cuobjdump -sass ...)或使用分析器来执行此操作。

代码中的含义是t>=0始终:

    if (fabs(temp) > t)

我的代码做了同样的假设。

答案 1 :(得分:1)

if (temp < 0)
            {
                A[i] += t;
            } 
            else
                A[i] -= t;

等于

A[i]-=-signbitof(temp)*t; // no branching

signbitof()的实现是另一个问题

同样为'fabs(temp)&gt;做同样的事情t'产生类似的东西:

A[i]-=-signbitof(temp)*t*pseudofabsgreaterthan(temp,t); // zero divergence

答案 2 :(得分:1)

您的算法是软阈值问题的实现,因为它正在计算:

enter image description here

这个问题已经在Soft Thresholding CUDA implementation中面临,其中已经设计了两个解决方案。

通过下面的代码,我将比较两个提到的解决方案和Robert Crovella提供的解决方案。

代码(CUDA错误检查被忽略,为了简洁起见,但总是添加它)

#include <thrust\device_vector.h>
#include <time.h>       /* time */

#define BLOCKSIZE 256

/*******************/
/* iDivUp FUNCTION */
/*******************/
int iDivUp(int a, int b) { return ((a % b) != 0) ? (a / b + 1) : (a / b); }

/*****************/
/* TEST KERNEL 1 */
/*****************/
__global__ void myKernel1(float* __restrict__ x, float lambda, const int N)
{
    int tid = threadIdx.x + blockIdx.x * blockDim.x;

    if (tid < N) {
        float xa = fabs(x[tid]); 
        x[tid] = (xa > lambda) ? x[tid] * ((xa - lambda) / xa) : 0;
    }

}    

/*****************/
/* TEST KERNEL 2 */
/*****************/
__global__ void myKernel2(float* __restrict__ x, float lambda, const int N)
{
    int tid = threadIdx.x + blockIdx.x * blockDim.x;

    if (tid < N) {
        float xa = fabs(x[tid]); 
        x[tid] = signbit(lambda-xa)*copysign(xa-lambda,x[tid]);
    }

}

/*****************/
/* TEST KERNEL 3 */
/*****************/
__global__ void myKernel3(float* __restrict__ x, float lambda, const int N)
{
    int tid = threadIdx.x + blockIdx.x * blockDim.x;

    if (tid < N) {
        float temp = x[tid];
        if (temp > lambda)
            temp -= lambda;
        else if (-temp > lambda)
            temp += lambda;
        else 
            temp = 0;
        x[tid] = temp;    }

}

/********/
/* MAIN */
/********/
void main() {

    const int N = 10000000;

    const float lambda = 0.3f;

    thrust::host_vector<float> h_data(N);
    srand (time(NULL));
    for (int i=0; i<N; i++) h_data[i] = rand()/RAND_MAX - 0.5f;

    thrust::device_vector<float> d_data(h_data);

    float time;
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start, 0);

    myKernel1<<<iDivUp(N,BLOCKSIZE),BLOCKSIZE>>>(thrust::raw_pointer_cast(d_data.data()), lambda, N);

    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&time, start, stop);
    printf("Elapsed time kernel 1:  %3.3f ms \n", time);

    d_data = h_data;

    cudaEventRecord(start, 0);

    myKernel2<<<iDivUp(N,BLOCKSIZE),BLOCKSIZE>>>(thrust::raw_pointer_cast(d_data.data()), lambda, N);

    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&time, start, stop);
    printf("Elapsed time kernel 2:  %3.3f ms \n", time);

    d_data = h_data;

    cudaEventRecord(start, 0);

    myKernel3<<<iDivUp(N,BLOCKSIZE),BLOCKSIZE>>>(thrust::raw_pointer_cast(d_data.data()), lambda, N);

    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&time, start, stop);
    printf("Elapsed time kernel 3:  %3.3f ms \n", time);

}

计算结果(使用CUDA 6.0测试)

GT540M

Kernel 1    47.9ms
Kernel 2    41.5ms
Kernel 3    42.4ms

K20C

Kernel 1    0.676ms
Kernel 2    0.591ms
Kernel 3    0.592ms