在Visual Studio Express环境下配置CUDA并使用Java通过JCuda与之交互可能会遇到一些挑战,特别是当希望从托管代码访问CUDA函数时。尽管网络上有许多资源可以帮助,但通常需要从不同来源整合信息,同时避免一些死胡同。这个过程有点像试错。希望通过分享经历,能够帮助少走弯路。
目前还没有在Visual Studio 2010 Express上配置CUDA。了解到,这个过程需要将2010项目配置为使用2008年的VC90编译器,而不是2010年的VC100编译器。可能还需要一些其他的技巧来让事情顺利进行。网络上有一些资源提供了这方面的指导。特别是,看到了一篇看起来很有希望的文章:
使用x86以外的配置运行托管代码对来说没有成功。网络上有几篇关于这个配置与VS Express的复杂帖子。Google搜索“Visual C++ 2008 Express Edition And 64-Bit Targets”可以找到一些有趣的方法来破坏VS Express安装。
首先在虚拟机中安装是一个好主意,但对来说,如何直接从客户机访问主机的GPU硬件并不清楚。VBox虚拟显卡适配器不支持CUDA,据所知,CUDA不再容易支持仿真模式。所以使用了标准技术:犯错,破坏安装,重新安装,然后跟随烟雾。
对GPU上的傅里叶变换特别感兴趣。只有少数的现成包装支持CUFFT功能。Cudafy(CodePlex)看起来最有希望,但当有VS Express时,它还不是一个即插即用的设置。
确保有一个支持CUDA的显卡。Nvidia在他们的开发者区域网站上有一个详尽的兼容GPU列表。
如果不确定,可以查看GPU Caps Viewer。通常对下载这样的工具持谨慎态度,但已经使用这个应用程序几年了,它被广泛认可,并且有一个可靠的绿色WOT评级。它将相当可靠地识别GPU并报告其OpenGL和CUDA功能。
安装VC++和VSC 2008 Express,然后通过在每个中运行一个“Hello World”测试来验证安装。
安装Nvidia开发驱动程序、CUDA工具包和CUDA SDK:
注意:从工具包的发行说明(开始 -> 程序 -> Nvidia):
对于Windows7-x64,安装程序可能错误地在路径说明中包含了一个额外的斜杠。
再次检查环境变量(计算机 -> 属性 -> 高级 -> 高级选项卡):
CUDA_BIN_PATH %CUDA_PATH%\bin
CUDA_INC_PATH %CUDA_PATH%\include
CUDA_LIB_PATH %CUDA_PATH%\lib\x64
到目前为止检查安装情况:
nvcc –V
(应该得到一个编译发布消息。)
找到bandwidthTest.exe(C:\ProgramData\NVIDIA Corporation\NVIDIA GPU Computing SDK 4.1\C\bin\win64\Release)并运行它。
也试试oceanFFT.exe
将“C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v4.1\extras\visual_studio_integration\rules”中的所有*.rules文件复制到“C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\VCProjectDefaults”
将“C:\ProgramData\NVIDIA Corporation\NVIDIA GPU Computing SDK 4.1\C\doc\syntax_highlighting\visual_studio_8\usertype.dat”复制到“C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE”文件夹
打开VC++并在工具 -> 选项:
文本编辑器 -> 文件扩展名添加两个扩展名:.cu和.cuh
项目和解决方案 -> VC++目录
添加%CUDA%bin在可执行文件的目录中
添加%CUDA Directory%include在包含文件的目录中
添加%CUDA%lib在库文件的目录中
关闭VC++然后重新打开,然后加载“hello world”程序并确保它仍然有效。
示例:FFT的简单裸机包装。
创建一个新的空Win 32项目,命名为BareBonesCuda。在下一页上勾选“dll”复选框。
添加一个源文件 - 类型cpp - 但命名为.cu扩展名,例如:
test.cu
右键单击项目并选择自定义构建规则。勾选CUDA运行时API的复选框。将有两个。使用没有版本#名称后的那个。
右键单击项目并选择属性。
在链接器 -> 常规 -> 附加库目录中添加:$(CUDA_PATH)/lib/$(PlatformName);
在链接器 -> 输入 -> 附加依赖项中添加:cudart.lib cufft.lib
将以下内容粘贴到test.cu中:
extern "C" int __declspec(dllexport) __stdcall _Fft(float real[], float imaginary[], int N, int batchSize) {
cufftComplex *a_h, *a_d;
cufftHandle plan;
int i, nBytes;
nBytes = sizeof(cufftComplex)*N*batchSize;
a_h = (cufftComplex *)malloc(nBytes);
for (i = 0; i < N*batchSize; i++) {
a_h[i].x = real[i];
a_h[i].y = imaginary[i];
}
cudaMalloc((void **)&a_d, nBytes);
if (cudaGetLastError() != cudaSuccess) {
cufftDestroy(plan);
free(a_h); cudaFree(a_d);
return 0;
}
cudaMemcpy(a_d, a_h, nBytes, cudaMemcpyHostToDevice);
if (cufftPlan1d(&plan, N, CUFFT_C2C, batchSize) != CUFFT_SUCCESS) {
cufftDestroy(plan);
free(a_h); cudaFree(a_d);
return 0;
}
cufftExecC2C(plan, a_d, a_d, CUFFT_FORWARD);
cudaDeviceSynchronize();
cudaMemcpy(a_h, a_d, nBytes, cudaMemcpyDeviceToHost);
for (i = 0; i < N*batchSize; i++) {
real[i] = a_h[i].x;
imaginary[i] = a_h[i].y;
}
cufftDestroy(plan);
free(a_h); cudaFree(a_d);
return 1;
}
构建它。(希望也能成功。)
在上面的示例中,一个名为BareBonesCuda.dll的文件在解决方案的Debug文件夹中被创建。注意它。
创建一个新的C#控制台应用程序。将配置更改为x86,然后调试一次空解决方案。这将在解决方案中创建一个名为\bin\x86\Debug的文件夹。将BareBonesCuda.dll复制到这个文件夹中。
将以下内容粘贴到Program.cs中:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MyTestSharp {
class Program {
[DllImport("BareBonesCuda.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_Fft")]
public static extern int _Fft(float[] real, float[] imaginary, int N, int batchSize);
static void Main(string[] args) {
test();
}
private static List fftFloat(float[] real, float[] imaginary, int N) {
int oK = _Fft(real, imaginary, N, 1);
List fftResult = new List();
fftResult.Add(real);
fftResult.Add(imaginary);
return fftResult;
}
private static void test() {
int N = 32768;
float[] real = new float[N];
float[] imaginary = new float[N];
StringBuilder sb = new StringBuilder();
char br = (char)13;
for (int i = 0; i < N; i++) {
real[i] = (float)i + 1;
sb.Append(real[i].ToString());
sb.Append("+ ");
imaginary[i] = 0;
sb.Append(imaginary[i].ToString());
sb.Append(br);
}
Console.WriteLine(sb.ToString());
sb = new StringBuilder();
List result = fftFloat(real, imaginary, N);
for (int i = 0; i < N; i++) {
sb.Append(real[i].ToString());
sb.Append("+ ");
sb.Append(imaginary[i].ToString());
sb.Append(br);
}
Console.WriteLine(sb.ToString());
}
}
}
运行它。(再次,希望也能成功。)