MLIR:编译器基础架构重定义
MLIR: 编译器基础架构重定义
MLIR(多级中间表⽰)是语⾔(如 C)或库(如 TensorFlow)与编译器后端(如 LLVM)之间的中间表⽰ (IR) 系统。允许不同语⾔的不同编译器堆栈之间的代码重⽤以及其他性能和可⽤性优势。
MLIR 由Google开发为⼀个开源项⽬,主要是为了改进 TensorFlow 在不同后端的⽀持,但通常可⽤于任何语⾔。
背景
要了解 MLIR 的适⽤范围,需要简要概述 C、Java 和 Swift 等常⽤语⾔的编译器基础架构,然后继续介绍 TensorFlow 的编译器基础架构。这样,MLIR 的原理和可交付成果的想法就很清楚了。
编译器架构情况:
从 C 的编译器基础结构开始。C 编译器接收 C 代码,转换为抽象语法树 (AST),然后将其转换为 LLVM IR。AST 是⼀种树数据结构,其中节点表⽰算⼦等代码组件,叶节点表⽰数据。例如,像a+b这样的代码语句的抽象语法树如下:
随后,抽象语法树被转换为 LLVM IR(中间表⽰)。LLVM 是⼀种编译器基础架构,可将 LLVM IR 转换为机器代码。LLVM 是⼀种常⽤⼯具,是最流⾏的语⾔(如 C、C++、JAVA、Swift 等)的⼀部分。
所以,C代码的流程如下:
C 代码由 C 编译器(如 GCC)转换为 Clang AST
Clang AST 通过 C 编译器(如 GCC)转换为 LLVM IR
LLVM IR 由 LLVM 转换为机器码
C 的这种编译策略存在⼀些问题,例如:
在 AST 中,将代码语句链接到代码⾏号或源代码的信息丢失了,因为 AST ⽆法存储此类信息。导致⽆法指向源代码的错误。⼀个常见的例⼦是,当发⽣segmentation fault时,错误信息并没有说明是源代码中的哪⼀⾏导致了这个问题。
由于 C 代码直接转换为 AST,不会进⾏特定于语⾔的优化。事实上,如果开发了 C 库,则在此流程中⽆法在编译器期间进⾏特定于库的优化。
Java 和 Swift 等语⾔采⽤了不同的⽅法来解决这个问题。
Java是第⼀个解决这个问题成功的语⾔。Java 的⽅法是将 Java 代码转换为 Java 字节码 (JavaBC),是 Java 的内部表⽰。正是在这种表⽰进⾏了 Java 特定和库特定的优化。在此之后,Java BC 被转换为 LLVM IR,后者被 LLVM 转换为机器代码。因此,Java 的⽅法是避免创建 AST,是任何程序的通⽤表⽰,因此创建了⾃⼰的格式,不仅解决了 C 的问题,⽽且使 Java 成为第⼀个独⽴于平台的语⾔。
Java ⽅法的⼀个问题是变得⾮常复杂,需要深⼊了解 LLVM 才能充分利⽤。这将现有资源发挥到极致,以获得最佳优化。
其它语⾔复制 Java 的技术是⼀个问题,所以后来,Swift 提出了⾃⼰的⽅法,并被⼴泛采⽤。
Swift代码被转换为 AST,其中 Swift 特定表⽰被转换为 Swift 的内部表⽰。正是在这⼀点上,所有语⾔特定的优化都完成了。随后,Swift IR 被转换为 LLVM IR,后者被 LLVM 获取并转换为机器代码。
这是⼀种相对简单的⽅法,解决了所有 C 的原始问题。在功能上,与 Java 的⽅法相同。Swift 的⽅法变得⾮常流⾏,并被改编成Rust等新语⾔。
此时的主要问题,每当创建⼀种新语⾔时,必须再次进⾏所有优化,例如程序流程优化、数据结构优化等(以前的语⾔已经完成)。唯⼀不同的是语⾔特定的优化。LLVM 负责机器级优化。因此,必须为每种语⾔再次创建整个管道。
如果仔细观察,语⾔仅在操作抽象和内部语⾔特定优化⽅⾯有所不同。为了帮助创建新的语⾔和库,Google TensorFlow 团队决定创建MLIR(多级中间表⽰)。
实际上,这个问题是在 TensorFlow 中遇到的,⾕歌并没有专门为 TF 解决这个问题,⽽是决定⼀劳永逸地修复编译器基础架构。
TensorFlow 的编译器基础架构:
流程如下:
可以使⽤API 之⼀(如 C++ 或 Python)来编写 TensorFlow 代码
TensorFlow 代码被转换为 TF 图,Gappler(TensorFlow 中的⼀个模块)进⾏图级优化,包括⼏个机器学习特定的优化,如算⼦融合。
在此之后,TF 图被转换为其后端之⼀(替代 LLVM)的众多内部表⽰ (IR) 之⼀。
TensorFlow 有许多后端,如 XLA(⽤于 TPU)、TF Lite(⽤于移动设备)等
问题是对于每个路径(如 XLA 和 TF Lite),开发⼈员必须重新实现所有优化,并且⼤多数优化是通⽤的,但代码重⽤是不可能的。这个问题将通过 MLIR 解决。
因此,MLIR 的流程如下图所⽰:
概括:
不同语⾔的相同编译器基础结构,不可重⽤
类似于 Swift 和其他语⾔中的编译器基础结构
tensorflow ⽀持许多具有不同 IR 的后端,⽆需代码重⽤
⽬的是增加代码重⽤以快速适应新的硬件后端
中间 IR ⽤于捕获数据流信息并应⽤优化、⽐ LLVM 更好的源代码跟踪、灵活的设计、重⽤ LLVM 进⾏机器代码⽣成
使⽤ MLIR 的优势
使⽤ MLIR 的优势:
默认情况下源代码位置跟踪
默认情况下,所有功能都在多核上运⾏(使⽤ OpenMP)
更好地重⽤编译器堆栈的代码(⽤于新库和硬件),重⽤其它语⾔完成的优化
通过开发 CIL IR 为 C 提供更好的编译器堆栈
对于 TensorFlow,⼀条路径中的优化可以在其它路径中重⽤,从⽽实现⼤量代码重⽤。
内部关键思想
MLIR 的主要思想是:
没有预定义的类型或指令。这允许开发⼈员⾃定义数据类型和指令抽象
没有预定义的操作
MLIR 中的基本对象是⼀种⽅⾔dialects,从实现的⾓度可以认为是⼀个类
需要定义指向 C++ 代码的⽅⾔dialects,⽅⾔dialects就像⾃定义语⾔的类
⽅⾔dialects有 3 部分:函数名、输⼊参数列表、输出参数列表
对于每种⽅⾔dialects,默认功能如类型检查、映射到 LLVM IR
MLIR 默认⽀持线性代数运算,例如⽅⾔dialects之间的类型检查
语⾔被转换为带有⽅⾔dialects的形式
在这种形式(⽅⾔dialects集)上,执⾏优化
经过各种级别的lowering,⽅⾔dialects可以直接转换为LLVM IR
在 MLIR 中更好地使⽤ OpenMP,所有信息都可⽤
python转java代码默认情况下,所有功能都在所有内核上运⾏(更好的系统使⽤)
(优于 LLVM)每个操作数都有⼀个源代码内存地址属性,直接指向发⽣错误的源代码⾏
将使⽤ XLA 基础架构进⾏性能分析和剖析。
应⽤
MLIR⽤于改进 TensorFlow 编译器基础架构:
1. 通过源代码跟踪改进 TF Lite 的⽣成
编译流程如下:
TF代码
⽅⾔dialects
验证/优化
⽤于 tf lite 后端的 tf lite ⽅⾔dialects
1. 使⽤ MLIR 更改 XLA HLO 的路径
编译流程如下:
tf
⽅⾔dialects
验证/优化
xla⽅⾔dialects
xla后端
它将重⽤来⾃ TF Lite 路径的⼀些组件
1. 使⽤ MLIR ⽀持新的编译器后端
当⼀个新平台 P 出现并且它有⼀个新的后端 B 时,然后转换 TensorFlow 代码通过使⽤ TF Lite 和 XLA 的现有代码,转换为现有的⽅⾔dialects/IR。接下来,需要编写⼀个新的⽅⾔dialects集/IR(针对平台进⾏⾃定义优化)和转换代码,转换为平台后端编译器所需的 IR。MLIR 的使⽤使该过程更易于管理和快速。
其它应⽤
MLIR 是⼀种通⽤⼯具,可⽤于其它⽬的,例如:
通过 MLIR 插⼊新的 IR 来解决 C 和 C++ 代码的问题
新语⾔可以直接使⽤使⽤ MLIR 的现有语⾔的优化,因此开发新语⾔将变得容易和快速。
⼀旦使⽤MLIR的系统实施,⼀组编译技术的创新可以被多个组轻松使⽤
简⽽⾔之,LLVM 极⼤地创新了编译器⽣态系统,但现在 MLIR 通过利⽤ LLVM 的最佳部分实现更简单、更⾼效的编译器⽣态系统,从⽽进⼊了下⼀阶段。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。