一、说明
在学习了CUDA编程中的Driver API和Runtime API后,一定会有这种想法,能不能够将二者结合起来,适应更广泛的应用开发的需求。这个肯定是可以的,别说它是CUDA自己的技术框架内容,就是不是,也会有极客将其搞在一起,这样的例子比比皆是(最常见的就是某些数据库的工具软件)。
CUDA提供混合编程,其实就是允许Driver API和Runtime API在开发时可以互操作。
二、混合编程的流程
CUDA作为一个编程框架,为Driver API和Runtime API在开发中的混合编程提供工作机制,它既可以侧重于驱动API为主也可以侧重于运行时API为主。
其关键的工作流程为:
- 最好使用Driver API进行初始化并创建上下文
- 使用cuCtxGetCurrent(&ctx)获取当前上下文并共享使用
- 通常情况下,使用驱动API来加载模块和获取函数,然后使用驱动API或运行时API启动内核
- 需要明白二者一些接口函数的和返回值处理的不同,比如错误的处理和内存的分配接口等等
除此之外,可以方便的使用Runtime API,一般来说,它是很安全的。如果有一些需要使用Driver API的开发工作时,可以再灵活的处理相关的任务和接口。
如果开发者能够熟练的掌握这种开发机制,那么就能够做到既有Runtime的安全方便又有Driver的深入底层的控制能力。
三、混合编程的注意点
大家都明白,事情往往不是简单的一加一等于二,处理不好则可能一加一小于二。所以在混合编程中需要注意以下几点:
- 注意接口初始化的顺序
一般来说,要首先对驱动API进行初始化,然后再调用运行时的API - 注意上下文的管理
二者开发的目的是协同完成问题的解决,而不是各自为战,所以目标指向必须是相同的即上下文要一致,特别是在多线程中,更是如此 - 内存的管理
和C/C++中的内存管理一样,要保持内存管理的接口的一致性,即用哪个创建就必须使用哪个回收而不能交叉使用。当然,如果能够进行封装或自动调用框架内的相关上下文管理来进行处理更佳 - 其它资源的管理
其它资源亦是如内存管理相同,要特别注意一些细节,防止出现意外情况 - 错误和异常的处理
这个比较简单,只必须二者处理的机制不同。但要防止对错误结果的处理不匹配
四、例程
下面看一个驱动API与运行时API调用的例程:
#include <cuda.h>
#include <cuda_runtime.h>
#include <iostream>
int main() {
// driver api init
CUresult ret = cuInit(0);
if (ret != CUDA_SUCCESS) {
std::cerr << "cuInit err!" << std::endl;
return -1;
}
CUdevice dev;
ret = cuDeviceGet(&dev, 0);
// create context
CUcontext context;
ret = cuCtxCreate(&context, 0, dev);
// call runtime api
float *data;
cudaError_t runtimeRet = cudaMalloc(&data, 1024 * sizeof(float));
if (runtimeRet != cudaSuccess) {
std::cerr << "cudaMalloc err!" << std::endl;
}
//release
cudaFree(data);
cuCtxDestroy(context);
return 0;
}
主要是用来介绍一下调用的方式,而非为业务内容。
五、总结
通常来说“既要又要”不是一种常态的情况。但在某些场景下,为了达到实际的目的,可以利用一些灵活的方式来达到这种目的。但这也对开发者提出了更高的要求,不但要对两种开发方式都相当的理解还要融会贯通的掌握两种方式编程的特点,并且能够巧妙的在二者之间进行上下文和资源的动态管理。
一句话,要得越多,付出的成本也就越高。

133

被折叠的 条评论
为什么被折叠?



