前言
我是去年5月份开始接触tvm的,再加上各位大佬都非常活跃,tvm已经更新了N个版本。这是本人在tvm学习中的一些总结,能力有限比较浅显,如有大佬发现错误,请及时指出,同时欢迎大佬们交流。
关于tvm是什么,以及安装之类的问题,网上已经有很全的信息,这里推荐蓝色大佬的文章,以及tvm的官方网址(ps:幸好当初看的第一篇tvm的文章就是蓝大的,少走了很多弯路,在这里表示感谢>.<):
Redirecting…tvm.ai 蓝色:手把手带你遨游TVMzhuanlan.zhihu.com再贴一个源码github地址
https://github.com/apache/incubator-tvmgithub.com代码库结构
首先是代码结构,有如下几个目录:
现在介绍每个目录的作用。
3rdparty是第三方库的实现,包括以前的Halide IR,但为了代码结构更加合理,现在大部分都集成到其他地方
cmake包含了所有的编译配置文件
docs 是相关的文档,官方文档在这里都能找到,是学习tvm的一个重要途径
include 是C++代码的头文件目录
jvm 是java相关的文件夹
nnvm 是中间的nnvm所在的目录,现在用处不大
python 是python文件所在的目录,所有与python相关的都在该目录中
tests 是测试文件,包含了作者写的很多测试,是学习tvm的另一个手段
topi 是tvm算子库,是为了让tvm可以同时手工定制和自动优化计算Kernel函数
1tutorial 是官网上相关的教程,同样是学习的好途径
vta 是tvm的软件栈,是一个类似tpu的硬件架构
rust apps conda docker golang web verilog 都是特有领域中的内容,对一般项目没有影响
src 是全部的C++代码,很多主要功能实现都在这里面,包括codegen,runtime等等,下面会对这个文件夹继续说明
runtime: 最小运行时代码。(说实话,我也查阅了一些资料,但是对于runtime这个概念还不是很明白,希望有大佬指导)
node: IR/AST 节点
ir: 通用ir基础架构
tir: 张量级ir
te: DSL张量表达式
arith: 算术表达式
relay: Relay IR,NNVM IR的更高级别
autotvm: 自动优化的tvm
api: API 函数注册.
driver: 编译驱动程序API
添加新的后端
上面大概介绍了代码库的结构,下面开始进入主题,添加一个新的后端。
tvm现在支持的后端有llvm,cuda,opencl,nvptx等等。
tvm有一个很重要,同时也十分有意思的特点,就是Python和c++互相调用的机制——PackedFunc。PackedFunc是一个实现C ++和Python互操作方案。
提供了一个最小化的C语言API,可以通过C语言API把PackedFunc嵌入到任何编程语言中。TVM函数通过PackedFunc暴露接口给前端语言,已经被编译的模块也通过PackedFunc返回已编译的函数。
想要具体了解可以进tvm教程查看。
tvm在Python层提供了相关的设备接口,然后使用tvm.build真正的编译,所以首先在Python端进行添加。
1,文件路径:tvm/python/tvm/target.py,这个类中定义了有关target,我们将新添加的后端命名为rpu。下面这个函数的作用是两点,一个是字符串的拼接——_merge_opts;
一个是target的创建——TargetCreate。
def rpu(model='unknown', options=None):
"""Returns a rpu target.
Parameters
----------
model: str
The model of rpu device
options : str or list of str
Additional options
"""
opts = _merge_opts(['-model=%s' % model], options)
return _api_internal._TargetCreate("rpu", *opts)
2,在 python/tvm/runtime/ndarray.py 添加关于rpu的声明。
def rpu(dev_id=0):
""
dev_id : int, optional
The integer device id
Returns
-------
ctx : TVMContext
The created context
"""
return TVMContext(12, dev_id)
3,在python/tvm/runtime/__init__.py 下添加上面加的名称。
from .ndarray import vpi, rocm, opengl, ext_dev, micro_dev,rpu
4,在python/tvm/_ffi/runtime_ctypes.py添加rpu的两个掩码。
STR2MASK= {'rpu': 13, } MASK2STR= {13: 'rpu', }
Python端添加完毕了,接下来添加C++端。
- runtime部分
1,在/src/runtime/http://module.cc的RuntimeEnabled函数添加新设备的名字支持。
else if (target == "rpu") {
f_name = "device_api.rpu";
}
2,还需要新建一个rpu目录,里边存放ModuleNode、Workspace等支持,主要模仿已有的其他后端实现进行。(ps:这一块没有弄明白,暂时先放着)
- 代码生成部分
1,首先在include/tvm/target/target.h添加rpu用以构造Target实例
TVM_DLL Target rpu(const std::vector<std::string>& options =
std::vector<std::string>());
2,在include/dlpack/dlpack.h中把rpu加入设备枚举类型,为后面target的创建做准备。
kDLRpu = 12,
同时在include/tvm/runtime/device_api.h补充对kDLRPU的支持
case kDLRPU: return "rpu";
3,在src/target/http://target.cc中添加CreateTarget的支持,以便tvm.build()传进来的target字符串能够被识别。
Target rpu(const std::vector<std::string>& options) {
return CreateTarget("rpu", options);
}
4,在CreateTarget函数里添加对rpu的判断。
else if (target_name == "rpu") { t->device_type = kDLRPU;}
5,最重要的代码生成部分可以参考其他后端。
我假定添加的后端拥有LLVM工具链的支持,所以可以复用大部分http://codegen_llvm.cc的代码,我的操作是调用LLVM的init方法以及AddFunction这两个主要方法。详细的代码等验证正确性之后再上传