我有一个包含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
?
答案 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)
您的算法是软阈值问题的实现,因为它正在计算:
这个问题已经在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