技术开发 频道

GPU高性能开发技术:CUDA实战

  关于示例文件的重要注意事项

  因为项目解决方案文件中的自定义生成规则条目,你必须做基本的配置才能打开连接的示例,如果该规则因路径错误不可访问,VC ++ 2008会返回错误,你也可以编辑cudalib.vcproj文件修复cuda.rules文件的路径。

  如果这样做没有帮助,那么直接创建一个新项目,像前面配置小节描述的那样手动添加自定义生成规则,然后将示例中的.cu文件拷贝到新项目。

  你也需要手工将cutil.h文件从“\NVIDIA GPU Computing SDK\C\common\inc\”目录拷贝到“C:\CUDA\include\”(这是修复导入路径最简单的方法),并将cutil32.lib(或用于64位的cutil64.lib)添加到链接程序输入附加依赖,结果看起来是“cudart.lib cutil32.lib”(在示例项目中已经做好)。

  CUDA工具包和SDK已经用于生成dll。

  即使没有这一步,主要项目部分(用C#编写)也可以工作,因为我已经将生成dll库添加到“\bin\Debug”和“\bin\Release”文件夹了(如果你在dll部分做了任何修改,你都必须替换它)。

  C#项目部分你需要VS 2010版本,因为它用到了.NET 4的功能。

  第2部分:CUDA DLL

  DLL部分代码已经用VC++ 2008 IDE写好,假设IDE配置正确,并用它创建了一个新的Win32控制台应用程序,将应用程序类型转换成“DLL”,并标记为“空白项目”(不需要预便于头和/或dllmain()函数),添加新的资源文件并保存为扩展名为.cu的文件,语法着色应该会工作,记住添加CUDA自定义生成规则和链接程序依赖。

  如果想运行CUDA内核(在GPU设备上执行的函数),我们需要一些包装函数暴露给外部dll,内核安装和调用都将在这个函数内,非常棒的一个功能是,我们可以传递内核执行配置参数给它(Grid大小,块大小和贡献内存大小),而不用给它们设置常量,这样我们就可以在目标机器上运行基准测试确定最合适的值。

// cuda wrapper function
extern "C" int __declspec(dllexport) __stdcall SomeCalculationsCU
(
float *a_h, // pointer to input array
const unsigned int N, // input array size
const unsigned int M, // kernel M parameter
const int cuBlockSize = 512, // kernel block size (max 512)
const int showErrors = 1 // show CUDA errors in console window
)
{
int tmp = PRINT_ERRORS;
PRINT_ERRORS
= showErrors;
float *a_d; // pointer to device array
size_t size = N * sizeof(float);
int cuerr = 0; // no errors
unsigned int timer = 0;
cudaMalloc((
void**)&a_d, size); // allocate array on device
cudaMemcpy(a_d, a_h, size, cudaMemcpyHostToDevice);
int n_blocks = N / cuBlockSize + (N % cuBlockSize == 0 ? 0 : 1);
cutCreateTimer(
&timer); // from cutil.h
cutStartTimer(timer);
some_calculations
<<<n_blocks, cuBlockSize>>> (a_d, N, M); // kernel invocation
cudaThreadSynchronize(); // by default kernel runs in parallel with CPU
code
cutStopTimer(timer);
cuerr
= checkCUDAError("cuda kernel");
cudaMemcpy(a_h, a_d, size, cudaMemcpyDeviceToHost);
if(!cuerr) cuerr = checkCUDAError("cuda memcpy");
sExecutionTime
= cutGetTimerValue(timer);
cudaFree(a_d);
if(!cuerr) cuerr = checkCUDAError("cuda free");
PRINT_ERRORS
= tmp;
return cuerr;
}

  该函数中最重要的部分是“extern "C" int _declspec(dllexport) _stdcall”部分,使得外部dll可见,必须指定调用约定(这里是_stdcall),因为默认情况下,C函数使用“_cdecl”,.NET平台使用CallingConvention.Winapi调用(即_stdcall)。另一个重要事项是,为调用程序和被调用函数使用相同的约定。

  这个函数也可以是控制台应用程序中的main()函数,因此,你可以修改它,并增加一些打印结果,例如:

int main(void)
{
float *a_h;
const unsigned int N = 2000;
const unsigned int M = 10;
const int cublocks = 256;
size_t size
= N * sizeof(float);
a_h
= (float*)malloc(size);
for(unsigned int i = 0; i < N; i++) a_h[i] = (float)i;
SomeCalculationsCU(a_h, N, M, cublocks,
1);
printf(
"exec time = %f ms\n", sExecutionTime);
}
0
相关文章