为了调用方便和减少每次都没写.cu文件的麻烦和出错的几率,可以将编写好的CUDA并行程序生成dll动态库,每次使用是只需调用库的接口,省时省力,也便于集成到其他平台上。
环境:Win10,VS2017,CUDA10.1
封装动态库
- 启动VS2017,创建新项目
- 生成的工程如下,我们主要在CudaDll.h和CudaDll.cpp中进行修改
- 根据自己的目的修改CudaDll.h和CudaDll.cpp文件,在这里我们只需要一个函数的接口,因此把第一个框框出来的部分删掉,将函数名改为vectorAdd(int *a, int *b, int *c, int size);记得在函数前面加上前缀属性extern “C”
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 CUDADLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// CUDADLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef CUDADLL_EXPORTS
#define CUDADLL_API __declspec(dllexport)
#else
#define CUDADLL_API __declspec(dllimport)
#endif
// 此类是从 dll 导出的
//class CUDADLL_API CCudaDll {
//public:
// CCudaDll(void);
// // TODO: 在此处添加方法。
//};
//extern CUDADLL_API int nCudaDll;
extern "C" CUDADLL_API int vectorAdd(int *a, int *b, int *c, int size);
- 在CudaDll.cpp,删掉第一个框和第三个框的内容,将导出函数修改为我们需要的函数,然后先放一边,先去编写.cu文件,完成之后再来修改函数体的内容
// CudaDll.cpp : 定义 DLL 的导出函数。
//
#include "pch.h"
#include "framework.h"
#include "CudaDll.h"
这是导出变量的一个示例
//CUDADLL_API int nCudaDll=0;
// 这是导出函数的一个示例。
extern "C" CUDADLL_API int vectorAdd(int *a, int *b, int *c, int size)
{
return 0;
}
这是已导出类的构造函数。
//CCudaDll::CCudaDll()
//{
// return;
//}
- 右键点击源文件,选择“添加”,选择“新建项”
- 按照下面的方法往项目中添加kernel.cu文件,同样的方法添加kernel.h文件(添加.h文件要选择CUDA C/C++ Header)
完成之后
- 将CUDA并行程序的主要内容编写到这两个文件之中,调用的接口写到kernel.cuh
kernel.cu的内容
#include "kernel.cuh"
//CUDA核函数
__global__ void addKernel(int *c, const int *a, const int *b)
{
int i = threadIdx.x;
c[i] = a[i] + b[i];
}
//向量相加
int myVectorAdd(int *a, int *b, int *c, int size)
{
int result = -1;
int *dev_a = 0;
int *dev_b = 0;
int *dev_c = 0;
cudaError_t cudaStatus;
// 选择用于运行的GPU
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
result = 1;
goto Error;
}
// 在GPU中为变量dev_a、dev_b、dev_c分配内存空间.
cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
result = 2;
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
result = 3;
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
result = 4;
goto Error;
}
// 从主机内存复制数据到GPU内存中.
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
result = 5;
goto Error;
}
cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
result = 6;
goto Error;
}
// 启动GPU内核函数
addKernel << <1, size >> > (dev_c, dev_a, dev_b);
// 采用cudaDeviceSynchronize等待GPU内核函数执行完成并且返回遇到的任何错误信息
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess) {
result = 7;
goto Error;
}
// 从GPU内存中复制数据到主机内存中
cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
result = 8;
goto Error;
}
result = 0;
// 重置CUDA设备,在退出之前必须调用cudaDeviceReset
cudaStatus = cudaDeviceReset();
if (cudaStatus != cudaSuccess) {
return 9;
}
Error:
//释放设备中变量所占内存
cudaFree(dev_c);
cudaFree(dev_a);
cudaFree(dev_b);
return result;
}
kernel.cuh的内容
#pragma once
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
__global__ void addKernel(int *c, const int *a, const int *b);
int myVectorAdd(int *a, int *b, int *c, int size);
- 修改项目的自定义方式为:CUDA10.1
- 修改cu文件的项类型
- 此时kernel.cu文件会有很多报错,没关系,把调试器改成x64,添加链接器的附加依赖项 cudart.lib
完成之后,所有的报错都消失了 - 接下来回到CudaDll.cpp文件中添加kernel.cuh头文件并修改vectorAdd函数体
// CudaDll.cpp : 定义 DLL 的导出函数。
//
#include "pch.h"
#include "framework.h"
#include "CudaDll.h"
#include "kernel.cuh"
// 这是导出函数的一个示例。
CUDADLL_API int vectorAdd(int *a, int *b, int *c, int size)
{
myVectorAdd(a, b, c, size);
return 0;
}
- 进行到这里,就可以生成dll文件了
- 完成之后文件夹下生成了dll文件和lib文件(这一步失败的话,一定要去确定一下附加依赖项有没有正确添加cudart.lib
调用动态库
调用动态库就很简单啦
先新建好一个普通的工程(此处就不赘述了)
将生成的CudaDll.h头文件、dll文件和lib文件拷贝到工程的当前目录下
记得把调试器改成x6,否则编译不通过!!!
修改工程属性,在附加依赖项处添加“CUDADLL.lib”(非常重要)
将头文件CudaDll.h添加到工程中
编写调用程序进行测试
#include <iostream>
#include "CudaDll.h"
int main()
{
const int size = 6;
int a[size] = { 1, 2, 3,4,5,6 };
int b[size] = { 12,14,16,18,20,22 };
int c[size] = { 0 };
int result = vectorAdd(a, b, c, size);
printf("[%d, %d, %d, %d, %d, %d] \n + [%d, %d, %d, %d, %d, %d] \n= [%d, %d, %d, %d, %d, %d]",
a[0], a[1], a[2], a[3], a[4], a[5],
b[0], b[1], b[2], b[3], b[4], b[5],
c[0], c[1], c[2], c[3], c[4], c[5]);
return 0;
}
运行成功