code:https://github.com/OAID/Tengine
图片,代码都来自以上项目。
1. 简介
Tengine 由 OPEN AI LAB 主导开发,该项目实现了深度学习神经网络模型在嵌入式设备上的快速、高效部署需求。为实现在众多 AIoT 应用中的跨平台部署,本项目使用 C 语言进行核心模块开发,针对嵌入式设备资源有限的特点进行了深度框架裁剪。同时采用了完全分离的前后端设计,有利于 CPU、GPU、NPU 等异构计算单元的快速移植和部署,降低评估、迁移成本。
2. Content
因为Tengine是由C语言开发的,和ncnn,mnn等用C++开发的框架有一定的区别。因为只能用函数,所以op,serializer(模型解析器)等的注册都是要在程序的时候进行的。而且对op,serializer等组织需要使用函数指针来模拟C++的多态性。
这里对op的注册机制进行解读。
首先给出数据结构之间的关系图:
2.1 流程
这里只是对op_method
和op_name
的注册梳理,以absval
的注册为例
- 定义好
absval
的op_method
和op_name
struct method m;
m.version = 1;
m.init = init_op; // 函数指针
m.release = release_op; // 函数指针
return register_op(OP_ABSVAL, OP_ABSVAL_NAME, &m);
- 初始化
static vector_t* internal_op_method_registry
和static vector_t* internal_op_name_registry
这里可以就着上面的关系图进行梳理
这里最外面的容器是vector_t
, 然后vector_t
中mem
才是指向注册数据的位置。mem
指向8个vector_entry_t
空间(初始为8个,如果不够了按8个递增申请),valid
指示数据是否写入,这里的unsigned char data[]
指明method
,op_name
在内存的初始位置。后面在使用的时候,就在internal_op_method_registry
和internal_op_name_registry
去找。
vector_t* v = (vector_t*)sys_malloc(sizeof(vector_t));
if (v == NULL)
{
return NULL;
}
v->elem_num = 0;
v->elem_size = elem_size;
v->free_func = free_data;
v->entry_size = align(elem_size + (int)sizeof(vector_entry_t), TE_VECTOR_ALIGN_SIZE); // 32, 要写入的数据 字节数
v->ahead_num = 8; // 为什么有8个空间, 以8的倍数进行拓展
v->space_num = v->ahead_num;
v->real_mem = sys_malloc(v->entry_size * v->space_num + TE_VECTOR_ALIGN_SIZE); // 32*8+8
v->mem = align_address(v->real_mem, TE_VECTOR_ALIGN_SIZE); // align = 8, 对申请的内存进行 8 字节对齐
for (int i = 0; i < v->space_num; i++)
{
vector_entry_t* e = get_vector_entry(v, i); // 前面放 vector_entry_t
e->valid = 0; // 是否写入的标志位
}
return v;
- 注册
op_name
下面的 push_vector_data , 会用 memcpy 把栈上的数据内容 copy 到已申请的堆内存上memcpy(e->data, data, v->elem_size);
ir_op_name_entry_t op_map;
op_map.type = type;
op_map.name = name;
return push_vector_data(internal_op_name_registry, &op_map); // 写入数据,如果
- 同理注册
method
这里在注册前会internal_op_method_registry
遍历,看是否已经注册
static int register_op_registry(ir_method_t* method)
{
if (find_op_method(method->type, method->version)) // 遍历检查是否已经注册
{
return -1;
}
return push_vector_data(internal_op_method_registry, method);
}
总结
- 总结起来就是把各个
op
的ir_method_t
数据保存到internal_op_method_registry
,ir_op_name_entry_t
数据保存到internal_op_name_registry
- 对照着上面的数据关系图来梳理