【TVM 教程】编写自定义 Pass

自定义pass编写 自定义pass编写 TVM是一个框架,抽象了机器学习加速器的异质性。有时,用户可能需要自定义一些分析和IR转换,使TVM适应自己的专用硬件。本文可帮助用户在TVM中编写自定义pass。 先决条件 在阅读本文之前,假设读者已经熟悉以下主题: • 在TVM中编写算法并进行调度。否则,请参见示例教程,例如 如何在CPU上优化GEMM。 • HalideIR的基本结构。否则,请参阅HalideIR/src/ir/IR.h以了解定义了IR节点的哪些属性。 • 访客设计模式。否则,请检查 Python AST模块以查看 阅读详情

Apache TVM是一个深度的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →https://tvm.hyper.ai/

作者:Jian Weng

TVM 是一个抽象出机器学习加速器异质性的框架,有时用户希望自定义一些分析和 IR 转换,使得 TVM 适应自己的专用硬件。本教程介绍如何在 TVM 中编写自定义 Pass。

先决条件​

阅读本教程前,假设读者已经熟悉以下主题:

  • 在 TVM 中编写算法并对其进行调度,若不熟悉,请参阅示例教程如 如何在 CPU 上优化 GEMM。
  • 熟悉 HalideIR 的基本结构,若不熟悉,请参阅 HalideIR/src/ir/IR.h 了解定义了 IR 节点的哪些属性。
  • 访问器设计模式,若不熟悉,请参阅 Python AST 模块 以查看 AST 访问器的实现原理。
  • Schedule 如何降低为 IRModule 类或 LLVM 模块。若不熟悉,请参阅 python/tvm/build_module.py 获取相关基础知识。
import tvm
from tvm import te
import numpy as np

首先编写一个简单的向量加法,并用默认 schedule 构建。然后,使用自定义的降低 pass 而非调度原语,来直接操作 IR。

n = tvm.tir.const(128, "int32")
a = te.placeholder((n,), name="a")
b = te.placeholder((n,), name="b")
c = te.compute((n,), lambda i: a[i] + b[i], name="c")

sch = te.create_schedule(c.op)
ir = tvm.lower(sch, [a, b, c])
print(ir)

输出结果:

@main = primfn(a_1: handle, b_1: handle, c_1: handle) -> ()
  attr = {"from_legacy_te_schedule": True, "global_symbol": "main", "tir.noalias": True}
  buffers = {a: Buffer(a_2: Pointer(float32), float32, [128], []),
             b: Buffer(b_2: Pointer(float32), float32, [128], []),
             c: Buffer(c_2: Pointer(float32), float32, [128], [])}
  buffer_map = {a_1: a, b_1: b, c_1: c}
  preflattened_buffer_map = {a_1: a_3: Buffer(a_2, float32, [128], []), b_1: b_3: Buffer(b_2, float32, [128], []), c_1: c_3: Buffer(c_2, float32, [128], [])} {
  for (i: int32, 0, 128) {
    c[i] = (a[i] + b[i])
  }
}

编写 Pass​

本质上,「IR 转换 pass」是将语句映射到新语句的函数。因此,我们要定义这个向量化函数,并逐步实现它。

TVM 为用户提供了两个类来分析和转换 IR。

IR 访问器​

可以用 tvm.tir.stmt_functor.post_order_visit(stmt, func) 从 Halide IR 中收集信息。 func 是一个回调函数,会在退出当前 IR 节点之前调用,即 post-order visit。然后存储 IR 访问的结果,因为 func 的返回值将被忽略。

备注

必须用数组来存储 IR 访问的结果。值甚至是一个单变量。这主要是由于 Python-C runtime 的限制,每次递归都会刷新变量值,但会保留数组值。

loops = []

def find_width8(op):
    """查找范围可以被 8 整除的所有「tir.For」节点。"""
    if isinstance(op, tvm.tir.For):
        if isinstance(op.extent, tvm.tir.IntImm):
            if op.extent.value % 8 == 0:
                loops.append(op)

IR 转换​

转换接口与访问器接口略有不同。访问器中只有一个后序回调,但转换访问器同时支持前序回调和后序回调。若要保留原始 IR 节点,只需返回 None。若要将当前节点更改为某个节点,使用 TVM IR maker 接口构建,并返回这个值。

备注

若调用 pre-order 函数后返回一个非 None 的值,则将跳过 post-order 函数。

def vectorize8(op):
    """Split 可以向量化 `find_width8` 中的循环。"""
    if op in loops:
        extent = op.extent.value
        name = op.loop_var.name
        lo, li = te.var(name + ".outer"), te.var(name + ".inner")
        body = tvm.tir.stmt_functor.substitute(op.body, {op.loop_var: lo * 8 + li})
        body = tvm.tir.For(li, 0, 8, tvm.tir.ForKind.VECTORIZED, body)
        body = tvm.tir.For(lo, 0, extent // 8, tvm.tir.ForKind.SERIAL, body)
        return body
    return None

@tvm.tir.transform.prim_func_pass(opt_level=0)
def vectorize(f, mod, ctx):
    global loops

    tvm.tir.stmt_functor.post_order_visit(f.body, find_width8)

    if not loops:
        return f

    # 最后一个列表参数表示将转换哪些类型的节点。
    # 在这种情况下,只有 `For` 节点会调用 `vectorize8`
    return f.with_body(tvm.tir.stmt_functor.ir_transform(f.body, None, vectorize8, ["tir.For"]))

对接低层(Glue to Lowering)​

到目前为止,已经完成了这个 IR 转换 pass 的编写。接下来将这个 pass 和 TVM 的底层 pass 对接。

在这种情况下,通过元组列表作为参数提供给 tir.add_lower_pass,将上面编写的 pass 注入 TVM 标准较低级的 pass。「元组」表示降级的不同阶段。 TVM 中有四个阶段的降级,每个阶段完成后,都会调用自定义的阶段。

备注

以下是每个阶段完成的基本转换:

  • 阶段 0 生成原始 IR 和循环级别。
  • 阶段 1 扁平化数组存储。
  • 阶段 2 转换循环,如展开、矢量化和线程绑定。
  • 阶段 3 清理工作。

因此,这个转换 pass 适合放在第 1 阶段之后。

with tvm.transform.PassContext(config={"tir.add_lower_pass": [(1, vectorize)]}):
    print(tvm.lower(sch, [a, b, c]))

输出结果:

@main = primfn(a_1: handle, b_1: handle, c_1: handle) -> ()
  attr = {"from_legacy_te_schedule": True, "global_symbol": "main", "tir.noalias": True}
  buffers = {a: Buffer(a_2: Pointer(float32), float32, [128], []),
             b: Buffer(b_2: Pointer(float32), float32, [128], []),
             c: Buffer(c_2: Pointer(float32), float32, [128], [])}
  buffer_map = {a_1: a, b_1: b, c_1: c}
  preflattened_buffer_map = {a_1: a_3: Buffer(a_2, float32, [128], []), b_1: b_3: Buffer(b_2, float32, [128], []), c_1: c_3: Buffer(c_2, float32, [128], [])} {
  for (i.outer: int32, 0, 16) {
    let cse_var_1: int32 = (i.outer*8)
    c[ramp(cse_var_1, 1, 8)] = (a[ramp(cse_var_1, 1, 8)] + b[ramp(cse_var_1, 1, 8)])
  }
}

快速回顾​

快速回顾本教程有关编写自定义 IR 转换 pass:

  • 用 tvm.tir.stmt_functor.post_order_visit 收集每个 IR 节点的信息。
  • 用 tvm.tir.stmt_functor.ir_transform 转换 IR 节点。
  • 总结以上两点来编写一个 IR 转换函数。
  • 用 tvm.transform.PassContext 将此函数放入 TVM 降级 pass。

下载 Python 源代码:low_level_custom_pass.py

下载 Jupyter Notebook:low_level_custom_pass.ipynb

TVM前端研究--Pass 在 TVM 中,Pass 根据 IR 类型的不同分为 Relay 上的优化和 TIR 上的优化。因此,Relay 上的 Pass 包括通用的 pass,在 tvm.relay.transform 目录下,还有一些 Pass 包含系统信息或后端信息,在其他目录下如 AnnotateUsedMemory Pass 等。是包含了一个 Pass 所需的基本信息如 name 是 pass 名称,opt_level 表示将在哪个优化级别启用 pass,required 表示执行某个 pass 所需的 pass。 阅读详情

相关推荐

TVM学习教程

目录Step 1:TVM 视频介绍Step 2:研读 TVM 论文Step 3:了解 TVM 官网中可以学习的资料Step 4:上手 TVM CodeStep 5:TVM 其他相关资料 Step 1:TVM 视频介绍 https://www.bilibili.com/video/av29521815/ Step 2:研读 TVM 论文 https://www.usenix.org/system/files/osdi18-chen.pdf Step 3:了解 TVM 官网中可以学习的资料 https://tv

qq_33287871的博客 3077

【tvm官网教程01】设计和架构-1

【tvm官网教程01】设计和架构-1目的1. 编译流1.1 关键数据结构1.2 Transformations1.2.1 变换的分类1.2.2 relay/transform1.2.2.1 子函数1.2.2.2 overall问题1.2.3 tir/transform1.3 搜索空间和基于学习的transformation1.4 Target Translation1.5 Runtime Execution1.6 小结2. 逻辑组件2.1 tvm/support2.2 tvm/runtime2.3 tvm

B10030607的博客 1994

告别Godot默认编辑器:手把手教你用VSCode配置C#开发环境(附中文注释避坑指南)

本文详细介绍了如何将VSCode配置为Godot引擎的C#开发环境,提升游戏开发效率。从环境准备、扩展配置到解决中文编码问题,提供完整的避坑指南和高级调试技巧,帮助开发者充分利用VSCode的智能补全和调试功能,打造高效工作流。

weixin_30596023的博客 189

【从零开始学深度学习编译器】一,深度学习编译器及TVM 介绍

0x0. 介绍 大家好呀,在过去的半年到一年时间里,我分享了一些算法解读,算法优化,模型转换相关的一些文章。这篇文章是自己开启学习深度学习编译器的第一篇文章,后续也会努力更新这个系列。这篇文章是开篇,所以我不会具体讲解TVM的知识,更多的是介绍一下我自己眼中的深度学习编译器是什么?以及为什么我要选择学习TVM,最后我也会给出一个让读者快速体验TVM效果的一个开发环境搭建的简要教程。 0x1. 为什么需要深度学习编译器? 深度学习编译器这个词语,我们可以先拆成两个部分来看。 首先谈谈深度学习领域。从训练框架角

I good vegetable a! 4091

MATLAB 2022b 用户专属:保姆级IPOPT求解器安装配置全流程(含OPTI工具箱)

本文为MATLAB 2022b用户提供IPOPT求解器的详细安装与配置指南,涵盖OPTI工具箱的适配安装、环境变量设置及常见问题解决方案。通过实战案例演示非线性优化问题的求解过程,帮助用户高效部署这一强大的优化工具,提升工程计算效率。

weixin_29092031的博客 405

直流无刷BLDC六步换相“电流环”、“速度环”的关系

4、在电流闭环模式下,电机的电流严格按给定电流运行,负载大小变化时,电流不变,电机速度跟随负载非正常变化,例如速度严重“超速”时,需要“限速”保护,这个任务是“速度环”的,这时“速度环”会根据设定的速度上限,控制触发器,将电机速度迅速将下来,电流降下来,在“限速”保护期间,“电流环”失去对触发器的控制作用;3、在电流闭环模式下,电机的电流严格按给定电流运行,负载大小变化时,电流不变,电机速度跟随负载正常变化,“速度环”对速度大小的控制作用是没有的;6、很多人想当然,“电流环”不工作,电机“电流”哪里来的?

u011878611的博客 5471

TVM安装教程(踩坑记录)

TVM安装教程以及一些踩坑记录

m0_49339981的博客 2279

【博客汇总】UV打印机博客汇总

UV平板机博客汇总

Print World的专栏 1741

【TVM 教程】使用 TVM 部署框架预量化模型 - 第 3 部分(TFLite)

更多 TVM 中文文档可访问 →此教程介绍如何量化 TFLite 计算图,并通过 TVM 编译和执行。有关使用 TFLite 量化模型的更多详细信息,参阅。TFLite 模型下载。开始前,先安装 TensorFlow 和 TFLite 包。执行命令检查 TFLite 包是否安装成功。

HyperAI超神经 1580

Java8对接三方流式接口,并实时输出(GPT)

【代码】Java8对接三方流式接口,并实时输出(GPT)

m0_58617940的博客 1728

433MHz无线通信

基本概念 工作频率:433.92MHz 调制方式:ASK/OOK,FSK,GFSK 现有遥控与接收器方案中,多数使用ASK/OOK调试方式。ASK即“幅移键控”又称为“振幅键控”,也有称为“开关键控”(通断键控)的,所以又记作OOK(On-Off keyed)信号。ASK是一种相对简单的调制方式。幅移键控(ASK)相当于模拟信号中的调幅,不同的是与载频信号相乘的是二进制数码。幅移就是把频...

沧海孤叶的博客 5万+

【TVM 教程】如何使用 TVM Pass Instrument

​可以使用装饰器创建自定义 instrument 类。创建一个工具类(计算每次 Pass 引起的每个算子出现次数的变化)。可以在 Pass 之前和之后查看op.name来找到每个算子的名称,从而计算差异。# Pass 可以嵌套。# 使用堆栈来确保得到之前/之后正确的 pairs。# 弹出最新记录的 Pass。# 只记导致差异的 Pass。"""return ["""ret = {}else:# 某些 CallNode 可能没有“名称”,例如 relay.Functionreturn。

HyperAI超神经 1038

slam_toolbox 简介和使用

专为移动机器人在未知环境中实时构建地图和定位设计。它适用于2D和3D环境,支持多种机器人平台和传感器配置。

weixin_74808846的博客 3307

【TVM 教程】如何使用 TVM Pass Infra

Apache TVM 是一个深度的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →<https://tvm.hyper.ai/>

HyperAI超神经 832

【自适应调制】自适应调制解调通信链路误码率matlab仿真与分析

BPSK中的B是“Binary”的意思,也就是有两个变化状态,比如说相位上的“+90°(代表1)、-90°(代表0)”,一个状态代表的就是一个比特。QPSK的“Q”是“Quadrature”的意思,有四个变化状态,如相位上的“+45°(代表00)、-45°(代表11)、+135°(代表10)、-135°(代表01)”,那么一个状态就代表两个比特的信息,如图2所示。同理8PSK的一个状态代表三个比特。但是功率资源并不是无限的,基站总的发射功率是一定的,为了充分利用所有的功率,采用的就是AMC技术。

FPGA/MATLAB学习教程/源码/项目合作开发 1833

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

【从零开始学深度学习编译器】八,TVM的算符融合以及如何使用TVM Pass Infra自定义Pass

对于一张有向图(可以有环)我们规定一个起点, 从点到图上另一个点可能存在很多条路径(下面将到简写为)。如果对于的任意一条路径中都存在一个点, 那么我们称点为的支配点(也可以称作是的必经点), 注意点不讨论支配点. 下面用表示离点最近的支配点。对于原图上除外每一个点, 从向建一条边, 最后我们可以得到一个以为根的树. 这个树我们就叫它"支配树"。这篇文章介绍了TVMPass Infra的一些实践例子,然后介绍了支配树和算符融合Pass。

生活需要深度 868

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

TVM Pass 总结

介绍 tvm.relay.transform

fwx_x的博客 3162

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

TVM Pass概述

use pass infra Design and Architecture Pass又称transform,每一个transform要么把现有程序转换并优化为一个等价的程序,要么把程序lower到下层。relay/transform包括很多优化模型用的pass,包括常量折叠(constant folding)和死代码删除(dead-code elimination)等。 这里拿tests/python/relay/test_pass_fold_constant.py里的单测作为例子: import num

Eurypterid的博客 1352

TVM 项目深度解析:如何编写自定义 Pass 优化计算图

在深度学习编译器 TVM 中,Pass 是优化计算图的核心机制。本文将深入讲解如何在 TVM 中编写自定义 Pass,帮助开发者针对特定硬件架构进行深度优化。 ## Pass 基础概念 Pass 是 TVM 中对中间表示(IR)进行转换和优化的基本单元。它能够: 1. 分析计算图的特定模式 2. 对计算图进行变换和优化 3. 为后续优化阶段准备数据 TVM 提供了丰富的内置 Pass,但有

gitblog_00022的博客 580

【TVM 教程】向 Relay 中添加 Compiler Pass

Apache TVM 是一个深度的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →https://tvm.hyper.ai/

HyperAI超神经 960

TVM 实践指南:向 Relay 框架添加自定义 Compiler Pass 的完整流程

摘要:本文详细介绍了TVM深度学习编译器中Relay中间表示层自定义CompilerPass的开发方法。文章从RelayCompilerPass的架构解析入手,阐明其函数级和模块级Pass的分层设计及注册机制。通过代码示例展示了自定义Pass的核心开发流程,包括IR遍历、模式匹配、节点转换等关键技术,并以Conv2D-BatchNorm融合为例演示具体实现。最后探讨了Pass的调试、性能评估及集成优化最佳实践,为开发者提供了TVM中间表示优化的完整技术指南。

jingpide9527的博客 835

【TVM教程】Pass 基础设施

​系统重点关注可扩展性,使用户能快速添加新传递而不破坏兼容性。其结构包括后端与前端:后端实现核心逻辑,前端则提供简单的 API 供用户创建与控制优化流程。

HyperAI超神经 631

【从零开始学深度学习编译器】五,TVM Relay以及Pass简介

在前面几节的介绍中我们了解到了TVM是如何将ONNX前端模型转换为IR Module的,并且还剖析了TVM中的Relay算子和TOPI算子的扭转过程,知道了Relay算子的最终计算也是基于TOPI算子集合完成的。这几行代码展示了TVM的编译流程,在这个编译流程里面不仅包含了基于Relay IR进行的优化策略来去除冗余的算子(也叫Pass)还包含了将Relay程序编译成特定后端(这里是llvm)可以执行的代码(codegen)。

生活需要深度 960

【从零开始学深度学习编译器】七,万字长文入门TVM Pass

0x0. 前言 这篇文章基于TVM 0.8.0.dev版本。在【从零开始学深度学习编译器】五,TVM Relay以及Pass简介 这篇推文中已经简单介绍了Relay和Pass机制。但是那篇文章介绍得不是很细节,对Relay的抽象语法树结构以及Pass的代码实现都没有细读,因此这篇文章以Constant Folding为例来详细介绍TVM Relay Pass。 0x1. TVM Relay树结构 在介绍Pass之前需要理清一些关键概念。 AST 摘自wiki 在计算机科学中,抽象语法树(Abstract

I good vegetable a! 1728
上一篇: 一键部署丨DeepSeek-R1 新版本小幅升级便登顶开源王座,多项评测超越 Qwen3
下一篇: 深度强化学习赋能城市消防优化,中科院团队提出DRL新方法破解设施配置难题
HyperAI超神经
博客等级 码龄7年 8872粉丝 1753原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值