3.11_CUDA运行时与CUDA驱动程序API
3.11 CUDA运行时与CUDA驱动程序API
CUDA运行时提供了方便的语言集成功能,可以使程序很轻松的从零开始。通过自动进行像初始化上下文或加载模块这样的任务,并且用其他的C++代码以内联方式启动内核。CUDART使开发者专注于怎样使代码工作的更快。少数的CUDA抽象,像CUDA模块,在CUDART中并不可用。
另一方面,驱动程序API暴露了所有的CUDA抽象,使开发者可以在应用程序中操作它们。驱动程序API没有提供任何性能优势。但它为应用程序提供了显式的资源管理,这在需要资源管理的应用程序中十分重要,例如拥有插件架构的大规模商业应用。
驱动程序API的运行速度并不比CUDA运行时显著提升,如果你正在寻找改进CUDA应用程序性能的方法,请到其他章节去找。
CUDA的大部分特性对CUDA运行时和驱动程序API都可使用,只有几个例外。表3-9给出了详细信息:
表3-9 CUDA运行时vs驱动程序API
在两种API之间,像内存复制这样的操作趋向于功能性相同,但是接口却全然不同。但流API是几乎相同的。
事件API有一些细微的区别,如果开发者想要指定一个标志词(创建一个阻塞事件时需要),CUDART提供了一个独立的CUDAEventCreateWithFlags()函数。
内存复制函数的接口大部分都是不同的,尽管它们的实际功能是相同的。CUDA支持3种类型的内存——主机内存、设备内存、CUDA数组——内存复制发生在它们的全排列间,并可以发生在1D、2D或3D三种结构上。所以内存复制函数要么包括大量的不同函数,要么包括小数量却能支持不同内存类型的函数。
CUDA中的最简单的内存复制是主机与设备内存间的复制,但是这些函数接口仍然大不相同:CUDART使用void既代表主机指针又能表示设备指针,并且一个内存复制函数接受一个目标参数;而驱动程序API使用void代表主机内存,CUdeviceptr代表设备内存,3个独立的函数(cuMemcpyHtoD()、cuMemcpyDtoH()和cuMemcpyDtoD())对应不同的复制目标。下表给出CUDART和驱动程序API主机与内存间的相关参数列表:
对于2D和3D内存复制,驱动程序API实现了一批接受一个描述结构体并支持所有类型的内存复制的函数,这也支持低维内存复制。例如,如果需要,cuMemcpy3D()可以代替cuMemcpyHtoD()执行1D主机设备间的内存复制:
CUDA_MEMCPY3D cp = {0};
cp.dstMemoryType = CU_MEMORYTYPE_DEVICE;
cp.dstDevice = dptr;
cp.srcMemoryType = CU_MEMORYTYPE_HOST;
cp.srcHost = host;
cp.WidthInBytes = bytes;
cp.Height = cp.Depth = 1;
status = cuMemcpy3D(&cp);CUDART使用描述结构体的组合来描述复杂的内存复制函数(例如,CUDAMemcpy3D()),同时使用不同的函数实现不同内存类型的复制。如同cuMemcpy3D()函数,CUDART中的CUDAMemcpy3D()函数可以接受一个描述结构体来描述内存复制的双方,也包括跨维度内存复制(执行1D与2D CUDA数组中的一列之间的复制,或执行2D CUDA数组与3D CUDA数组一个切片之间的复制)。它的描述结构仅有微小改变,这个改变在于另外嵌入了其他结构体。表3-10给出了两个API的3D复制结构体的对照表。
表3-10 3D内存复制结构体
structudaMemcpy3DParams
{
structudaArray *srcArray;
structudaPos srcPos;
structudaPitchedPtr srcPtr;
structudaArray *dstArray;
structudaPos dstPos;
structudaPitchedPtr dstPtr;
structudaExtent extent;
enumudaMemcpyKind kind;
};
structudaPos
{
size_t x;
size_t y;
size_t z;
};
structudaPitchedPtr
{
void *ptr;
size_t pitch;
size_t xsize;
size_t ysize;
};两者3D内存复制的用法是相似的。它们均被设计为零初始化,并且开发者需要对给定的操作设置需要的成员。例如,执行主机到3D数组间的复制可以按下述步骤执行:
structudaMemcpy3DParams cp = {0};
cp.srcPtr.ptr = host;
cp.srcPtr.pitch = pitch;
cp.dstArray = hArray;
cpextent.width = Width;
cpextent.height = Height;
cpextent.depth = Depth;
cp.kind =udaMemcpyHostToDevice;
status =udaMemcpy3D(&cp);对于一个覆盖整个CUDA数组的3D复制,源和目标地址偏移量在第一行被设置为0,且只被引用一次。不同于函数的参数,代码只需要引用复制操作需要的参数,而且如果程序执行超过一个相同的操作(例如填充不止一个设备内存区或CUDA数组),这个描述结构体可被复用。