# 二、创建 CUDA 项目

## 下载工具

### Visual Studio 2012 Express

在开始任何 CUDA 项目之前，您需要确保安装了合适的 IDE(如果您已经安装了 Visual Studio Express 或 Professional，则可以跳过此步骤)。下载并安装 Visual Studio 2012 Express。除非另有说明，本书中的代码示例和截图都是基于这个 IDE 的。为 Visual Studio 2010 和 2013 创建 CUDA 项目的步骤几乎是相同的，这些 ide 也应该可以使用，而无需对我的指令进行任何更改。Visual Studio 2012 Express 可从微软网站[这里](http://www.microsoft.com/en-us/download/details.aspx?id=34673)下载，Visual Studio 2013 Express 可从[这里](http://www.visualstudio.com/products/visual-studio-express-vs)下载。

### CUDA 工具包

在下载和安装 CUDA 工具包之前，您应该确保您的硬件支持 CUDA。CUDA 专用于运行 NVIDIA 图形硬件的系统。AMD 显卡、英特尔或任何其他图形硬件都不会执行 CUDA 程序。此外，只有相对现代的 NVIDIA 显卡支持 CUDA。2006 年的 GeForce 8 系列卡是第一代支持 CUDA 的硬件。您可以在 NVIDIA 开发者网站[这里](https://developer.nvidia.com/cuda-gpus)查看您的硬件是否在以下支持 CUDA 的设备列表中。

一旦你确定你的硬件支持 CUDA，下载并安装最新的 CUDA 工具包。您可能希望注册为 NVIDIA 开发人员，以接收关于最新 CUDA 版本和 CUDA 相关事件的新闻，并尽早访问即将发布的 CUDA 版本。该工具包可从 NVIDIA 开发者网站[获得，此处为](https://developer.nvidia.com/cuda-downloads)。

请务必下载适合您将要开发的环境的工具包版本。CUDA 工具包需要大约 1 GB 的下载量，因此下载可能需要一些时间。我将使用的版本是 64 位的 Windows 桌面版本。我们不会编写任何 64 位的特定代码，所以 32 位版本也可以。该工具包附带了许多代码示例、显卡的开发人员驱动程序，以及对 CUDA 进行编码所需的库。在安装 CUDA 工具包之前，请务必安装 Visual Studio，因为它将 CUDA 功能添加到了集成开发环境中。

以适合您下载文件的平台的方式运行下载的文件，并确保按照给出的说明正确安装工具包。

一旦您安装了 CUDA 工具包，我鼓励您探索安装的文件夹。在对 CUDA 编程时，我们将多次引用这个安装文件夹及其库和头。如果在安装过程中不更改安装路径，工具包安装到的默认文件夹如下:

c:\程序文件\NVIDIA GPU 计算工具包\CUDA\v6.5

其中`C:`是主系统驱动，`v6.5`是你安装的工具包版本。

## 设备查询

在我们开始一个 CUDA 项目之前，了解您将要编程的硬件可能是一个好主意。CUDA 水平低；它的设计考虑了非常具体的硬件。在本书的整个过程中，我们会提到许多我们将要编程的 CUDA 设备的特定功能和指标。CUDA 工具包安装了一个包含大量示例的示例包，其中一个示例叫做设备查询。这是一个非常方便的应用程序，它将关于您安装的硬件的有趣信息打印到控制台窗口。根据您下载的 CUDA 工具包的版本，DeviceQuery.exe 程序可能位于几个不同的地方。它是 CUDA 示例的一部分，您可以通过单击 NVIDIA 程序组中的**示例**图标并定位**设备查询**来找到它。

应用程序也可以从 NVIDIA 网站[这里](http://docs.nvidia.com/cuda/cuda-samples/index.html#device-query)单独下载安装。

![](img/image001.png)

图 2.1:设备查询输出

| ![](img/tip.png) | 提示:你可能会发现，当你运行 deviceQuery.exe 时，它打开和关闭得太快，无法阅读。在 Windows 资源管理器中打开包含 deviceQuery.exe 文件的文件夹。按住 Shift 键并右键单击文件夹中的空白区域。您应该会在上下文菜单中看到一个打开命令窗口的选项。单击此按钮将打开一个命令窗口，您可以在其中键入“deviceQuery.exe”来运行该程序，并且该窗口不会自动关闭。 |

图 2.1 是我在本书中使用的设备的设备查询输出。通过检查设备查询的输出，可以找到对各种指标的引用，例如每个流式多处理器(SM)的最大线程数。当我在文本中引用“设备查询”中的特定值时，您应该为自己的硬件查找这些值。如果您如前所述打开设备查询，您可能希望在阅读本书以供参考时保持其打开和最小化。

## 创建 CUDA 项目

CUDA 在第一次调用 CUDA 运行时函数时被初始化。CUDA 运行时是内存管理和其他基本功能的集合。要调用 CUDA 运行时函数，项目需要包含`CUDA.h`并链接到 CUDA 运行时库`CUDART.lib`。

要创建我们的第一个项目，在 Visual Studio 2012 Express 中打开一个新的解决方案，选择 **C++** 作为语言，使用一个空项目。创建项目后，在解决方案资源管理器中右键单击项目名称，然后单击**构建定制**，如图 2.2 所示。如果您使用的是 Visual Studio 2013，则“构建自定义”选项位于标有**构建依赖关系**的子菜单下。

![](img/image003.jpg)

图 2.2:构建定制

这将打开“生成自定义”窗口。选择 **CUDA x.x()旁边的复选框。目标。道具)**如图 2.3 所示。选择此生成自定义将导致 Visual Studio 对扩展名为. cu 的文件使用 CUDA 工具集，这些文件是 CUDA 源文件。CUDA 内核(为 GPU 设计的函数)是用。cu 文件但是。cu 文件也可以包含常规的 C++。一旦选择了 CUDA x.x()。目标。道具)搭建定制，点击**确定**保存修改。

![](img/image004.jpg)

图 2.3: CUDA 5.5(。目标。道具)

接下来，我们可以向我们的项目添加一个 CUDA 源文件。在解决方案资源管理器中右键单击项目，单击**添加**，然后在上下文菜单中单击**新建项目**，如图 2.4 所示。

![](img/image005.jpg)

图 2.4:添加代码文件

点击 **C++ 文件(。**并命名你的文件。我已经调用了我的`Main.cu`，如图 2.5 所示。重要的是给文件一个. cu 扩展名，而不是默认的. cpp。由于构建定制设置，这个代码文件将被发送到 NVIDIA CUDA C 编译器(NVCC)。NVCC 编译器将编译所有的 CUDA 代码，并将剩余的 C++ 代码返回给微软 C++ 编译器。

![](img/image006.png)

图 2.5:添加. cu 文件

接下来，我们需要确保 Visual Studio 知道 CUDA 头文件所在的目录；这可能在你的机器上和在我的机器上是一样的(取决于你是否在安装过程中改变了目录)。如果您使用的是 Visual Studio Professional，路径可能已经设置好了，但最好还是检查一下。通过单击菜单栏中的**项目**并选择**项目名称属性**(其中项目名称是您项目的名称)，打开项目的属性。单击左侧面板中的 **VC++ 目录**，然后单击右侧面板中的**包含目录**条目旁边的展开箭头。在出现的菜单中点击**编辑**；这将允许您指定 Visual Studio 搜索标题的路径，包括三角形括号(<和>)。参考图 2.6。

![](img/image007.jpg)

图 2.6:包含目录的属性

在**包含目录**对话框中，点击**新建文件夹**图标，在电脑上找到 CUDA 工具包**包含**文件夹。它很可能位于类似 C:\Program Files\NVIDIA GPU 计算工具包\CUDA\v5.5\include 的位置，如图 2.7 所示。点击**选择文件夹**时，会出现图 2.8 的方框。选择文件夹后，点击**确定**。

![](img/image008.jpg)

图 2.7: CUDA 工具包包含目录

![](img/image009.png)

图 2.8:单击确定

添加此路径后，当 Visual Studio 找到包含在右尖括号(<和>)中的标题时，将使用其他标准包含路径搜索 CUDA **包含**目录。

接下来，我们需要指定 CUDA 库文件在哪里(这是静态链接“lib”库)。您可能不需要指定 CUDA 库文件夹的位置，这取决于 CUDA 安装程序是否自动注册了安装 CUDA 库的文件夹。除非您在安装 CUDA 工具包时指定了不同的目录，否则库文件很可能位于与标题相似的位置。选择**库目录**，点击右侧下拉箭头，点击**编辑**，如图 2.9 所示。

![](img/image010.png)

图 2.9:库目录的属性

![](img/image011.png)

图 2.10: CUDA 库目录

有两个库目录；一个用于 32 位项目，另一个用于 64 位项目。选择适合您当前解决方案平台的解决方案。请记住，CUDA 版本号和确切路径在您的机器上可能与以下示例不同。

c:\程序文件\NVIDIA GPU 计算工具包\CUDA\v5.5\lib\Win32

c:\程序文件\NVIDIA GPU 计算工具包\CUDA\v5.5\lib\x64

一旦添加了库目录，我们就需要链接到 CUDA 运行时库:一个名为`CUDART.lib`的文件。展开**链接器**，然后从项目属性页面的左侧面板选择**输入**。在**附加依赖项**框中输入`$(CUDAToolkitLibDir)\CUDART.lib;`。您可以将 CUDART 依赖项添加到列表的开头或结尾，但请注意不要删除所有标准的窗口库依赖项。记得点击**应用**保存这些更改，然后点击**确定**关闭对话框，如图 2.10 所示。稍后，当我们链接到其他库时，步骤是相同的。例如，要链接到`curand.lib`库，您可以将`$(CUDAToolkitLibDir)\CURAND.lib;`添加到附加依赖项中。

| ![](img/tip.png) | 提示:如果路径已经为 Visual Studio 所知，您实际上可以只提供库的名称。比如可以键入 CUDART.lib 而不是较长的$(cudatolkitlibdir)\ cudart . lib。 |

![](img/image012.png)

图 2.10: CUDA 运行时库

现在我们已经添加了包含路径和运行时库，我们准备编写一些 CUDA 代码。最初，我们可以通过将清单 2.1 中的源代码写入。我们之前添加到项目中的 cu 文件。

```cpp
    #include <iostream>
    #include <cuda.h>          // Main CUDA header
    using namespace std;
    int main() {
    cout<<"Hello CUDA!"<<endl;
    return 0;
    }

```

清单 2.1:基本的 CUDA 程序

通过按 F5 或点击**调试**菜单中的**开始调试**来调试您的项目。清单 2.1 没有用 CUDA 做任何事情，但是如果项目构建正确，这就很好地表明一切都安装正确了。程序将打印文本“你好 CUDA！”到屏幕上。

| ![](img/note.png) | 注意:CUDA 的每个版本都是为一个或多个特定版本的 Visual Studio 而设计的。CUDA 可能不支持您的平台工具集(Visual Studio 版本)。您可以在项目的属性中更改平台工具集，但前提是您安装了其他版本的 Visual Studio。可以在项目属性的“配置属性&#124;常规”页面中指定平台工具集。在“平台”选项中，您可以看到已安装的 Visual Studio 版本。要使用其他平台工具集，您需要安装不同版本的 Visual Studio。本文中的代码是用 Visual Studio 2012 构建的，我使用的平台工具集版本是 v110。 |

## 文本高亮显示

让 Visual Studio 认识到。cu 文件实际上是 C++ 源代码文件。这样，我们在编程时就能获得代码突出显示和智能感知的所有好处。默认情况下，Visual Studio Express 不知道这些文件中的代码基本上只是带有一些 CUDA 扩展的 C++ 代码。单击**工具**打开 Visual Studio 属性页，然后从菜单中选择**选项**。

![](img/image014.png)

图 2.11:的语法高亮显示。cu 文件

为启用 C++ 语法突出显示和智能感知代码建议。cu 文件，点击左侧面板中的**文本编辑器**，然后点击**文件扩展名**(见图 2.11)。在右侧面板中，在**扩展**框中键入“CU”。从**编辑器**下拉列表中选择**微软 Visual C++** ，然后点击**应用**。这些更改将被保存以供将来的项目使用。每当将. cu 文件添加到项目中时，Visual Studio 将始终使用 C++ 颜色和智能感知。

您可能会注意到，大多数 CUDA 关键字都用红色下划线标出，就好像它们是错误一样。Visual Studio 将。cu 文件作为常规的 C++ 但是不理解 CUDA 的关键字。CUDA 关键字被 NVCC 理解，没有任何声明。您可以包含一些标题来定义关键字，并帮助 Visual Studio 以与常规代码相同的方式识别它们。通用 CUDA C 编程唯一必需的头是 CUDA.h 头。清单 2.2 中显示的以下标题也可能包括在内(所有标题都位于与 cuda.h 相同的文件夹中)以及所示的`#define`；这将启用内核突出显示，并防止 Visual Studio 将 CUDA 关键字作为错误加下划线。

```cpp
    #define __cplusplus
    #define __CUDACC__

    #include <iostream>
    #include <cuda.h>
    #include <device_launch_parameters.h>
    #include <cuda_runtime.h>
    #include <device_functions.h>

```

清单 2.2:建议的标题和定义

| ![](img/note.png) | 注意:程序可以在有或没有这些附加头的情况下生成，但是 Visual Studio 会将所有未定义的符号报告为错误。当一个程序有合法的错误并且无法编译时，CUDA 符号错误的数量可以达到数百个。这使得很难知道 Visual Studio 报告的哪些错误是真正的错误，哪些是未定义的 CUDA 符号。由于这个原因，通常最好在清单 2.2 中包含标题(或者在代码中定义关键字的任何标题)，即使它们是可选的。 |

## 超时检测和恢复

在我们使用显卡计算密集型算法之前，我们需要确保系统不会假设设备已经停止运行。通常在图形处理过程中，设备会非常快速地计算一帧图形，并在 1/60 秒内响应操作系统(如果它以每秒 60 帧的速度运行应用程序)。当使用该设备进行通用编程时，我们经常期望计算能够持续更长的时间。CUDA 内核可能在几分之一秒内执行，但根据计算的复杂性，它可能需要几周的计算时间。

问题是，当设备在两秒钟内没有响应时，操作系统会假设设备在计算图形时已经停止。操作系统将重置图形驱动程序，使用户返回到窗口或更糟。充其量，这将导致用户的屏幕关闭，程序崩溃，然后屏幕重新打开。但它也可能导致系统重置，而不安全地关闭任何正在运行的应用程序。

我们可以通过在 Windows 注册表中设置`TdrLevel`和`TdrDelay`键来改变操作系统允许设备运行的时间。“Tdr”代表超时检测恢复。这些注册表项用于设置 Windows 在假定设备已停止之前等待设备响应的时间。它们也可以用来完全关闭超时检测。

程序 RegEdit.exe 是用来改变窗口注册表项。要运行 RegEdit.exe，按住 Windows 键并按 R 打开**运行**对话框(Windows 键在键盘左下角；上面有 Windows 徽标的图像)。按图 2.12 在**打开**框中输入“regedit”，点击**确定**。用户帐户控制框可能会提示您；如果是，点击**是**允许注册表编辑器对系统进行修改。如果您没有系统的管理权限，您需要在继续之前联系您的管理员。

![](img/image016.png)

图 2.12:窗口运行对话框

***警告**:使用 RegEdit.exe 程序时要小心。Windows 注册表包含许多对系统操作很重要的键。使用文件>导出选项或创建系统还原点，在进行更改之前保存注册表备份。*

![](img/image017.png)

图 2.13:注册表编辑器

当您打开注册表编辑器应用程序时，您将看到类似于常规文件浏览窗口的显示，如图 2.13 所示。我们需要确保两个键`TdrLevel`和`TdrDelay`存在于图形驱动程序的注册表设置中，并且具有适合我们使用的值。

可以在以下位置找到到达图形驱动程序密钥的路径

HKEY _ LOCAL _ MACHINE \ System \ current controlset \ Control \ graphics drivers

单击左侧面板中的 **HKEY_LOCAL_MACHINE** 文件夹，然后单击**系统**子文件夹，以此方式继续，直到找到**图形驱动程序**子文件夹。您的屏幕应该类似于图 2.14。

![](img/image018.png)

图 2.14:图形驱动程序子文件夹

您可能在 GraphicsDrivers 子文件夹中有不同的键，并且您可能已经有了`TdrLevel`和`TdrDelay`键。如果这些键已经存在，您可以跳过下一步(我们将在那里创建它们)并跳到图 2.17，在那里我们修改键。

![](img/image019.png)

图 2.15:添加新密钥

要在注册表中添加新项，请右键单击左侧面板中的**图形驱动程序**文件夹，单击**新建**，然后从上下文菜单中选择 **DWORD** **(32 位)值**。这将向注册表添加一个 32 位整数的新项。该密钥最初将被称为新值#1 或类似的东西。右键单击`TdrLevel`并从上下文菜单中选择**重命名**，重命名键`TdrLevel`，如图 2.16 所示。

![](img/image020.png)

图 2.16:重命名变量

使用相同的步骤添加另一个名为`TdrDelay`的 32 位整数键。一旦键被添加，您可以通过右键单击它们的名称并从上下文菜单中选择**修改**来编辑它们的值，如图 2.17 所示。

![](img/image021.png)

图 2.17:修改值

如果键已经存在，那么您所需要做的就是修改这些值。如图 2.17 所示，选择**修改**时，会出现编辑图纸对话框(见图 2.18)。然后，您可以在**值数据**文本框中为键键入新值，使用**基数**选项选择十六进制或十进制作为基数。

![](img/image022.png)

图 2.18:编辑值

有关 TDR 键所有设置的完整说明，请访问 MSDN 开发中心，网址为:[。](http://msdn.microsoft.com/en-us/library/windows/hardware/ff569918%28v=vs.85%29.aspx)

`TdrLevel`键可以设置为四个值中的任意一个；我们只关心其中的两个:

*   0—检测已禁用。这对于 CUDA 编程来说是最方便的；这意味着系统永远不会检测到超时并重置驱动程序。
*   3—超时时恢复。这是默认设置。如果响应时间超过`TdrDelay`键中指定的秒数，驾驶员将重置。

默认情况下，`TdrLevel`和`TdrDelay`的设置分别为 3 和 2。这意味着 Windows 会给设备两秒钟的时间，如果它没有响应，就会重置驱动程序。为了留出更多的时间(例如 60 秒)，将`TdrLevel`变量设置为 3，并将秒数(即`TdrDelay`变量的值)设置为 60(如图 2.19 所示)。

![](img/image023.png)

图 2.19:TDR 变量设置示例

我们可以通过将`TdrLevel`键设置为 0 来完全关闭超时检测，而不是在 Windows 应该重置驱动程序之前留出一些时间。这意味着无论设备需要多长时间来响应，驱动程序都不会被重置。当你在进行 CUDA 编程时，这是最方便的设置——这也是我在本书中一直使用的设置。