OpenCL加速模块原型设计

原本想要按照标准的步骤执行,但是不太符合我的风格,调整之后,按照辅助编码为优先的原则,来进行设计。本质上适合自己的才是最好的。

目标和需求

  1. 利用OpenCL加速前后处理过程。
  2. 兼容已经存在的CPU处理过程。(回退机制暂时用不上)
  3. 需要合理的kernel source存储、加载、缓存机制。
  4. 可扩展性、类型自检、报错机制。
  5. 基本原则: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();
}