文章原创,转载请注明出处
创建dll
第一步新建一个win32dll项目。
选择dll
自动生成了dll相关的框架。库函数入口点DllMain:
第二步,添加CUDA函数相关的文件CUDAdll.cuh和CUDAdll.cu并设置框架属性。
为其设置生成属性:
CUDAdll.cuh文件
设置项目“生成自定义”
选择CUDA4.0并打钩
设置CUDAdll.cu文件的生成属性,选择CUDA C/C++。
此时如果编译该dll项目,会出现问题,报错如下:
1>Link:
1> 所有输出均为最新。
1> 正在创建库 D:\elec\mymfctest\VS2010p\GPUDemos\CUDAdlltest\Debug\CUDAdlltest.lib和对象 D:\elec\mymfctest\VS2010p\GPUDemos\CUDAdlltest\Debug\CUDAdlltest.exp
1>CUDAdll.cu.obj : error LNK2019:无法解析的外部符号 ___cudaRegisterFatBinary@4,该符号在函数 "void __cdecl __sti____cudaRegisterAll_42_tmpxft_00000e60_00000000_3_CUDAdll_cpp1_ii_564e775d(void)" (?__sti____cudaRegisterAll_42_tmpxft_00000e60_00000000_3_CUDAdll_cpp1_ii_564e775d@@YAXXZ)中被引用
1>CUDAdll.cu.obj : error LNK2019:无法解析的外部符号 ___cudaUnregisterFatBinary@4,该符号在函数 "void __cdecl __cudaUnregisterBinaryUtil(void)" (?__cudaUnregisterBinaryUtil@@YAXXZ)中被引用
1>D:\elec\mymfctest\VS2010p\GPUDemos\CUDAdlltest\Debug\CUDAdlltest.dll : fatal error LNK1120: 2个无法解析的外部命令
1>
1>生成失败。
意思是链接的时候出错了,找不到链接外部符号,原因是缺少cuda的运行库,通过项目的链接输入中添加CUDA的运行库cudart.lib可以解决。
至此,在没有编辑cuda函数实现文件的情况下可以正常将项目编译成功,则表明基本上将CUDAdll的框架搭建成功了。
第三步,编辑CUDAdll的实现文件对应的函数。
在CUDAdll.cuh中输入内容如下:
#include <stdio.h> //引入C函数库-实际上本程序就是应该以C的方式编译,尽管其后缀为cpp类型
#include <stdlib.h>
#include <cuda_runtime.h> //引入CUDA运行时库头文件
#ifdef __cplusplus //指明函数的编译方式,以得到没有任何修饰的函数名
extern "C"
{
#endif
#ifdef CUDADLLTEST_EXPORTS
#define CUDADLLTEST_API __declspec(dllexport) //导出符号宏定义
#else
#define CUDADLLTEST_API __declspec(dllimport)
#endif
extern CUDADLLTEST_API int count; //要导出的全局变量
CUDADLLTEST_API bool InitCUDA(void); //要导出的CUDA初始化函数
CUDADLLTEST_API void showHelloCuda(void); //要导出的测试函数
#ifdef __cplusplus
}
#endif
接着在在CUDAdll.cu中输入内容如下:
#include "stdafx.h" //引入预编译头文件
#include "CUDAdll.cuh" //引入导出函数声明头文件
//初始化CUDA
extern int count=0;
bool InitCUDA(void)//CUDA初始化函数
{
printf("Start to detecte devices.........\n");//显示检测到的设备数
cudaGetDeviceCount(&count);//检测计算能力大于等于1.0的设备数
if(count == 0){
fprintf(stderr, "There is no device.\n");
return false;
}
printf("%d device/s detected.\n",count);//显示检测到的设备数
int i;
for(i = 0; i < count; i++){//依次验证检测到的设备是否支持CUDA
cudaDeviceProp prop;
if(cudaGetDeviceProperties(&prop, i) == cudaSuccess) {//获得设备属性并验证是否正确
if(prop.major >= 1)//验证主计算能力,即计算能力的第一位数是否大于1
{
printf("Device %d: %s supports CUDA %d.%d.\n",i+1,prop.name,prop.major,prop.minor);//显示检测到的设备支持的CUDA版本
break;
}
}
}
if(i == count) {//没有支持CUDA1.x的设备
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
cudaSetDevice(i);//设置设备为主叫线程的当前设备
return true;
}
void showHelloCuda(void)//测试CUDA初始化函数
{
if(!InitCUDA()) //初始化失败
{
printf("Sorry,CUDA has not been initialized.\n");
return;
}
printf("Hello GPU! CUDA has been initialized.\n");
}
输入完毕保存后进行编译,则将在Debug目录下输出对应的dll文件:
通过微软的VC++6.0或者VS2010安装时带的Tools中的Depends工具打开CUDAdlltest.dll,可以看到导出的函数和变量:
需要注意的是前面在CUDAdll.cuh中输入内容中红色部分的指定编译方式宏很重要,如果没有该部分就会导出上图中VS在创建工程时默认给出导出示例函数和变量(包含该函数的两个文件CUDAdlltest.h和CUDAdlltest.cpp实际上没有用到仅作为比对,可以删去)都带有Visual C++自己的修饰符号,在win32默认的控制台程序中调用这些函数往往由于有修饰而加载时出现找不到函数的情况,而且在外部C语言程序/编译方式下无法正常运行。
测试dll
创建一个测试前面生成的dll的工程项目
选择win32控制台项目
自动生成框架如下:
首先使用动态加载DLL的方式:
在testCUDAdll.cpp中输入程序如下:
#include "stdafx.h"
#include <stdio.h> //引入C函数库
#include <stdlib.h>
#include <Windows.h>
//显示/动态加载链接测试
typedef void(*DLLFUNC)(void);//声明需要从dll中调用的函数原型的函数指针
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hcudaDll=LoadLibrary(__T("CUDAdlltest.dll"));//动态地加载CUDAdlltest.dll
if (hcudaDll)
{
DLLFUNC dllFun=(DLLFUNC)GetProcAddress(hcudaDll,"showHelloCuda");//获得函数指针
if (dllFun)
{
dllFun();//执行showHelloCuda函数
}
else
{
printf("Can not find the function in dll!");//可能由于函数名错误
}
FreeLibrary(hcudaDll);//动态地卸载CUDAdlltest.dll
}
else
{
printf("Load dll fail!");
}
return 0;
}
将CUDAdlltest.dll文件复制到测试程序.exe文件的目录下,编译之后运行结果如下:
可见程序执行正确。
使用CUDAdlltest.lib静态链接方式测试:
在testCUDAdll.cpp中输入程序如下:
#include "stdafx.h"
#include <stdio.h> //引入C函数库
#include <stdlib.h>
#include <Windows.h>
#pragma comment(lib,"CUDAdlltest.lib") //引用库文件
//导出函数的声明 extern "C"是不可少的
extern "C" int count; //已导出的全局变量
extern "C" bool InitCUDA(void); //已导出的CUDA初始化函数
extern "C" void showHelloCuda(void); //已导出的测试函数
//隐式/静态加载链接测试
int _tmain(int argc, _TCHAR* argv[])
{
showHelloCuda();//调用库中的函数
return 0;
}
将CUDAdlltest.lib文件复制到工程目录下以及将CUDAdlltest.dll文件复制到测试程序.exe文件的目录下,编译并运行程序,结果同上:
通过在VS平台将CUDA相关的函数(CUDA的编译器windows版本现在只能在VS平台中使用)封装成dll库之后就可以在Visual C++6.0平台中使用了,这是个非常好的方法。例如上面的测试程序在VC6.0的控制台程序中如下:
#include "stdafx.h"
#include <stdio.h> //引入C函数库
#include <stdlib.h>
#include <Windows.h>
#pragma comment(lib,"CUDAdlltest.lib") //引用库文件
//导出函数的声明 extern "C"是不可少的
extern "C" int count; //已导出的全局变量
extern "C" bool InitCUDA(void); //已导出的CUDA初始化函数
extern "C" void showHelloCuda(void); //已导出的测试函数
int main(int argc, char* argv[])
{
showHelloCuda();//调用库中的函数
return 0;
}
不过VS平台上创建的工程默认是Unicode字符集的,即前面的那个dll库文件是Unicode字符集的
如果直接将其拷贝到VC6.0默认创建的MBCS多字节字符集测试工程中,编译能通过,但是如果有字符操作的函数运行可能会出错,因而需要将字符集也设置为库创建时相同的字符集,程序中也可能要做相应的修改。
最后给出本文的三个测试程序(一个是库文件程序,另外两个是VS2010平台和VisualC++6.0平台下的测试程序,可以方便地作为CUDA dll应用程序开发的框架,为了能正确运行这些程序,请确保已经安装了NVIDIA GPU Computing SDK 4.0 及以上,并且电脑上要有NVIDIA的GPU显卡)的下载地址:
参考文献:
CUDA DLL开发流程,http://blog.csdn.net/openhero/article/details/4059411
GPU编程资源链接,http://blog.csdn.net/menglongbor/article/details/6938029