OpenCL加速模块原型设计
原本想要按照标准的步骤执行,但是不太符合我的风格,调整之后,按照辅助编码为优先的原则,来进行设计。本质上适合自己的才是最好的。
目标和需求
- 利用OpenCL加速前后处理过程。
兼容已经存在的CPU处理过程。(回退机制暂时用不上)- 需要合理的kernel source存储、加载、缓存机制。
- 可扩展性、类型自检、报错机制。
- 基本原则:GPU过程中尽量不要掺杂CPU过程,内存频繁拷贝对性能影响很大。
基本架构
思考
各个模块和各个类:
- 仍然考虑使用单例和工厂模式?
- KernelWrapper:是对每个特定算子的封装,可以进行重载(针对不同输入类型),直接从参数列表上限定输入数量和类型,同时会在内部进行参数类型检测,从特定kernel进行创建。
- ContextManager:是对所有OpenCL资源的封装,提供program的预编译和缓存策略。
- function直接调用ctx获取对应的kernelwrapper,随后准备输入,调用对应kernel。
缓存准备怎么搞,从哪里初始化:
如果是每个模块独立program的情况下,单独做缓存(因为没办法统一收集所有的kernel source,然后做,否则要register?),通用kernel统一做缓存。
算子做register,虽然不用创建,但是所有kernel代码还是放到头文件中,string类型本身就会占用内存,创建program也会消耗内存。
特化算子放到对应调用的模块bundle中,通用算子放到统一的opencl bundle中。
特化算子独立编译,通用算子独立编译,目前一共是两个program。
特化算子不用说,肯定是要编译的,并且特化算子的执行函数跟着模块走。
每个算子一个独立的缓存?KernelWrapper负责缓存和加载缓存。然后KernelWrapper从ContextManager中获取,每个Kernel都需要进行构建,他们都会有相应的函数。
暂时不考虑使用KernelWrapper了。
通过bundle的形式和通过头文件的形式目前唯一的差距就是库大小了。
主要模块
classDiagram
class ContextManager {
cl::Context _context
cl::Device _device
cl::CommandQueue _command_queue
cl::Program _program
cl::Buffer createBuffer()
cl::Kernel getKernel()
cl::Context context()
cl::Device device()
cl::CommandQueue command_queue()
}classDiagram
class KernelWrapper {
std::string _kernel_source
cl::Kernel _kernel
void create()
void operator()(Args...)
}// 实际执行的function,这里主要是
Status warpaffine(ContextManager ctx, Args... args) {
// Get the kernel from ctx.
KernelWrapper kernel = ctx->getKernel();
// 这里和之前的差异就是拿到的是KernelWrapper而不是cl::Kernel
// KernelWrapper是对opencl kernel的封装,参数都是特定类型,并且参数数量也特定
// 这样的好处是:外部调用更加清晰,明确知道需要哪些参数,而不用再回去看opencl代码
//
kernel(args);
return Status::OK();
}