TVM:設計與架構

TVM:設計與架構

本文檔適用于想要了解 TVM 架構和/或積極開發項目的開發人員。頁面組織如下:

  • 示例編譯流程概述了 TVM 將模型的高層描述轉換為可部署模塊所采取的步驟。要開始使用,請先閱讀本節。

  • 邏輯架構組件部分描述了邏輯組件。后面的部分是針對每個邏輯組件的特定指南,按組件的名稱組織。

  • 設備/目標交互描述了 TVM 如何與每種支持的物理設備和代碼生成目標進行交互。

  • 請查看開發人員操作指南以獲取有用的開發技巧。

本指南提供了架構的一些補充視圖。首先,我們回顧了一個端到端的編譯流程,并討論了關鍵的數據結構和轉換。這種基于運行時的視圖側重于運行編譯器時每個組件的交互。然后我們將回顧代碼庫的邏輯模塊及其關系。這部分提供了設計的靜態總體視圖。

示例編譯流程

在部分,我們將研究編譯器中的示例編譯流程。 下圖展示了該流程。 總體來說,它包含幾個步驟:

  • 導入:前端組件將模型引入到 IRModule 中,IRModule 包含內部表示模型的函數集合。

  • 轉換:編譯器將一個 IRModule 轉換為另一個功能等效或近似等效(例如在量化的情況下)IRModule。 許多轉換與目標(后端)無關。目標轉換pipeline的配置還可以根據具體設備目標改變。

  • 目標翻譯:編譯器將 IRModule 翻譯(代碼生成 codegen )為目標指定的可執行格式。 目標翻譯結果被封裝為一個runtime.Module,可以在目標運行時環境中導出、加載和執行。

  • 運行時執行:用戶加載一個 runtime.Module 并在支持的運行時環境中運行編譯好的函數。

在這里插入圖片描述

關鍵數據結構

設計和理解復雜系統的最佳方法之一是識別關鍵數據結構和操作(轉換)這些數據結構的 API。一旦我們確定了關鍵數據結構,我們就可以將系統分解為邏輯組件,這些邏輯組件要么定義關鍵數據結構的集合,要么定義數據結構之間的轉換。

IRModule 是全棧中使用的基本數據結構。 IRModule(中間表示模塊)包含一組函數。目前,我們支持兩種基本的函數變體。

  • relay::Function 是一種高層程序表示。一個 relay.Function 通常對應一個端到端的模型。您可以將 relay::Function 視為一張計算圖,它額外支持控制流、遞歸和復雜數據結構。

  • tir::PrimFunc 是一種低層程序表示,它包含包括循環嵌套選擇、多維加載/存儲、線程和向量/張量指令在內的元素。它通常用于表示在模型中執行(可能是經過融合的)層的算子程序。

在編譯過程中,一個中繼函數可能會被 lowered 為多個 tir::PrimFunc 函數和一個調用這些 tir::PrimFunc 函數的高層函數。

轉換

現在我們已經介紹了關鍵數據結構,讓我們談談轉換。每個轉換都可以用于以下目的之一:

  • 優化:將程序轉換為等效的,可能更優化的版本。

  • 降低(lowering):將程序轉換為更接近目標的較低級別表示。

relay/transform 包含一組優化模型的 pass。優化包括通用的程序優化,例如常量折疊和無用代碼消除,以及特定于張量計算的 pass,例如排布轉換和縮放因子折疊。

在 relay 優化 pipeline 的最后,我們將通過 pass(FuseOps) 以將端到端function(例如 MobileNet)分解為sub-function(例如 conv2d-relu)碎片。我們稱這些 function 碎片。這個過程幫助我們將原始問題分為兩個子問題:

  • 每個 sub-function 的編譯和優化。

  • 整體執行結構:我們需要對生成的 sub-function 進行一系列調用來執行整個模型。

我們使用低層 tir 階段來編譯和優化每個 sub-function。對于具體的目標,我們也可以直接進入目標翻譯階段并使用外部代碼生成器。

有幾種不同的方法(在 relay/后端)來處理對整體執行問題的調用。對于具有已知形狀且沒有控制流的簡單模型,我們可以 lower 為將執行結構存儲在圖中的 graph executor。我們還支持用于動態執行的虛擬機后端。最后,我們計劃支持 AOT 編譯,將高級執行結構編譯成可執行和生成的原語函數。所有這些執行模式都被一個統一的 runtime.Module 接口封裝,我們將在本文的后半部分討論。

tir/transform 包含 TIR 層次的函數的轉換 pass。許多 tir pass 的目的是 lowering。例如,通過將多維訪問扁平化到一維指針訪問,將內在函數擴展為特定于目標的內在函數,以及修飾函數入口以滿足運行時調用約定。當然,也有優化 pass,比如訪問索引簡化和死代碼消除。

許多低級優化可以在目標階段由 LLVM、CUDA C 和其他目標編譯器處理。因此,我們將寄存器分配等低層優化留給下游編譯器,只關注它們未涵蓋的優化。

搜索空間和基于學習的轉換

到目前為止,我們描述的轉換過程是確定性的和基于規則的。 TVM 的一個設計目標是支持針對不同硬件平臺的高性能代碼優化。為此,我們需要研究盡可能多的優化選擇,包括但不限于多維張量訪問、循環平鋪行為、特殊加速器內存層次結構和線程。

很難定義一個啟發式來做出所有選擇。相反,我們將采用基于搜索和學習的方法。我們首先定義一組可以用來轉換程序的操作。示例操作包括循環轉換、內聯、矢量化。我們將這些動作稱為調度原語 (scheduling primitives)。調度原語的集合定義了我們可以對程序進行的可能優化的搜索空間。然后系統搜索不同的可能調度序列以選擇最佳調度組合。搜索過程通常由機器學習算法來優化。

一旦搜索完成,我們可以記錄(可能是經過融合的)算子的最佳調度順序。然后編譯器可以查找最佳調度序列并將其應用于程序。值得注意的是,這個調度應用階段與基于規則的轉換完全一樣,使我們能夠與傳統 pass 共享相同的接口約定。

我們使用基于搜索的優化來處理初始 tir 函數生成問題。這部分模塊稱為 AutoTVM(auto_scheduler)。隨著我們繼續開發 TVM ,我們希望將基于學習的轉換擴展到更多領域。

目標轉換

目標轉換階段將 IRModule 轉換為相應的目標可執行格式。 對于 x86 和 ARM 等后端,我們使用 LLVM IRBuilder 來構建內存中的 LLVM IR。 我們還可以生成源級語言,例如 CUDA C 和 OpenCL。 最后,我們支持通過外部代碼生成器將 Relay 函數(子圖)直接轉換為特定目標。 重要的是最終代碼生成階段盡可能輕量級。 絕大多數的轉換和 lowering 應該在目標翻譯階段之前進行。

我們還提供了一個 Target 結構來指定編譯目標。 目標翻譯階段之前的轉換也可能受到目標的影響——例如,目標的向量長度會改變向量化行為。

運行時執行

TVM 運行時的主要目標是提供一個最小的 API,用于以他們選擇的語言加載和執行編譯的工件,包括 Python、C++、Rust、Go、Java 和 JavaScript。 下面的代碼片段顯示了 Python 中的這樣一個示例:

import tvm
# Example runtime execution program in python, with type annotated
mod: tvm.runtime.Module = tvm.runtime.load_module("compiled_artifact.so")
arr: tvm.runtime.NDArray = tvm.nd.array([1, 2, 3], device=tvm.cuda(0))
fun: tvm.runtime.PackedFunc = mod["addone"]
fun(a)
print(a.numpy())

tvm.runtime.Module 封裝了編譯的結果。 runtime.Module 包含一個 GetFunction 方法,用于按名稱獲取 PackedFuncs。

tvm.runtime.PackedFunc 是兩個生成函數的類型擦除函數接口。 runtime.PackedFunc 可以采用以下類型的參數和返回值:POD 類型(int,float)、字符串、runtime.PackedFunc、runtime.Module、runtime.NDArray 和 runtime.Object 的其他子類。

tvm.runtime.Moduletvm.runtime.PackedFunc 是模塊化運行時的強大機制。 例如,要在 CUDA 上獲取上述 addone 函數,我們可以使用 LLVM 生成主機端代碼來計算啟動參數(例如線程組的大小),然后從由支持的 CUDAModule 調用另一個 PackedFunc CUDA 驅動程序 API。 相同的機制可用于 OpenCL 內核。

上面的例子只處理了一個簡單的 addone 函數。 下面的代碼片段給出了使用相同接口執行端到端模型的示例:

import tvm
# Example runtime execution program in python, with types annotated
factory: tvm.runtime.Module = tvm.runtime.load_module("resnet18.so")
# Create a stateful graph execution module for resnet18 on cuda(0)
gmod: tvm.runtime.Module = factory["resnet18"](tvm.cuda(0))
data: tvm.runtime.NDArray = get_input_data()
# set input
gmod["set_input"](0, data)
# execute the model
gmod["run"]()
# get the output
result = gmod["get_output"](0).numpy()

主要的收獲是 runtime.Module 和 runtime.PackedFunc 足以封裝操作員級別的程序(例如 addone)以及端到端模型。

總結與討論

綜上所述,編譯流程中的關鍵數據結構有:

  • IRModule:包含relay.Function和tir.PrimFunc

  • runtime.Module:包含 runtime.PackedFunc

編譯的大部分是關鍵數據結構之間的轉換。

  • relay/transform 和 tir/transform 是確定性的基于規則的轉換

  • auto_scheduler 和 autotvm 包含基于搜索的轉換

最后,編譯流程示例只是 TVM 的一個典型用例。 我們將這些關鍵數據結構和轉換公開給 python 和 C++ API。 因此,您可以像使用 numpy 一樣使用 TVM,只是感興趣的數據結構從 numpy.ndarray 更改為 tvm.IRModule。 以下是一些示例用例:

  • 使用python API直接構建IRModule。

  • 編寫一組自定義轉換(例如自定義量化)。

  • 使用 TVM 的 python API 直接操作 IR。

邏輯架構組件

在這里插入圖片描述

上圖顯示了項目中的主要邏輯組件。 請閱讀以下部分以獲取有關組件及其關系的信息。

tvm/support

支持模塊包含基礎設施最常用的實用程序,例如通用 arena 分配器、套接字和日志記錄。

tvm/runtime

*Runtime (運行時)*是 TVM 的基礎。它提供了加載和執行已編譯工件的機制。運行時定義了一組穩定的標準 C API 來與 Python 和 Rust 等前端語言交互。

runtime::Object 是 TVM 運行時中除 runtime::PackedFunc 之外的主要數據結構之一。它是一個具有類型索引的引用計數基類,以支持運行時類型檢查和向下轉型。對象系統允許開發人員向運行時引入新的數據結構,例如 Array、Map 和新的 IR 數據結構。

除了部署用例,編譯器本身也大量使用 TVM 的運行時機制。所有 IR 數據結構都是 runtime::Object 的子類,因此可以從 Python 前端直接訪問和操作它們。我們使用 PackedFunc 機制向前端公開各種 API。

對不同硬件后端的運行時支持在運行時的子目錄中定義(例如 runtime/opencl)。這些特定于硬件的運行時模塊定義了用于設備內存分配和設備功能序列化的 API。

runtime/rpc 實現了對 PackedFunc 的 RPC 支持。我們可以使用 RPC 機制將交叉編譯的庫發送到遠程設備,并對執行性能進行基準測試。 rpc 基礎架構支持從各種硬件后端收集數據,以進行基于學習的優化。

  • TVM Runtime System

  • Runtime-Specific Information

  • Debugger

  • Putting the VM in TVM: The Relay Virtual Machine

  • Introduction to Module Serialization

  • Device/Target Interactions

tvm/node

節點模塊在 runtime::Object 之上為 IR 數據結構添加了額外的功能。 主要特征包括反射、序列化、結構等價和哈希。

有了 node 模塊,我們可以通過 Python 中的名稱直接訪問 TVM 的 IRNode 的任何字段。

x = tvm.tir.Var("x", "int32")
y = tvm.tir.Add(x, x)
# a and b are fields of a tir.Add node
# we can directly use the field name to access the IR structures
assert y.a == x

我們還可以將任意 IR 節點序列化為 JSON 格式,然后將它們加載回來。 保存/存儲和檢查 IR 節點的能力是使得編譯器更易于訪問的基礎。

tvm/ir

tvm/ir 目錄包含所有 IR 函數變體的統一數據結構和接口。 tvm/ir 中的組件由 tvm/relaytvm/tir 共享,值得注意的包括

  • IRModule

  • Type

  • PassContext 和 Pass

  • Op

不同的函數變體(例如,relay.Function 和 tir.PrimFunc)可以在 IRModule 中共存。 雖然這些變體可能沒有相同的內容表示,但它們使用相同的數據結構來表示類型。 因此,我們使用相同的數據結構來表示這些變體的函數(類型)簽名。 一旦我們明確定義了調用約定,統一類型系統就允許一個函數變體調用另一個函數。 這為未來的跨功能變體優化打開了大門。

我們還提供了一個統一的 PassContext 用于配置 pass 行為,以及通用的復合 pass 來執行 pass 管道。 以下代碼片段給出了 PassContext 配置的示例。

# configure the behavior of the tir.UnrollLoop pass
with tvm.transform.PassContext(config={"tir.UnrollLoop": { "auto_max_step": 10 }}):# code affected by the pass context

Op 是表示所有系統定義的原始運算符/內在函數的通用類。 開發人員可以向系統注冊新的 Ops 以及它們的附加屬性(例如 Op 是否為 elementwise)。

  • Pass Infrastructure

tvm/target

target 模塊包含將 IRModule 轉換為目標 runtime.Module 的所有代碼生成器。 它還提供了一個描述目標的通用 Target 類。

通過查詢 target 中的屬性信息和注冊到每個 target id(cuda, opencl) 的 builtin 信息,可以根據target定制編譯 pipeline。

  • Device/Target Interactions

tvm/tir

TIR 包含低層程序表示的定義。 我們使用 tir::PrimFunc 來表示可以通過 TIR 通道轉換的函數。 除了 IR 數據結構外,tir 模塊還通過通用 Op 注冊表定義了一組內置內在函數及其屬性,以及 tir/transform 中的轉換 pass。

tvm/arith

該模塊與 TIR 密切相關。 低層代碼生成的關鍵問題之一是分析索引的算術屬性——正性、變量界限和描述迭代器空間的整數集。 arith 模塊提供了一組進行(主要是整數)分析的工具。 TIR pass 可以使用這些分析來簡化和優化代碼。

tvm/te

te 全稱為 “張量表達式”。 這是一個特定領域的語言模塊,它允許我們通過編寫張量表達式來快速構造 tir::PrimFunc 變體。 重要的是,張量表達式本身并不是一個可以存儲到 IRModule 中的自包含函數,而是 IR 的一個片段,我們可以拼接起來構建一個 IRModule。

te/schedule 提供了一組調度原語來控制正在生成的函數。 將來,我們可能會將其中一些調度組件帶到一個tir::PrimFunc 本身。

  • InferBound Pass
  • Hybrid Frontend Developer Guide

tvm/topi

雖然可以通過 TIR 或張量表達式 (TE) 為每個用例直接構造運算符,但這樣做很乏味。 topi(張量算子清單)提供一組由 numpy 定義的預定義算子(在 TE 或 TIR 中),在深度學習 workload 中很常見。 我們還提供了一組常見的 schedule 模板,以實現跨不同目標平臺的高性能實現。

tvm/relay

Relay 是用于表示完整模型的高層功能 IR。 在 relay.transform 中定義了各種優化。 Relay 編譯器定義了多種方言,每種方言都旨在支持特定的優化風格。 值得注意的包括 QNN(用于導入預量化模型)、VM(用于降低到動態虛擬機)、內存(用于內存優化)。

  • Introduction to Relay IR
  • Relay Operator Strategy
  • Convert Layout Pass

tvm/autotvm

AutoTVM 和 AutoScheduler 都是自動化基于搜索的程序優化的組件。 這正在迅速發展,主要包括:

  • 成本模型和特征提取。

  • 用于存儲成本模型構建的程序基準結果的記錄格式。

  • 一組關于程序轉換的搜索策略。

自動化程序優化仍然是一個活躍的研究領域。 因此,我們嘗試將設計模塊化,以便研究人員可以通過 Python 綁定快速修改組件或應用他們自己的算法,并從 Python 綁定自定義搜索和插件他們的算法。

  • Benchmark Performance Log Format

Frontends

前端將來自不同框架的模型攝取到 TVM 中。 tvm.relay.frontend 是模型攝取 API 的命名空間。

  • TensorFlow Frontend

Security

  • Security Guide

microTVM

  • microTVM Design Document
  • microTVM Project API
  • Model Library Format

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/532535.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532535.shtml
英文地址,請注明出處:http://en.pswp.cn/news/532535.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

遞歸+回溯

遞歸-回溯 本文參考自代碼隨想錄視頻: https://www.bilibili.com/video/BV1cy4y167mM https://www.bilibili.com/video/BV1ti4y1L7cv 遞歸回溯理論基礎 只要有遞歸,就會有回溯,遞歸函數的下面的部分通常就是回溯的邏輯。 回溯是純暴力的搜索…

Nvidia CUDA初級教程1 CPU體系架構綜述

Nvidia CUDA初級教程1 CPU體系架構綜述 視頻:https://www.bilibili.com/video/BV1kx411m7Fk?p2 講師:周斌 本節內容:了解現代CPU的架構和性能優化: 流水線 Pipelining分支預測 Branch Prediction超標量 Superscalar亂序執行 Out…

Nvidia CUDA初級教程2 并行程序設計概述

Nvidia CUDA初級教程2 并行程序設計概述 視頻:https://www.bilibili.com/video/BV1kx411m7Fk?p3 講師:周斌 本節內容: 為什么需要?怎么做?一些技術和概念 串并行計算模式 串行計算模式 常規軟件時串行的 設計運行…

Nvidia CUDA初級教程4 GPU體系架構概述

Nvidia CUDA初級教程4 GPU體系架構概述 視頻:https://www.bilibili.com/video/BV1kx411m7Fk?p5 講師:周斌 本節內容: 為什么需要GPU三種方法提升GPU的處理速度實際GPU的設計舉例: NVDIA GTX 480: FermiNVDIA GTX 680: Kepler GP…

Nvidia CUDA初級教程5 CUDA/GPU編程模型

Nvidia CUDA初級教程5 CUDA/GPU編程模型 視頻:https://www.bilibili.com/video/BV1kx411m7Fk?p6 講師:周斌 本節內容: CPU和GPU互動模式GPU線程組織模型(需要不停強化)GPU存儲模型基本的編程問題 CPU與GPU交互 各自…

Nvidia CUDA初級教程6 CUDA編程一

Nvidia CUDA初級教程6 CUDA編程一 視頻:https://www.bilibili.com/video/BV1kx411m7Fk?p7 講師:周斌 GPU架構概覽 GPU特別使用于: 密集計算,高度可并行計算圖形學 晶體管主要被用于: 執行計算而不是 緩存數據控制指令…

由前中后遍歷序列構建二叉樹

由前/中/后遍歷序列構建二叉樹 基礎 首先,我們需要知道前中后序三種深度優先遍歷二叉樹的方式的具體順序: 前序:中左右中序:左中右后序:左右中 另外,要知道只有中序前/后序可以唯一確定一棵二叉樹&…

手寫nms

手寫nms 計算寬高的時候加1是為什么? 本文總結自互聯網的多種nms實現,供參考,非博主原創,各原文鏈接如下,也建議大家動手寫一寫。 Ref: 淺談NMS的多種實現 目標窗口檢測算法-NMS非極大值抑制 一、fas…

目標檢測綜述

目標檢測綜述 轉自:https://zhuanlan.zhihu.com/p/383616728 論文參考:[Object Detection in 20 Years: A Survey][https://arxiv.org/abs/1905.05055] 引言 目標檢測領域發展至今已有二十余載,從早期的傳統方法到如今的深度學習方法&#x…

Nvidia CUDA初級教程7 CUDA編程二

Nvidia CUDA初級教程7 CUDA編程二 視頻:https://www.bilibili.com/video/BV1kx411m7Fk?p8 講師:周斌 本節內容: 內置類型和函數 Built-ins and functions線程同步 Synchronizing線程調度 Scheduling threads存儲模型 Memory model重訪 Matr…

詳解優酷視頻質量評價體系

萬字長文 | 詳解優酷視頻質量評價體系 分享嘉賓|李靜博士,阿里巴巴文娛集團資深算法專家,阿里巴巴大文娛摩酷實驗室視頻體驗與質量團隊負責人 整理出品|AICUG人工智能社區 本文地址:https://www.6aiq.com/article/1617…

視頻質量評價:挑戰與機遇

視頻質量評價:挑戰與機遇 轉自:https://zhuanlan.zhihu.com/p/384603663 本文整理自鵬城實驗室助理研究員王海強在LiveVideoStack線上分享上的演講。他通過自身的實踐經驗,詳細講解了視頻質量評價的挑戰與機遇。 文 / 王海強 整理 / LiveVi…

關于二分法的邊界問題及兩種寫法

關于二分法的邊界問題及兩種寫法 二分查找法大家很熟悉了,對于一個有序序列,我們可以通過二分查找法在 O(logN)O(logN)O(logN) 的時間內找到想要的元素。但是,在代碼實現的過程中,如果沒有仔細理解清楚,二分法的邊界條…

LeetCode上的各種股票最大收益

LeetCode上的各種股票最大收益 對于力扣平臺上的股票類型的題目: 121 買賣股票的最佳時機 122 買賣股票的最佳時機 II 123 買賣股票的最佳時機 III 124 買賣股票的最佳時機 IV 309 最佳買賣股票時機含冷凍期 714 買賣股票的最佳時機含手續費 劍指 Offer 63. …

建設專業化運維服務團隊必要性

信息系統的生命周期涵蓋:設計、開發、測試、部署上線、運行維護。其中,運行維護階段是信息系統生命周期中的關鍵環節,其執行效果直接影響系統是否能達到預期的運行目標。為了實現這個目標,我們必須建立一個以業務服務為導向的專業…

docker初探

docker初探 本文旨在介紹 docker 基本的安裝、常用命令和常見概念的辨析,方便新手入門和筆者日后查閱,大部分內容整理自互聯網,原出處在文中注明。 文章目錄docker初探docker安裝(mac)版本、信息相關命令version/info…

ubuntu安裝zsh、oh-my-zsh及常用配置

ubuntu安裝zsh、oh-my-zsh及常用配置 目前,ubuntu默認的shell是bash,但還有一種shell,叫做zsh它比bash更加強大,功能也更加完善,zsh雖說功能強大,但是配置比較復雜導致流行度不是很高 但是好東西終究是好…

Segmentaion標簽的三種表示:poly、mask、rle

Segmentaion標簽的三種表示:poly、mask、rle 不同于圖像分類這樣比較簡單直接的計算機視覺任務,圖像分割任務(又分為語義分割、實例分割、全景分割)的標簽形式稍為復雜。在分割任務中,我們需要在像素級上表達的是一張…

tensorboard報錯:ValueError Duplicate plugins for name projector 問題的出現及解決過程

tensorboard報錯:ValueError: Duplicate plugins for name projector 問題的出現及解決過程 記錄如題問題的出現及解決過程。 報錯命令及信息 筆者在終端調用 tensorboard 時: tensorboard --logdirruns/ --bind_all報錯: raise ValueEr…

發布自己的Python包(Pypi)

發布自己的Python包(Pypi) 我們經常使用 Pypi 來安裝包,但是有時候我們也想要發布自己的 Pypi 包,有可能我們寫了一個特別牛的包,也有可能我們只是想使用自己常用的一些輪子,可能這是我們日常編碼中很常用的一些輪子,…