在转换过程中也必然涉及到TVM OP的2方面的工作:所有TVM Node OP的Expression通常是以命名为Make##OpName函数实现,比如MakeMaxPool2D;TVM继承了Halide的思想,将OP计算形式与计算调度分开(类似于逻辑计划与物理计划的分离),所以TVM OP的实现过程由Compute与Schedule两部分组成。
下面以conv2D为例,讲解其在TVM中的具体实现。
TVM解析TF Modle会将Conv2D OP映射为 _conv(‘conv‘),如上图红色部分为执行_conv(‘conv’)函数后新增表达式,_conv()函数实现大致如下:
![69615cf6988def9416c5ab9ffa47f7f2.png](https://i-blog.csdnimg.cn/blog_migrate/f4bb6e1776a3a470b0f0ab1a57533560.jpeg)
执行结果示例1:
![cf17de1bfca018fc1be6c0df6e931809.png](https://i-blog.csdnimg.cn/blog_migrate/3bb937afa3bfc156e591d633aeb7b7e3.jpeg)
执行结果示例2:
![71c5972ace98b1c8e99cf372013fb512.png](https://i-blog.csdnimg.cn/blog_migrate/adb9f36569c2b42346b7639c62c150d7.jpeg)
直接关注卷积的实现,其中_dimension_picker('conv')的作用是根据卷积维度返回新的OP名称,这里返回值为”conv2d”。而AttrCvt类构造及调用实现如下:
![29050d4fcb933c55d1b277fc8a299c6c.png](https://i-blog.csdnimg.cn/blog_migrate/3f650969e474ee9032784ad606c10702.jpeg)
在get_relay_op函数中如果op_name指定目录,则从指定目录中查找函数实现,如果没有指定目录,则依次从op目录、op/nn目录、op/image目录、op/vision目录查找。
![cc1c5753539ba2cc8dad2f98d350db51.png](https://i-blog.csdnimg.cn/blog_migrate/666822be872fd61c12dd3b3cf49020a8.jpeg)
conv2d返回的函数位置为nn.conv2d,其表达式生成函数为_make.conv2d。
![f6dd9e03d9fd4178248dc3c947885567.png](https://i-blog.csdnimg.cn/blog_migrate/0614fab1df278dea9ecc6eab1ce38a0c.jpeg)
对应_make.conv2d函数的设计如下:
![6ad2efeed3cccea081355d96133de990.png](https://i-blog.csdnimg.cn/blog_migrate/420496e6ca4b34584bd3f91bf7600a39.jpeg)
注意上述CallNode::make(…)函数生成TVM Node,Node能够转换为索引表达式形式,其实现如下:
![dc9996c2dbf7489dd72ebae1ddd1b2e4.png](https://i-blog.csdnimg.cn/blog_migrate/00f724ad07a1f6fc65062a9ec2e1a436.jpeg)
Conv2d的compute与schedule的实现代码如下:
![7253dc2191493a48764ba5263fff20cd.png](https://i-blog.csdnimg.cn/blog_migrate/0fd9a96061f0c8278abc3f416ac7352a.jpeg)
![b9cf9d68ee592dabf665527920b5465d.png](https://i-blog.csdnimg.cn/blog_migrate/c931a049cb9947d4c633b73dcd1234f8.jpeg)
conv2d compute之一 conv2d_nchw实现示例:
![433bc70c7f61e079658731895b555148.png](https://i-blog.csdnimg.cn/blog_migrate/cfce5ec4512e67e9b803d8c63a463e06.jpeg)
其对应的schedule为:
![b4da1ffbbfafdbaca9376566086c709d.png](https://i-blog.csdnimg.cn/blog_migrate/03121a1571d89ff8bbac0dd96044ee06.jpeg)
如何优化schedule?TVM提供了如下的方法:Blocking、Vectorization、Loop Permutation、Array Packing、Write cache for blocks、Parallel等,合理地使用这些优化方法可以获得更好的计算性能。详见《How to optimize GEMM on CPU》https://docs.tvm.ai/tutorials/optimize/opt_gemm.html
显然对于不同的硬件平台,TVM均提供了相应的compute及schedule代码。在Frontend处理阶段,DL的conv2D操作被映射为nn.conv2d。在Backend的codegen阶段,TVM设计了一套dispatch机制,可以将topi.nn.conv2d dispatch到目标硬件平台的分支,比如cuda平台,最终会调用到conv2d_cuda()。调用堆栈如下:
![0d1b04ac7b83c718a63ef8f7802365e6.png](https://i-blog.csdnimg.cn/blog_migrate/577df5bb7ad5df502e4bfd5ab1940183.jpeg)
其实现机制在后继Backend章节讲解。