CUDA 编程指南 13.4.2 · 非官方中文译本
连续阅读
原著发布于 2026 年 9 月 10 日对照 NVIDIA 英文原文对应英文网页

4.22. 驱动程序入口点访问

4.22.1. 简介

Driver Entry Point Access APIs提供了一种获取 CUDA 驱动程序函数地址的方法。从 CUDA 11.3 开始,用户可以使用从这些 API 获得的函数指针调用可用的 CUDA 驱动程序 API。

这些 API 提供的功能类似于其对应接口,即 POSIX 平台上的 dlsym 和 Windows 上的 GetProcAddress。所提供的 API 允许用户:

  • 使用CUDA Driver API.获取驱动程序函数的地址

  • 使用CUDA Runtime API.获取驱动程序函数的地址

  • 请求 CUDA 驱动程序函数的每线程默认流版本。有关更多详细信息,请参阅获取每线程默认流版本。

  • 在使用较旧工具包但配备较新驱动程序的情况下访问新的 CUDA 功能。

4.22.2. 驱动程序函数类型定义

为便于获取 CUDA 驱动程序 API 入口点,CUDA Toolkit 提供了对头文件的访问,这些头文件包含所有 CUDA 驱动程序 API 的函数指针定义。这些头文件随 CUDA Toolkit 一同安装,位于工具包的include/目录中。下表汇总了包含各个 CUDA API 头文件对应的typedefs的头文件。

表 27 CUDA 驱动程序 API 的类型定义头文件

API 头文件

API 类型定义头文件

cuda.h

cudaTypedefs.h

cudaGL.h

cudaGLTypedefs.h

cudaProfiler.h

cudaProfilerTypedefs.h

cudaVDPAU.h

cudaVDPAUTypedefs.h

cudaEGL.h

cudaEGLTypedefs.h

cudaD3D9.h

cudaD3D9Typedefs.h

cudaD3D10.h

cudaD3D10Typedefs.h

cudaD3D11.h

cudaD3D11Typedefs.h

上述头文件本身并不定义实际的函数指针,而是定义函数指针的类型定义。例如,cudaTypedefs.h为驱动程序 APIcuMemAlloc提供了以下类型定义:

typedef CUresult (CUDAAPI *PFN_cuMemAlloc_v3020)(CUdeviceptr_v2 *dptr, size_t bytesize);
typedef CUresult (CUDAAPI *PFN_cuMemAlloc_v2000)(CUdeviceptr_v1 *dptr, unsigned int bytesize);

CUDA 驱动程序符号采用基于版本的命名方案,除第一个版本外,其名称中都带有_v*扩展。当某个特定 CUDA 驱动程序 API 的签名或语义发生变化时,我们会递增相应驱动程序符号的版本号。以cuMemAlloc驱动程序 API 为例,第一个驱动程序符号名称为cuMemAlloc,下一个符号名称为cuMemAlloc_v2。在 CUDA 2.0(2000)中引入的第一个版本的类型定义是PFN_cuMemAlloc_v2000。在 CUDA 3.2(3020)中引入的下一个版本的类型定义是PFN_cuMemAlloc_v3020。

使用typedefs可以更轻松地在代码中定义具有适当类型的函数指针:

PFN_cuMemAlloc_v3020 pfn_cuMemAlloc_v2;
PFN_cuMemAlloc_v2000 pfn_cuMemAlloc_v1;

4.22.3. 获取驱动程序函数

使用驱动程序入口点访问 API 和适当的类型定义,我们可以获取指向任何 CUDA 驱动程序 API 的函数指针。

4.22.3.1. 使用驱动程序 API

驱动程序 API 要求将 CUDA 版本作为参数,以获取所请求驱动程序符号的 ABI 兼容版本。CUDA 驱动程序 API 为每个函数分别定义 ABI,并使用_v*扩展表示。例如,请看cuStreamBeginCapture的各个版本及其在cudaTypedefs.h中对应的typedefs:

// cuda.h
CUresult CUDAAPI cuStreamBeginCapture(CUstream hStream);
CUresult CUDAAPI cuStreamBeginCapture_v2(CUstream hStream, CUstreamCaptureMode mode);

// cudaTypedefs.h
typedef CUresult (CUDAAPI *PFN_cuStreamBeginCapture_v10000)(CUstream hStream);
typedef CUresult (CUDAAPI *PFN_cuStreamBeginCapture_v10010)(CUstream hStream, CUstreamCaptureMode mode);

从上述代码片段中的typedefs可以看出,版本后缀_v10000和_v10010表明上述 API 分别是在 CUDA 10.0 和 CUDA 10.1 中引入的。

#include <cudaTypedefs.h>

// Declare the entry points for cuStreamBeginCapture
PFN_cuStreamBeginCapture_v10000 pfn_cuStreamBeginCapture_v1;
PFN_cuStreamBeginCapture_v10010 pfn_cuStreamBeginCapture_v2;

// Get the function pointer to the cuStreamBeginCapture driver symbol
cuGetProcAddress("cuStreamBeginCapture", &pfn_cuStreamBeginCapture_v1, 10000, CU_GET_PROC_ADDRESS_DEFAULT, &driverStatus);
// Get the function pointer to the cuStreamBeginCapture_v2 driver symbol
cuGetProcAddress("cuStreamBeginCapture", &pfn_cuStreamBeginCapture_v2, 10010, CU_GET_PROC_ADDRESS_DEFAULT, &driverStatus);

参照上述代码片段,要获取驱动程序 APIcuStreamBeginCapture的_v1版本的地址,CUDA 版本参数应恰好为 10.0(10000)。同样,要获取该 API 的_v2版本的地址,CUDA 版本应为 10.1(10010)。指定较高的 CUDA 版本来获取驱动程序 API 的某个特定版本,可能并不总是具有可移植性。例如,在此处使用 11030 仍会返回_v2符号,但如果假设的_v3版本在 CUDA 11.3 中发布,那么当与 CUDA 11.3 驱动程序配合使用时,cuGetProcAddressAPI 就会开始转而返回较新的_v3符号。由于_v2和_v3符号的 ABI 和函数签名可能不同,使用为_v2符号设计的_v10010类型定义来调用_v3函数,会表现出未定义行为。

注意,使用无效的 CUDA 版本请求驱动程序 API 会返回错误CUDA_ERROR_NOT_FOUND。在上述代码示例中,传入小于 10000(CUDA 10.0)的版本将是无效的。

4.22.3.2. 使用运行时 API

运行时 APIcudaGetDriverEntryPointByVersion使用所提供的 CUDA 版本,以与cuGetProcAddress相同的方式获取所请求驱动程序符号的 ABI 兼容版本。在下面的代码片段中,所需的最低 CUDA 版本为 CUDA 11.2,因为cuMemAllocAsync是在该版本中引入的。

#include <cudaTypedefs.h>

int cudaVersion;
// Ensure a CUDA driver >= 11.2 is installed or we will get an error from cuGetProcAddress
status = cuDriverGetVersion(&cudaVersion);
if (cudaVersion >= 11020) {

   // Declare the entry point
   PFN_cuMemAllocAsync_v11020 pfn_cuMemAllocAsync;

   // Initialize the entry point
   cudaGetDriverEntryPointByVersion("cuMemAllocAsync", &pfn_cuMemAllocAsync, 11020, cudaEnableDefault, &driverStatus);

   // Call the entry point
   if(driverStatus == cudaDriverEntryPointSuccess && pfn_cuMemAllocAsync) {
       pfn_cuMemAllocAsync(...);
   }
}

4.22.3.3. 获取每线程默认流版本

某些 CUDA 驱动程序 API 可以配置为具有默认流或每线程默认流语义。具有每线程默认流语义的驱动程序 API,其名称以_ptsz或_ptds为后缀。例如,cuLaunchKernel有一个名为cuLaunchKernel_ptsz的每线程默认流变体。借助驱动程序入口点访问 API,用户可以请求驱动程序 APIcuLaunchKernel的每线程默认流版本,而不是默认流版本。将 CUDA 驱动程序 API 配置为具有默认流或每线程默认流语义会影响同步行为。有关更多详细信息,请参阅此处。

可以通过以下方式之一获取驱动程序 API 的默认流或每线程默认流版本:

  • 使用编译标志--default-stream per-thread或定义宏CUDA_API_PER_THREAD_DEFAULT_STREAM,以获得每线程默认流行为。

  • 分别使用标志CU_GET_PROC_ADDRESS_LEGACY_STREAM/cudaEnableLegacyStream或CU_GET_PROC_ADDRESS_PER_THREAD_DEFAULT_STREAM/cudaEnablePerThreadDefaultStream,强制采用默认流或每线程默认流行为。

4.22.3.4. 访问新的 CUDA 功能

始终建议安装最新的 CUDA 工具包来访问新的 CUDA 驱动程序功能,但如果出于某种原因,用户不想更新或无法获取最新的工具包,则可以使用此 API,仅通过更新 CUDA 驱动程序来访问新的 CUDA 功能。为便于讨论,假设用户正在使用 CUDA 12.3,并且想使用 CUDA 12.5 驱动程序中提供的新驱动程序 APIcuFoo。下面的代码片段展示了这一用例:

int main()
{
    // Manually define the prototype as cudaTypedefs.h in CUDA 12.3 does not have the cuFoo typedef
    typedef CUresult (CUDAAPI *PFN_cuFoo_v12050)(...);
    PFN_cuFoo_v12050 pfn_cuFoo = NULL;
    CUdriverProcAddressQueryResult driverStatus;
    int cudaVersion;

    // Ensure a CUDA driver >= 12.5 is installed or we will get an error from cuGetProcAddress
    CUresult status = cuDriverGetVersion(&cudaVersion);
    if (cudaVersion >= 12050) {
        // Get the address for cuFoo API using cuGetProcAddress. Specify CUDA version as
        // 12050 since cuFoo was introduced then
        CUresult status = cuGetProcAddress("cuFoo", &pfn_cuFoo, 12050, CU_GET_PROC_ADDRESS_DEFAULT, &driverStatus);

        if (status == CUDA_SUCCESS && pfn_cuFoo) {
            pfn_cuFoo(...);
        }
        else {
            printf("Cannot retrieve the address to cuFoo - driverStatus = %d\n", driverStatus);
            assert(0);
        }
    }

    // rest of code here
}

在下一个示例中,我们将讨论如何获取在 CUDA Toolkit 的某个次版本中发布的 API 新版本。注意,在 cuda.h 头文件中,将cuDeviceGetUuid提升到 _v2 的版本宏,直到跨入新的主版本时才会更新。因此,以下示例展示了在 11.4 及后续版本期间如何获取 _v2 版本。

注意,在这种情况下,原始版本(而不是 _v2 版本)的类型定义如下所示:

typedef CUresult (CUDAAPI *PFN_cuDeviceGetUuid_v9020)(CUuuid *uuid, CUdevice_v1 dev);

而 _v2 版本的类型定义如下所示:

typedef CUresult (CUDAAPI *PFN_cuDeviceGetUuid_v11040)(CUuuid *uuid, CUdevice_v1 dev);
#include <cudaTypedefs.h>

CUuuid uuid;
CUdevice dev;
CUresult status;
int cudaVersion;
CUdriverProcAddressQueryResult driverStatus;

status = cuDeviceGet(&dev, 0); // Get device 0
// handle status

// Ensure a CUDA driver >= 11.4 is installed or we will get an error from cuGetProcAddress
status = cuDriverGetVersion(&cudaVersion);
if (cudaVersion >= 11040) {
   PFN_cuDeviceGetUuid_v11040 pfn_cuDeviceGetUuid;
   status = cuGetProcAddress("cuDeviceGetUuid", &pfn_cuDeviceGetUuid, 11040, CU_GET_PROC_ADDRESS_DEFAULT, &driverStatus);
   if(CUDA_SUCCESS == status && pfn_cuDeviceGetUuid) {
      pfn_cuDeviceGetUuid(&uuid, dev);
   }
}

4.22.4. cuGetProcAddress 使用准则

以下是使用cuGetProcAddress时应牢记的准则。

  • 将传递给cuGetProcAddress的 CUDA 版本编码为与类型定义版本一致的值(不要使用CUDA_VERSION之类的编译时常量,也不要使用cuDriverGetVersion返回的版本之类的动态版本)

  • 在调用cuGetProcAddress之前,检查当前驱动程序版本(例如从cuDriverGetVersion获取的版本)是否足够,否则预计会出现错误,或可能返回非预期的符号

4.22.4.1. 运行时 API 使用准则

除非另有说明,CUDA 运行时 APIcudaGetDriverEntryPointByVersion将采用与驱动程序入口点cuGetProcAddress类似的准则,因为它允许用户请求特定的 CUDA 驱动程序版本。

4.22.5. 确定 cuGetProcAddress 失败的原因

cuGetProcAddress 存在两类错误,分别是 (1) API/使用错误和 (2) 无法找到所请求的驱动程序 API。第一类错误会通过 CUresult 返回值返回 API 的错误代码。例如,将 NULL 作为pfn变量传入,或者传入无效的flags。

第二类错误编码在CUdriverProcAddressQueryResult *symbolStatus中,可用于帮助区分导致驱动程序无法找到所请求符号的潜在问题。以下面的示例为例:

// cuDeviceGetExecAffinitySupport was introduced in release CUDA 11.4
#include <cuda.h>
CUdriverProcAddressQueryResult driverStatus;
cudaVersion = ...;
status = cuGetProcAddress("cuDeviceGetExecAffinitySupport", &pfn, cudaVersion, 0, &driverStatus);
if (CUDA_SUCCESS == status) {
    if (CU_GET_PROC_ADDRESS_VERSION_NOT_SUFFICIENT == driverStatus) {
        printf("We can use the new feature when you upgrade cudaVersion to 11.4, but CUDA driver is good to go!\n");
        // Indicating cudaVersion was < 11.4 but run against a CUDA driver >= 11.4
    }
    else if (CU_GET_PROC_ADDRESS_SYMBOL_NOT_FOUND == driverStatus) {
        printf("Please update both CUDA driver and cudaVersion to at least 11.4 to use the new feature!\n");
        // Indicating driver is < 11.4 since string not found, doesn't matter what cudaVersion was
    }
    else if (CU_GET_PROC_ADDRESS_SUCCESS == driverStatus && pfn) {
        printf("You're using cudaVersion and CUDA driver >= 11.4, using new feature!\n");
        pfn();
    }
}

第一种情况的返回代码为CU_GET_PROC_ADDRESS_VERSION_NOT_SUFFICIENT,表示在 CUDA 驱动程序中搜索时找到了symbol,但它是在所提供的cudaVersion之后才添加的。在此示例中,将cudaVersion指定为 11030 或更小的任意值,并使用版本大于或等于 CUDA 11.4 的 CUDA 驱动程序运行时,就会得到CU_GET_PROC_ADDRESS_VERSION_NOT_SUFFICIENT这一结果。这是因为cuDeviceGetExecAffinitySupport是在 CUDA 11.4 (11040) 中添加的。

第二种情况的返回代码为CU_GET_PROC_ADDRESS_SYMBOL_NOT_FOUND,表示在 CUDA 驱动程序中搜索时未找到symbol。这可能有几种原因,例如,驱动程序较旧而不支持该 CUDA 函数,或者只是存在拼写错误。在后一种情况下,与上一个示例类似,如果用户将symbol设为 CUDeviceGetExecAffinitySupport,请注意字符串开头的大写 CU,那么cuGetProcAddress将无法找到该 API,因为字符串不匹配。前一种情况的一个例子可能是,用户针对支持新 API 的 CUDA 驱动程序开发应用程序,却将应用程序部署到较旧的 CUDA 驱动程序上。以上一个示例为例,如果开发者针对 CUDA 11.4 或更高版本进行开发,但部署时使用的是 CUDA 11.3 驱动程序,那么在开发过程中,cuGetProcAddress可能曾经成功,但在部署使用 CUDA 11.3 驱动程序运行的应用程序时,该调用将不再有效,并会在driverStatus中返回CU_GET_PROC_ADDRESS_SYMBOL_NOT_FOUND。

原著 © 2007 至 2026 NVIDIA Corporation 及其关联公司。保留所有权利。
中文译本仅作学习参考,非 NVIDIA 官方出版物。技术含义有疑问时请核对NVIDIA 英文原文。
关于本译本

输入关键词搜索全书。