65.9K
CodeProject 正在变化。 阅读更多。
Home

避免在 Windows 上进行 CUDA 开发的考验与磨难

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (6投票s)

2010年9月8日

CPOL

9分钟阅读

viewsIcon

49457

理解用于 CUDA 开发的 Visual Studio 项目的组织结构

引言

有大量的示例代码可供开始学习如何使用英伟达的 CUDA 开发平台,以在图形处理单元上执行令人惊叹的快速并行浮点代码。但对于初学者来说,几乎没有信息可以帮助他们准确地了解如何组织、编译和链接他们的代码。本文回顾了 Visual Studio 项目在编译 CUDA 代码时的基本组织结构,希望能帮助大家尽快进入有趣的编码环节。

背景

“Hello, World”程序已经看过太多次,在某种程度上已经成为了陈词滥调。事实上,人们甚至忘记了第一个“Hello, World”程序具有非常重要的意义,远不止一段简单的示例代码。在 C 编译器早期,仅仅是安装编译器并使其运行就已经是一项艰巨的任务。有必须通过环境变量引用的包含目录,还有必须放置运行时库的文件夹,所有组件都必须保持正确的相互关系。如今,只需点击“Setup”图标即可,但在那个年代,安装编译器本身就是一项了不起的成就。

如果你能编译“Hello, World”程序,那就意味着你已成功安装了编译器。换句话说,编程本身并不难;难的是理解你所工作的环境!在 CUDA 的情况下,情况尤其如此,因为我们不是用一个,而是用两个独立的编译器进行编译。顺便说一句,这是一个平方函数。有两个编译器,出错的可能性就会增加四倍。

我们将不涵盖的内容

64 位系统上的 Visual Studio Express:在 64 位系统上安装 Visual Studio Express 并不能立即为你提供 64 位开发环境。如果要这样做,你必须首先确保你的机器上已安装相应的 64 位工具。这是你可以开始的地方:Jen's Blog

Visual Studio 2010:这是一个快速发展的领域,但据我所知,英伟达尚未为 CUDA 开发提供 VS2010 的支持。

入门

我们不会讨论 CUDA 的安装;英伟达提供了这方面的信息。你需要 CUDA 兼容的驱动程序来运行 CUDA 代码,以及 CUDA SDK 来开发代码。这里是你开始的地方。CUDA 二进制文件的默认安装目录是C:\CUDA。英伟达出色的示例代码安装在C:\Documents and Settings\All Users\Application Data\NVIDIA Corporation。如果你已成功加载并编译了一些示例,那么你已经做得不错了。查看示例项目相对于一个名为“common”的文件夹的位置。

projectdirectories.jpg

英伟达的示例假设这种相对关系存在。英伟达提供了一个模板解决方案,你可以将其用作自己项目的起点。如果你将此解决方案移到其他位置,则必须自行确保能够成功找到包含文件和库文件。这无法在 Visual Studio IDE 的图形工具中完成。你又回到了 70 年代,使用你的 C 编译器了!

核心细节

当你编译 CUDA 项目时,部分代码必须为 CUDA 设备编译,部分代码必须为 Windows 主机编译。你可以考虑的一个英伟达示例是“cppintegration”。在此解决方案中,有扩展名为“.cu”的文件和扩展名为“.cpp”的文件。.cpp 文件是良好、经典的 C 或 C++。 .cu 文件将由英伟达的编译器nvcc.exe编译。但是,如果 cpp 代码在 Windows 上运行,cu 代码在设备上运行,那也太简单了。cpp 代码确实由 Microsoft VC 编译器编译为 Windows。但是,cu 代码通常是分开的。英伟达编译器会将部分代码编译为在 CUDA 设备上运行,但部分代码必须编译为在 Windows 主机上运行。当然,主计算机必须能够在运行时加载和执行设备代码。

对于刚开始接触 CUDA 的开发者来说,一个非常重要的事实是英伟达编译器必须能够调用 cl.exe,即 VC 编译器。此外,英伟达编译器将使用命令行参数调用 cl。初学者在 CUDA 开发中遇到的一个重大障碍是 nvcc 使用的命令行参数必须与 Visual Studio 项目中使用的参数一致,否则你的代码将无法成功链接。

自定义生成

Visual Studio 允许定义自定义生成步骤,这些步骤可以调用外部工具。有两种方法。英伟达创建了一个自定义规则文件,并将此规则文件与 .cu 扩展名相关联。当你点击 Visual Studio 中的“生成”时,规则文件会告诉 VS 为 .cu 文件调用 nvcc。任何 cpp 文件都直接由 VS 编译。编译完成后,Visual Studio 链接器会将所有对象文件链接在一起:由 VS 创建的以及由 nvcc 创建的。希望这不会出错。但成功编译的最大障碍之一是,你现在正在处理两组包含文件和两组库规范。你的 Windows 部分的代码包含在 VS familiar 属性对话框中设置。英伟达编译器不知道 Visual Studio 项目中的设置。要更改操作的 nvcc 端,如果你移动了英伟达公共目录的相对位置,则必须修改 nvcc 命令行。

nvcc 命令行

如果你熟悉自定义生成规则,你可以编辑规则文件。缺点是任何修改都将应用于所有 CUDA 项目。创建一个新的自定义生成规则文件可能是明智的。如果你不熟悉自定义生成规则文件,那么就存在一个问题。Microsoft 完全修改了 Visual Studio 2010 中自定义生成规则的结构。如果你学习你的 CUDA 项目的自定义生成规则,你就是在花费时间学习即将过时的东西。

一种替代方法是将自定义生成步骤与每个单独的 .cu 文件关联起来。尽管这需要大量的重复复制粘贴,但也有一些优点。如果你刚开始接触自定义生成规则,我认为这是一种更容易上手的方式,而且它还有一个优点,那就是让你对生成过程中实际发生的事情有更清晰的认识。在这里,我们看到一个 .cu 文件的属性页,其中单独定义了自定义生成步骤。

FilePropertyPages.jpg

请记住填写“Outputs”部分。如果留空,生成步骤将不会运行。此示例还显示可以与同一步骤关联其他 .cu 文件。这些其他文件不包含在 Visual Studio 管理的生成中,但它们将被调用以包含在 nvcc 执行的编译步骤中。你可以根据需要使用此功能,但 CUDA 开发人员中的一种常见做法是将许多 .cu 文件分成两类。“template.cu”文件将包含通过 nvcc 编译的代码,但这些代码可能针对主机,而第二个文件“template_kernel.cu”仅包含“kernel”代码,即将在设备上独占运行且永不在主机上运行的代码。

这是上面属性页中的实际 nvcc 命令行

"$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)bin" -c -D_DEBUG -D_CONSOLE -D_MBCS -
Xcompiler /EHsc,/W3,/nologo,/Wp64,/Od,/Zi,/RTC1,/MTd -I"$(CUDA_INC_PATH)" -I./ -
I../../common/inc -o $(ConfigurationName)\$(InputName).obj $(InputFileName)

让我们分块来看

"$(CUDA_BIN_PATH)\nvcc.exe"

这显然是调用可执行文件。请注意,Visual Studio 使用类 make 的“$”来解引用环境变量。如果你想手动从命令行运行它,你需要更改为 DOS 风格的“%”表示法。

"$(VCInstallDir)bin" -c -D_DEBUG -D_CONSOLE -D_MBCS -
Xcompiler /EHsc,/W3,/nologo,/Wp64,/Od,/Zi,/RTC1,/MTd

在此命令行部分,你会注意到指定了将传递给 VC 的参数。如果这些参数与 Visual Studio 项目属性中设置的项目参数不兼容,你的项目可能会编译但无法链接。特别要注意运行时库的参数,在本例中是 /RTC1。如果 Visual Studio 属性引用了不同的库,你可能会遇到链接错误,提示某个过程已定义。也就是说,链接器在 VS 库和指定给 nvcc 的库中遇到了同名过程。这在从调试生成切换到发布生成时也可能出现问题。VS 库将被更改为发布运行时,而 nvcc 命令行可能仍会引用调试运行时版本。这有时需要试验。在 VS 链接器属性中,我们可以指定“忽略所有默认库”,或者像本例一样,要求不链接特定库。同时还要注意包含标准 CUDA 库供链接器考虑。

LinkProperties.jpg

包含文件文件夹规范

-I./ -I../../common/inc

此示例让 nvcc 在两个位置查找包含文件;你可以根据需要添加任意数量的-I参数。初次接触 CUDA 时遇到的一个非常常见的错误是找不到包含文件。当这种情况发生时,你必须确定是 VC 还是 nvcc 未能找到包含文件。如果错误发生在自定义生成步骤中,那几乎肯定是 nvcc。在这种情况下,在 VS 项目属性页中添加其他包含目录不会有任何效果。你必须在 nvcc 命令行中添加其他包含目录,就像我们这里看到的一样。

-o $(ConfigurationName)\$(InputName).obj $(InputFileName)

最后,命令行以输出对象文件的路径和文件名以及输入文件的名称结束。这里使用的样式比硬编码文件名容易得多。一旦找到一个成功的命令行,你就可以将其复制并粘贴到 VS 项目中其他 .cu 文件的属性页中。在大型项目中,文件将被分组到单独的文件夹中,这些文件夹可以在命令行中指定,如下所示

.\eigenvalues\$(InputFileName)

关注点

由 Jason Sanders 和 Edward Kandrot 合著的《CUDA by Example》一书,是一本非常周全且有价值的英伟达硬件 GPU 编程入门书籍。认识我的人都知道,我很少,而且只会吝啬地赞扬计算机书籍。虽然这本书有很多好的代码示例,但它在实际编译和运行示例方面提供的指导很少;这就是为什么我认为写下几点注意事项可能对那些考虑 CUDA 开发的人有所帮助。顺便说一句,这很有趣,而且非常值得努力。

历史

  • 首次发布 20100906
© . All rights reserved.