驱动 API:
驱动程序API在安装设备驱动程序期间在系统上复制的cuda动态库(cuda.dll或cuda.so)中实现。 它的所有入口点都以cu为前缀。
它是一个基于句柄的命令API:大多数对象都被不透明的句柄引用,这些句柄可能被指定为函数来操作对象。
表15中汇总了驱动程序API中可用的对象。
驱动程序API必须在调用驱动程序API的任何函数之前使用cuInit()进行初始化。 然后必须创建一个CUDA上下文,该上下文连接到特定的设备,并按照上下文中的详细说明向调用主机线程传输流。
在CUDA上下文中,内核通过主机代码显式加载为PTX或二进制对象,如Module中所述。 因此用C编写的内核必须分别编译到PTX或二进制对象中。 内核使用API入口点启动,如内核执行中所述。
任何想要在未来设备体系结构上运行的应用程序都必须加载PTX,而不是二进制代码。 这是因为二进制代码是特定于体系结构的,因此与未来的体系结构不兼容,而PTX代码在加载时由设备驱动程序编译为二进制代码。
以下是使用驱动程序API编写的内核示例的主机代码:
int main()
{
int N = ...;
size_t size = N * sizeof(float);
// Allocate input vectors h_A and h_B in host memory
float* h_A = (float*)malloc(size);
float* h_B = (float*)malloc(size);
// Initialize input vectors
...
// Initialize
cuInit(0);
// Get number of devices supporting CUDA
int deviceCount = 0;
cuDeviceGetCount(&deviceCount);
if (deviceCount == 0) {
printf("There is no device supporting CUDA.\n");
exit(0);
}
// Get handle for device 0
CUdevice cuDevice;
cuDeviceGet(&cuDevice, 0);
// Create context
CUcontext cuContext;
cuCtxCreate(&cuContext, 0, cuDevice);
// Create module from binary file
CUmodule cuModule;
cuModuleLoad(&cuModule, "VecAdd.ptx");
// Allocate vectors in device memory
CUdeviceptr d_A;
cuMemAlloc(&d_A, size);
CUdeviceptr d_B;
cuMemAlloc(&d_B, size);
CUdeviceptr d_C;
cuMemAlloc(&d_C, size);
// Copy vectors from host memory to device memory
cuMemcpyHtoD(d_A, h_A, size);
cuMemcpyHtoD(d_B, h_B, size);
// Get function handle from module
CUfunction vecAdd;
cuModuleGetFunction(&vecAdd, cuModule, "VecAdd");
// Invoke kernel
int threadsPerBlock = 256;
int blocksPerGrid =
(N + threadsPerBlock - 1) / threadsPerBlock;
void* args[] = { &d_A, &d_B, &d_C, &N };
cuLaunchKernel(vecAdd,
blocksPerGrid, 1, 1, threadsPerBlock, 1, 1,
0, 0, args, 0);
...
}
上下文:
CUDA上下文类似于CPU进程。 在驱动程序API中执行的所有资源和操作都封装在CUDA上下文中,并且在上下文被销毁时系统会自动清除这些资源。 除了诸如模块和纹理或表面引用之类的对象外,每个上下文还有其自己独特的地址空间。 因此,来自不同上下文的CUdeviceptr值引用不同的内存位置。
主机线程一次只能有一个设备上下文。 当使用cuCtxCreate()创建一个上下文时,它对调用主机线程是最新的。 在上下文中运行的CUDA函数(大多数函数不涉及设备枚举或上下文管理)将返回CUDA_ERROR_INVALID_CONTEXT,如果有效的上下文对于线程不是当前的。
每个主机线程都有一堆当前上下文。 cuCtxCreate()将新的上下文推送到栈顶。 可以调用cuCtxPopCurrent()来从主机线程分离上下文。 上下文然后“浮动”,并可以作为任何主机线程的当前上下文。 cuCtxPopCurrent()还恢复以前的当前上下文(如果有的话)。
还会为每个上下文维护使用次数。 cuCtxCreate()创建一个使用次数为1的上下文。cuCtxAttach()增加使用次数,cuCtxDetach()减少使用次数。 当调用cuCtxDetach()或cuCtxDestroy()时,当使用计数变为0时,上下文将被销毁。
使用次数有助于在同一上下文中运行的第三方授权代码之间的互操作性。 例如,如果加载三个库以使用相同的上下文,则每个库都会调用cuCtxAttach()来增加使用次数,cuCtxDetach()会在使用上下文完成库时减少使用次数。 对于大多数库,预计应用程序将在加载或初始化库之前创建一个上下文; 这样,应用程序就可以使用自己的启发式创建上下文,并且该库仅根据传递给它的上下文进行操作。 如图19所示,希望创建自己的上下文的库(它们的API客户端不知道可能创建了自己的上下文)将使用cuCtxPushCurrent()和cuCtxPopCurrent()。