CUDA卷积加速(一)
推出这个系列的目的呢,主要是因为CUDA学习的一个主要用途就是对图像处理进行加速,而处理图像的过程中经常要用到卷积。卷积的计算有多种形式,本系列主要研究的是二维矩阵的卷积计算。从最原始的计算方法(就是本科教科书上的那种)再到优化后的适用于大型数据的算法,均提供并行化的设计思路。
考虑到机器学习很火,很多人也用python来做图形处理。但不好的地方在于python目前还不支持GPGPU编程,这就很尴尬。所以说编程语言掌握全面一点总归是好的嘛!
下面是普通版的CUDA进行卷积的操作方法,是对卷积最初始的算法进行的GPU平台的移植。我们称之为版本二(版本一就是原始版的嘛)。算法本身不难,但他考验了一些CUDA的基本功,对以后的深入学习有着铺垫作用。
那么利用计算机计算卷积最原始的版本呢,莫过于离散乘积求和,即对f[n-m]g[m]求和。那么整个卷积就只需要几个循环便可以做出来了。
那么这里我们也提供对上述过程最简单的CUDA程序给大家看:
__global__ void conv_kernel(const float *ina, const float *inb, float *out, size_t len_a, size_t len_b, size_t len_out) { const int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid >= len_out) { return; } float sum = 0.0f; for (int m = 0; m < len_b; ++m) { int k = tid - m; if (0 <= k && k < len_a) { sum += ina[k] * inb[m]; } } out[tid] = sum; }
<pre class="cpp" name="code">__global__ void conv2_kernel(const float *ina, const float *inb, float *out, size_t len_a, size_t len_b, size_t len_out) { const int tid = blockIdx.x * blockDim.x + threadIdx.x; const unsigned int tx = threadIdx.x; //线程数 const size_t len_s = blockDim.x; //Block x宽度 extern __shared__ float sIna[]; //共享内存 if (tid >= len_out) { return; } if (tid < len_a) { sIna[tx] = ina[tid]; } __syncthreads(); float sum = 0.0f; for (int m = 0; m < len_b; ++m) { int k = tid - len_b + m + 1; int sk = tx - len_b + m + 1; if (0 <= sk && sk < len_s) { sum += sIna[sk] * inb[m]; } else { if (0 <= k && k < len_a) { sum += ina[k] * inb[m]; } } } out[tid] = sum; }
__constant__ static float c_b[1024]; __global__ void conv3_kernel(const float *ina, float *out, size_t len_a, size_t len_b, size_t len_out) { const int tid = blockIdx.x * blockDim.x + threadIdx.x; const unsigned int tx = threadIdx.x; const size_t len_s = blockDim.x; extern __shared__ float sIna[]; const float *inb = &c_b[0]; if (tid >= len_out) { return; } if (tid < len_a) { sIna[tx] = ina[tid]; } __syncthreads(); float sum = 0.0f; for (int m = 0; m < len_b; ++m) { int k = tid - len_b + m + 1; int sk = tx - len_b + m + 1; if (0 <= sk && sk < len_s) { sum += sIna[sk] * inb[m]; } else { if (0 <= k && k < len_a) { sum += ina[k] * inb[m]; } } } out[tid] = sum; }
一口气列了三种,都是非常经典的加速技巧。
下面附上main()
<pre class="cpp" name="code">int main(int argc, char const *argv[]) { thrust::host_vector<float> h_a(N_A); thrust::device_vector<float> d_a(N_A); thrust::host_vector<float> h_b(N_B); thrust::device_vector<float> d_b(N_B); size_t N = h_a.size() + h_b.size() - 1; size_t L = pow( 2, static_cast<int>(log2(N - 1)) + 1 ); thrust::host_vector<float> h_result(N); thrust::device_vector<float> d_result(N); thrust::sequence(h_a.begin(), h_a.end()); // thrust::sequence(h_b.begin(), h_b.end()); thrust::sequence(h_b.rbegin(), h_b.rend()); // get raw pointer for kernel float *raw_point_a = thrust::raw_pointer_cast( &d_a[0] ); float *raw_point_b = thrust::raw_pointer_cast( &d_b[0] ); float *raw_point_result = thrust::raw_pointer_cast( &d_result[0] ); int numThreads = NUM_THREADS; int numBlocks = (L + numThreads - 1) / numThreads; cudaEvent_t start, stop; checkCudaErrors( cudaEventCreate(&start) ); checkCudaErrors( cudaEventCreate(&stop) ); cudaEventRecord(start); // copy a b to device thrust::copy(h_a.begin(), h_a.end(), d_a.begin()); thrust::copy(h_b.begin(), h_b.end(), d_b.begin()); // conv(raw_point_a, raw_point_b, raw_point_result, N_A, N_B, L, numBlocks, numThreads); conv2(raw_point_a, raw_point_b, raw_point_result, N_A, N_B, N, numBlocks, numThreads); thrust::copy(d_result.begin(), d_result.end(), h_result.begin()); cudaEventRecord(stop); checkCudaErrors( cudaThreadSynchronize() ); float time = 0; cudaEventElapsedTime(&time, start, stop); cout << "run times: " << time << " ms " << endl; // for (thrust::host_vector<float>::iterator i = h_result.begin(); i != h_result.end(); ++i) // { // cout << *i << " "; // } cout << endl; return 0; }
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: 图像均值滤波的CUDA并行化优化
- 下一篇: linux 中php以及nginx的重启命令