到此,经由前三篇文章的说明,我们对gpu编程应该有了很大的认知,基本上可以完成一些简答的开发小任务了,其实cuda的功能还包括很多,这个系列就不一一介绍了,最终篇,我们介绍一下cuda GPU编程在实际工程中的代码调用吧。
cuda是由.cu文件保存的,g++和gcc都不能识别和编译这个,我们要么用nvcc编译,要么像前面一样,通过cmake文件来编译。即便如此,我们的.cu文件也不能直接被.cpp文件调用,我们需要对.cu文件中的函数进行包装,使其成为普通的c family函数,然后我们才能通过头文件在其他函数中调用包装过的函数,完成相应的任务。
接下来我们建立一个add_vector.h、add_vector.cu、 main.cpp、CMakeLists.txt等文件,来完成一个简单的功能。
add_vector.h:
#ifndef __VECTOR_ADD_H__
#define __VECTOR_ADD_H__
#include "cuda.h"
#define N 100
void kernel_wrapper(int *a, int *b, int *c);
#endif</code></pre>
add_vector.cu:
<pre class="pure-highlightjs"><code class="">#include "vector_add.h"
__global__ void add(int *a, int *b, int *c){
int tid = blockIdx.x;
if(tid<N){
c[tid] = a[tid] +b[tid];
}
}
void kernel_wrapper(int *a, int *b, int *c){
int *dev_a, *dev_b, *dev_c;
cudaMalloc((void**) &dev_a, N*sizeof(int));
cudaMalloc((void**) &dev_b, N*sizeof(int));
cudaMalloc((void**) &dev_c, N*sizeof(int));
cudaMemcpy(dev_a, a, N*sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(dev_b, b, N*sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(dev_c, c, N*sizeof(int), cudaMemcpyHostToDevice);
add<<<N,1>>>(dev_a, dev_b, dev_c);
cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);
cudaFree(dev_a);
cudaFree(dev_b);
cudaFree(dev_c);
}</code></pre>
main.cpp:
#include<iostream>
using namespace std;
int main(){
int a[N], b[N], c[N];
for(int i=0; i<N; i++){
a[i] = i;
b[i] = 2;
}
kernel_wrapper(a, b, c);
for(auto item:c){
cout<<item<<endl;
}
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(chapter0)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
find_package(CUDA)
#1
CUDA_ADD_LIBRARY(add vector_add.cu)
CUDA_ADD_EXECUTABLE(g main.cpp)
target_link_libraries(g add)
#2
#CUDA_ADD_EXECUTABLE(g main.cpp vector_add.cu)
##target_link_libraries(g ${OpenCV_LIBS})
这里的关键就是.cu语言中对kernel函数的包装,使所有的cuda执行代码都包含在其中,只对外暴露*a、 *b、 *c三个普通的内存指针,伪装成了普通的c famliy函数,所以普通的函数调用它自然是很没问题的。
最后在cmake中,我们甚至可以将.cu函数编译成静态库或者共享库,然后来链接到主程序上。最后执行cmake文件,我们得到需要的二进制文件。
到此,本系列结束。
网友评论