TVM_深度学习编译器

TVM_深度学习编译器

TVM所做的是要比传统compiler更偏上层的,你可以把它理解成source-to-source compiler,需要其他的后端(backend)来生成最后的指令。比如当编译的Target是Intel CPU时,翻译的顺序是Relay IR -> TVM IR/ Halide IR -> LLVM IR,之后交给LLVM生成最后的可执行程序。当编译的Target是NVIDIA GPU时,这个顺序就变成了 Relay IR -> TVM IR/Halide IR -> CUDA program (比如生成一个文件, my_kernel.cu),然后调用cuda的runtime compiler来生成CUDA可执行文件。

细节上可能跟最新的不一样,因为TVM大概在逐渐废弃Halide IR,TVM IR也是每个版本都有改动。从前后端的角度上看,vitis AI可以作为TVM的后端之一,而且不久前,TVM也支持了这个后端([RFC][BYOC] Vitis-AI integration),具体支持的如何,我没有试过。TVM的核心优势在于从Halide等框架借鉴来的算子描述与调度分离,TVM IR侧重于loop nest来做loop transformation。vitis从官网上看,可以做剪枝量化这些,之后把算子分派到具体的执行单元。

TVM + vitis AI backend的话,从RFC来看,大概是先在TVM这一层做优化,然后翻译到vitis的graph IR。但是我很好奇,如果我做tiling,这个tiling size在底层没有支持,它怎么翻译?还是说这个RFC所做的只是Relay graph optimization,比如算子融合,constant folding这类,然后直接转换到vitis。

从你描述来看,你的板子是定制的,这个翻译过程应该会更麻烦一些,大概需要对TVM的后端进行扩展,或者写tensor intrinsic之类。我魔改TVM做的比较多,写扩展的难度一般般,不算太复杂。我觉得你可以先用vitis AI成功部署之后(vitis支不支持自己设计的架构我也不知道, Orz…),再考虑借助TVM做一些别的优化。你的板子是定制的,从TVM入手的话,感觉怎么都得回到后端生成的这个问题上来。

把你自己的代码生成TVM

tvm.d2l.ai 笔记

tvm.schedule 自动 代码 生成 调度

tvm在线文档

在TVM中添加新设备Codegen

tvm会议

课程资料

在android上安装和运行tvm

TVM 使用示例

# 矩阵赋值import tvmA = tvm.placeholder((10, 10))
B = tvm.compute((10, 10), lambda i, j: A[i, j])
s = tvm.create_schedule(B.op)     # 调度
f = tvm.build(s, [A, B], "hello") # 编译生成代码
print(f.get_source())             # 打印生成的代码

生成的代码

// hello tvm backend!
void default_function( void* args,  void* arg_type_ids, int32_t num_args,  void* out_ret_value,  void* out_ret_tcode) {// ......float* placeholder = (float*)(((DLTensor*)arg0)[0].data);// ......float* compute = (float*)(((DLTensor*)arg1)[0].data);// ......for (int32_t i = 0; i < 10; ++i) {for (int32_t j = 0; j < 10; ++j) {compute[((i * 10) + j)] = placeholder[((i * 10) + j)]; // 矩阵赋值}}
}

相关TVM API

  • tvm.placeholder((n,), name=‘a’)

    • def placeholder(shape, dtype=None, name=“placeholder”)
    • 作用:类似 tf.placeholder,占位符,后续可指定输入数据。
  • tvm.compute(A.shape, lambda i: A[i] + B[i], name=‘c’)

    • def compute(shape, fcompute, name=“compute”, tag=“”, attrs=None)
    • 作用:定义计算图。 fcompute 是 lambda function of indices-> value,匿名函数。
  • tvm.create_schedule(C.op)

    • def create_schedule(ops)
    • 作用:为一系列Ops构建默认Schedule。
  • tvm.lower(s, [A, B, C], simple_mode=True)
    * def lower(sch, args, name=“default_function”, binds=None, simple_mode=False)

    • 作用:输入schedule对象和输入输出tensor list。
    • 如果 simple_mode 为True,则输出simple and compact statement(起始就是C语言形式的伪代码,表示op操作具体过程)。
    • 如果 simple_mode 为False,我也不知道输出啥。
  • tvm.build(s, [A, B, C])

    • def build(inputs, args=None, target=None, target_host=None, name=“default_function”, binds=None)
    • 通过输入Schedule等参数,构建可运行的module。
  • mod.export_library(mod_fname) 与 tvm.module.load(mod_fname)

    • def export_library(self, file_name, fcompile=None, **kwargs)
    • def load(path, fmt=“”)
    • 作用:将 tvm.module.Module 导出为本地文件,或将本地文件导入生成 tvm.module.Module 对象。
  • relay.frontend.from_mxnet(model, {‘data’: x.shape})

    • def from_mxnet(symbol, shape=None, dtype=“float32”, arg_params=None, aux_params=None)
    • 作用:将MXNet模型转换为TVM Relay模型。
  • with relay.build_config(opt_level=3)

    • def build_config(opt_level=2, fallback_device=_nd.cpu(), required_pass=None, disabled_pass=None):
    • 作用:为 relay.build 设置一些参数,比如图优化等。
  • tvm.context(target)

    • def context(dev_type, dev_id=0)
    • 作用:为给定的设备设置运行环境。
  • tvm.contrib.graph_runtime.create(graph, mod, ctx)

    • def create(graph_json_str, libmod, ctx)
    • 作用:通过 graph/module/ctx 创建实际可执行模块,之后导入输入数据、获取模型预测结果都通过这个方法返回的对象。
    • 举例:rt.set_input, rt.run, rt.get_output 等。
  • relay.save_param_dict(params)

    • def save_param_dict(params)
    • 作用:保存模型参数。
  • remote = rpc.connect(‘172.31.0.149’, 9090)

    • def connect(url, port, key=“”, session_timeout=0)
    • 作用:建立远程rpc连接,方便交叉编译等功能。
    • 该remote对象有很多功能:
    • remote.upload(mod_fname):上传文件
    • remote.load_module(mod_fname):导入模块
    • remote.cpu():建立远程context对象

tvm 调用分析

TVM代码走读(九) 计算和调度

import tvm
from tvm import te
import numpy as npA = te.placeholder((m, n), name='A')
B = te.placeholder((m, n), name='B')
C = te.compute((m, n), lambda i, j: A[i, j] * B[i, j], name='C')

compute python 接口 python/tvm/te/operation.py def compute(shape, fcompute, name=“compute”, tag=“”, attrs=None): 函数

def compute(shape, fcompute, name="compute", tag="", attrs=None):"""Construct a new tensor by computing over the shape domain.The compute rule is result[axis] = fcompute(axis)Parameters----------shape: Tuple of ExprThe shape of the tensorfcompute: lambda function of indices-> valueSpecifies the input source expression计算函数name: str, optionalThe name hint of the tensortag: str, optionalAdditional tag information about the compute.attrs: dict, optionalThe additional auxiliary attributes about the compute.Returns-------tensor: TensorThe created tensorif isinstance(body, _tensor.TensorIntrinCall):for i, s in enumerate(shape[out_ndim:]):var_name = "ax" + str(i)dim_var.append(tvm.tir.IterVar((0, s), var_name, 4))op_node = _ffi_api.TensorComputeOp(name,tag,dim_var,body.reduce_axis,out_ndim,body.intrin,body.tensors,body.regions,body.scalar_inputs)else:if not isinstance(body, (list, tuple)):body = [body]body = convert(body)op_node = _ffi_api.ComputeOp(name, tag, attrs, dim_var, body)

调用链 python/tvm/te/operation.py [def compute() 函数 ] —> _ffi_api.TensorComputeOp() —> src/te/operation/compute_op.cc ComputeOp::ComputeOp()

ComputeOp::ComputeOp(std::string name, std::string tag, Map<String, ObjectRef> attrs,Array<IterVar> axis, Array<PrimExpr> body) {if (!attrs.defined()) {attrs = Map<String, ObjectRef>();}auto n = make_object<ComputeOpNode>();n->name = std::move(name);n->tag = std::move(tag);n->attrs = std::move(attrs);n->axis = std::move(axis);n->body = std::move(body);if (n->body[0]->IsInstance<tir::ReduceNode>()) {const tir::ReduceNode* reduce = n->body[0].as<tir::ReduceNode>();n->reduce_axis = reduce->axis;}VerifyComputeOp(n.get());data_ = std::move(n);
}

te.compute可以总结为如下几步:

根据传入的fcompute,翻译成对应的表达式传入.
生成ComputeOpNode,记录计算节点.
根据计算节点,返回计算节点输出对应的Tensor.

TVM 中 Relay 涉及到的 Pass 优化操作

Concept

一种 Pass 代表一种优化。比较多在 LLVM 中提到。

ModulePass是将整个程序视作一个单元处理的pass。

FunctionPass是以单个函数为作用域的pass, 每个函数间是相互独立的。

Pass

high-level pass:

header file: include/tvm/relay/transforms.h
src files: src/relay/pass
python: python/tvm/relay/transform.py

tensort level IR pass:

header file include/tvm/tir/ir_pass.h
src files: src/tir/pass python: python/tvm/tir/ir_pass.py

本文只涉及到 high-level pass。

tensort level IR pass 更多的是底层代码生成时做的优化,如 VectorizeLoop,UnrollLoop,InjectPrefetch,StorageRewrite 等。

ModulePass

  1. LambdaLift 将局部函数提升为全局函数。

在 src/relay/backend/vm/compiler.cc, src/relay/backend/vm/lambda_lift.cc。

  1. RemoveUnusedFunctions 去掉 relay 的 IR 模块中未使用的函数。

Inline 将一个被 inline 标记的全局函数嵌入到 relay 的IR模块中。

  1. EtaExpand 为构造函数添加抽象,或者给一个函数添加全局变量。
    如: square 被转化为 fn (%x: int32) -> int32 { square(x) } See https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B7-conversion

  2. PartialEval 在编译时评估静态的代码碎片,尽可能多的做常量传播,常量折叠,代码嵌入等优化,以减少运行时开销,获得更多的融合优化。作为代价生成的code量会增加。

  3. PrintIR 打印IR。通常作为一系列 pass 的结果,用来debug。

  4. PartitionGraph 将一个混合后端的relay程序分割到各自后端的计算区域。

  5. ToANormalForm 将一个数据流图转化为行政范式 (Administrative Normal Form, A-Normal Form, ANF)。 将一个表达从隐式共享的图的形式转化为显式共享, 也就是 ANF。 The scope of the root expression is the global scope. The scope of any non root expression is the least common ancestor of all it’s scope. Values are ordered by post-DFS order in each scope.

FunctionPass

  1. DeadCodeElimination 去掉不影响程序结果的表达。将没有被索引到的 let bindings 去掉,将被使用过一次的 let bindings 作为代码嵌入。

  2. FoldConstant 折叠常量函数。

  3. FuseOps 融合算子,可以指定融合的优化级别

  4. RewriteAnnotatedOps 重写被注释的算子, 比如 on_device 会标记在哪一种设备上进行调度计算。帮助进行异构计算执行。

  5. ToCPS 将一个表达转化为连续传递式样 (continuation passing style, CPS)。 CPS 意思是每一个函数将不会直接返回结果,而是传递一个另外的函数,作为参数,然后将结果传到下一个续集。这样每一个函数调用时将会多一个参数,表示其余的计算。每一个中间计算都会被传入一个续集。

  6. ToGraphNormalForm 去除所有的 let binding, 并将所有的变量转化为直接的指针索引。返回的表达叫做 graph normal form。

  7. SimplifyInference 在推理时简化一些特定的算子,比如 batchnorm 会被代替为一些更加简单的算子。这部分 Efficient Deep Learning Inference on Edge Devices 有写。

  8. FastMath 将非线性激活函数替换成近似计算的算子以获得更快的计算速度。一些情景下有损失计算精度的风险。 InferType 推理一个表达式的类别。获得的结果是一个有显式类别信息的新表达式,以及它的返回类型。

  9. EliminateCommonSubexpr 寻找并去掉共同的子表达式。比如有两个表达式被评估为等同,则新建一个变量来替代它们两个。

  10. CombineParallelConv2D 将并行运算的二维卷积合成为一个卷积,如果合成的卷积的分支数大于某个最小值。

  11. CombineParallelDense 将并行运算的稠密算子合并为批处理的矩阵乘法 (batch_matmul),如果合成的算子的分支数小于某个最小值。

  12. BackwardFoldScaleAxis / ForwardFoldScaleAxis 将轴的缩放折叠进卷积或者稠密算子的weights中 https://github.com/apache/incubator-tvm/pull/2020

  13. CanonicalizeOps 将一些深度学习的算子规范化为一系列简化的算子,比如将 bias_add 规范化为先进行 expand_dims 然后做 broadcast_add

  14. AlterOpLayout 转换算子的布局,或者将元算子替换为其他表达式。可以用来计算定制数据布局的卷积,或者更加通用的weights的前置转换。

  15. ConvertLayout 给定一个目标的数据布局,将表达式大多数算子的数据布局转化为目的布局。在理想情况下,只有两个起始和结束的布局需要转换。一般在relay转化别的框架时需要。主要使用 AlterOpLayout 和 InferCorrectLayout。

  16. RFC - https://discuss.tvm.ai/t/layout-conversion-pass/4009

  17. Legalize 将一个表达式转化为另一个表达式 (expr),可以基于算子的shape,dtype,或者 data layout 转化为另一个算子或者一系列算子。一般在target平台相关的优化中,两个语等价的算子性能不一定会一样。

  18. CanonicalizeCast 规范化 cast 表达式,使得算子的融合更加方便。

  19. MergeComposite 将多个模式相匹配的算子合并为一个复合算子。主要用在使用外部代码生成工具时多个算子map到同一个外部算子的情况。定义作为Wrapper使用。 在 src/relay/pass/annotate_target.cc

  20. AnnotateTarget 将表达式中的算子标注编译器或者部署平台,使得算子被wrap为 compiler_begin/subgraph_start 和 compiler_end /subgraph_end,从而用做之后其他编译器的代码生成。定义作为Wrapper使用。 在 src/relay/pass/annotate_target.cc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/745765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一、C#冒泡排序算法

一、C#冒泡排序算法 简介 冒泡排序算法是一种基础的排序算法&#xff0c;它的实现原理比较简单。核心思想是通过相邻元素的比较和交换来将最大&#xff08;或最小&#xff09;的元素逐步"冒泡"到数列的末尾。 实现原理 冒泡排序是一种简单的排序算法&#xff0c;其…

CSS Module

CSS Module的作用&#xff1a;将CSS样式作用域限制在特定的组件范围内&#xff0c;以避免全局样式污染和命名冲突。 Vue中如何实现样式模块…

【TB作品】MSP430单片机,音乐播放器,四首音乐,八音盒,Proteus仿真

文章目录 题目要求仿真结果实验报告&#xff1a;基于MSP430单片机的八音盒设计实验目的实验设备实验原理总结 代码和仿真图 题目要求 八音盒 本设计利用MSP430单片机结合内部定时器及LED/LCD,设计一个八音盒,按下单键可以演奏预先设置的歌曲旋律。 基本要求: 使用LED/LCD显示器…

JAVA基础:数组、重载、数据类型、封装、字符串、静态、继承、重写、多态、代码块、权限、接口、内部类

1 数组 //静态初始化 int[] arr1new int[]{1,2,3,4} //简化形式 int[] arr2{1,2,3,4} //动态初始化 int[] arr3new int[5] 2 方法重载 在同一个类中的多个方法的方法名相同,参数个数不同&#xff0c;参数类型不同&#xff0c;参数类型顺序不同 public class Test1 {public …

【javaWeb】在webapp中手动发布一个应用

标题 &#x1f432;一、为什么要在webapp中手动发布一个应用&#x1f389;二、手动发布步骤1.下载Tomcat2.解压并安装3.在webapps中创建文档 ✨三、总结 &#x1f432;一、为什么要在webapp中手动发布一个应用 好处解释灵活性手动发布应用程序可以根据自己的需求进行自定义配置…

2.24计组碎片

op(a1)说的是对a1存的数&#xff0c;即&#xff08;a1)&#xff0c;进行op操作 访存说的是访问、读写主存&#xff0c;如果是写在寄存器上&#xff0c;就不需要访存 对于C就是说&#xff0c;如果是1100-1110的话&#xff0c;就可以对应取到0000-1111&#xff0c;如果是下面那个…

css性能优化的方法?

CSS 优化在网页性能和用户体验中起着重要作用。以下是一些常见的 CSS 优化方法&#xff1a; 合并和压缩CSS文件&#xff1a; 将多个 CSS 文件合并为一个文件&#xff0c;并对其进行压缩&#xff0c;可以减少 HTTP 请求次数和文件大小&#xff0c;加快页面加载速度。 使用CSS预处…

全面解析 LoRA、QLoRA、RLHF,PPO,DPO,Flash Attention、增量学习等大模型算法

随着大模型的飞速发展&#xff0c;在短短一年间就有了大幅度的技术迭代更新&#xff0c;从LoRA、QLoRA、AdaLoRa、ZeroQuant、Flash Attention、KTO、蒸馏技术到模型增量学习、数据处理、新的开源模型的理解等&#xff0c;几乎每天都有新的发展。 作为算法工程师&#xff0c;面…

【Linux】文本替换Ubuntu 中 sed 指令的使用指南

没什么 大愿望 没有什么事 要赶 看见路口红灯 一直闪 它像 眨眼的小太阳 乌云还 挺大胆 顶在头上 吹不散 我抓在手里 捏成棉花糖 什么烦恼 不能忘 既然 是路一定有转弯 哪个风景 都漂亮 &#x1f3b5; 周深《小美满》 在 Ubuntu&#xff08;或其他类 Unix…

09.JavaScript中的深浅拷贝和异常处理

一、深浅拷贝 1.浅拷贝 首先浅拷贝和深拷贝只针对引用类型 浅拷贝&#xff1a;拷贝的是地址 常见方法&#xff1a; 拷贝对象&#xff1a;Object.assgin() / 展开运算符 {…obj} 拷贝对象拷贝数组&#xff1a;Array.prototype.concat() 或者 […arr] 如果是简单数据类型拷贝…

pdfgear:免费易用的pdf处理软件,轻松进行pdf文件转换、阅读、编辑等

名人说:东边日出西边雨,道是无晴却有晴。——刘禹锡 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、软件介绍①pdfgear②核心功能③PDFgear的优势二、下载安装①下载②安装三、使用方法①阅读②pdf编辑③pdf转换四、总结

Spring状态机简单实现

一、什么是状态机 状态机&#xff0c;又称有限状态自动机&#xff0c;是表示有限个状态以及在这些状态之间的转移和动作等行为的计算模型。状态机的概念其实可以应用的各种领域&#xff0c;包括电子工程、语言学、哲学、生物学、数学和逻辑学等&#xff0c;例如日常生活中的电…

基于单片机的恒压供水控制器设计

摘 要 随着我国现代化的进程不断加快&#xff0c;城市居民生活水平不断提高&#xff0c;随之而来的是房屋的翻新和重建&#xff0c;但建筑层数的不断增高&#xff0c;使得供水所需压力不断提高&#xff0c;若建筑设计时对压力判断不足&#xff0c;会导致供水时无法供应到高楼层…

2.1HTML5基本结构

HTML5实际上不算是一种编程语言&#xff0c;而是一种标记语言。HTML5文件是由一系列成对出现的元素标签嵌套组合而成&#xff0c;这些标签以<元素名>的形式出现&#xff0c;用于标记文本内容的含义。浏览器通过元素标签解析文本内容并将结果显示在网页上&#xff0c;而元…

【Jenkins】data stream error|Error cloning remote repo ‘origin‘ 错误解决【亲测有效】

错误构建日志 17:39:09 ERROR: Error cloning remote repo origin 17:39:09 hudson.plugins.git.GitException: Command "git fetch --tags --progress http://domain/xxx.git refs/heads/*:refs/remotes/origin/*" returned status code 128: 17:39:09 stdout: 17…

多线程(代码案例: 单例模式, 阻塞队列, 生产者消费者模型,定时器)

设计模式是什么 类似于棋谱一样的东西 计算机圈子里的大佬为了能让小菜鸡的代码不要写的太差 针对一些典型的场景, 给出了一些典型的解决方案 这样小菜鸡们可以根据这些方案(ACM里面叫板子, 象棋五子棋里叫棋谱, 咱这里叫 设计模式), 略加修改, 这样代码再差也差不到哪里去 … …

clickhouse(配合bytebase)_docker搭建文档

特点 数据吞吐量非常大&#xff0c;能够存储海量的数据&#xff0c;并能够以水平扩展的方式进行扩容。对大表的查询计算处理效率也非常高&#xff0c;甚至很多场景下都可以拥有数百列的数据规模&#xff0c;很多大规模的数据查询也都能轻松达到毫秒级别 但需要指出&#xff0…

已解决org.apache.zookeeper.KeeperException.BadArgumentsException异常的正确解决方法,亲测有效!!!

已解决org.apache.zookeeper.KeeperException.BadArgumentsException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 控制数据大小&#xff1a;在写入数据前&#xff0c;检查数据大小是否超过限…

数码管的动态显示(三)

1.原理 data_reg寄存&#xff0c;只寄存符号位和数据位不包含小数点位。 动态数码管每个显示1ms&#xff0c;所以计数到5*10^4-1 为了将sel和seg同步&#xff0c;把sel打了一拍。 6位都使用到了可以这么计算&#xff0c;6位都显示的是数据。或者最高位显示的是小数点&#xff…

Flink广播流 BroadcastStream

文章目录 前言BroadcastStream代码示例Broadcast 使用注意事项 前言 Flink中的广播流&#xff08;BroadcastStream&#xff09;是一种特殊的流处理方式&#xff0c;它允许将一个流&#xff08;通常是一个较小的流&#xff09;广播到所有的并行任务中&#xff0c;从而实现在不同…