Paddle-Lite OpenCL后端整体架构
ysh329 opened this issue · comments
ysh329 commented
Paddle-Lite OpenCL后端主要分为如下4部分:
- CLWrapper(static):寻找设备上的动态库、检查符号、创建OpenCL API函数指针等;
- CLRuntime(static):完成OpenCL平台、设备的初始化,上下文、命令队列、
cl::Program
的创建; - CLContext:
cl::Kernel
的创建、设置LocalWorkSize、GlobalWorkSize、AutoTune等; - TargetWrapper:对
CLWrapper
的部分OpenCL API函数做框架层面的封装,如Image2D和Buffer的Malloc、Free等。
其它琐碎的地方:
- cl_image_convertor:将NCHW的数据排布,在CPU上完成Image2D的排布转换,为上传到GPU上做准备;
- cl_utility:封装EnqueueNDRangeKernel,封装mutable_data等;
- opencl_kernel_sources:包含cl kernel代码。
ysh329 commented
OpenCL的模型转换
OpenCL的Predictor创建
OpenCL的内存管理
AutoTune
cl::NDRange CLContext::DefaultLocalWorkSize(
const cl::NDRange &gws,
register size_t max_ws,
const int &divisor /*=2*/,
const bool &reverse /*=false*/,
const size_t &user_def_max_ws /*=0*/) {
register size_t lx = reverse ? gws[2] : gws[0];
register size_t ly = gws[1];
register size_t lz = reverse ? gws[0] : gws[2];
max_ws = (user_def_max_ws > 0 && user_def_max_ws <= max_ws) ? user_def_max_ws
: max_ws;
max_ws = divisor > 1 ? max_ws / divisor : max_ws;
if (max_ws > 0) {
while (ly > max_ws) {
// replace mod with bit operate
ly = (ly & 0x01) ? 1 : ly >> 1;
}
while (ly * lz > max_ws) {
lz = (lz & 0x01) ? 1 : lz >> 1;
}
while (ly * lz * lx > max_ws) {
lx = (lx & 0x01) ? 1 : lx >> 1;
}
}
return reverse ? cl::NDRange{lz, ly, lx} : cl::NDRange{lx, ly, lz};
}
精度设置API架构
背景:增加该API的目的是:在X86 MacOS和X86 Linux系统上,OpenCL设备支持FP32精度计算,同时,也是为了验证一套opencl kernel代码的计算结果是正确的(因为FP16确实有累计系统误差的问题),而非Kernel代码有bug。
精度API提供3种精度设置:
- CL_PRECISION_AUTO:默认选项,自动精度选择,优先考虑性能,选择低精度的FP16;
- CL_PRECISION_FP16:强制选择FP16执行,若设备不支持FP16,则会报错abort并退出;
- CL_PRECISION_FP32:强制选择FP32精度执行,同上。
类似AutoTune,只不过将实现的过程放在了CLRuntime中
,因为这部分需要作为build option作为参数传入opencl kernel里,子在opencl kernel内的精度用宏来表示如CL_DTYPE_float
表示定义CL_DTYPE
为float,计算精度为float,CL_DTYPE_half
同理。
ysh329 commented
Image2D的数据排布
static std::map<std::string, size_t> InitImageDimInfoWith(
const DDim& tensor_dim) {
size_t new_dims[] = {1, 1, 1, 1};
for (size_t j = 0; j < tensor_dim.size(); ++j) {
new_dims[4 - tensor_dim.size() + j] = tensor_dim[j];
}
size_t N, C, H, W;
N = new_dims[0];
C = new_dims[1];
H = new_dims[2];
W = new_dims[3];
size_t width = W * ((C + 3) / 4);
size_t height = H * N;
return std::map<std::string, size_t>({{"width", width}, {"height", height}});
}
如果维度不够4维如只有2维度,则将2维度前面补1