编译器基础介绍

        随着深度学习的不断发展,AI 模型结构在快速演化,底层计算硬件技术更是层出不穷,对于广大开发者来说不仅要考虑如何在复杂多变的场景下有效的将算力发挥出来,还要应对 AI 框架的持续迭代。AI 编译器就成了应对以上问题广受关注的技术方向,让用户仅需专注于上层模型开发,降低手工优化性能的人力开发成本,进一步压榨硬件性能空间。

1. 编译器与解释器

编译器(Compiler)和解释器(Interpreter)是两种不同的工具,都可以将编程语言和脚本语言转换为机器语言。虽然两者都是将高级语言转换成机器码,但是其最大的区别在于:解释器在程序运行时将代码转换成机器码,编译器在程序运行之前将代码转换成机器码

机器语言:机器语言程序是由一系列二进制模式组成的(例如 110110),表示应该由计算机执行的简单操作。机器语言程序是可执行的,所以可以直接在硬件上运行。

编译器 Compiler

编译器可以将整个程序转换为目标代码(object code),这些目标代码通常存储在文件中。目标代码也被称为二进制代码,在进行链接后可以被机器直接执行。典型的编译型程序语言有 C 和 C++。

编译器的几个重要的特点:

  1. 编译器读取源程序代码,输出可执行机器码,即把开发者编写的代码转换成 CPU 等硬件能理解的格式
  2. 将输入源程序转换为机器语言或低级语言,并在执行前并报告程序中出现的错误
  3. 编译的过程比较复杂,会消耗比较多的时间分析和处理开发者编写的程序代码
  4. 可执行结果,属于某种形式的特定于机器的二进制代码

目前主流如 LLVM 和 GCC 等经典的开源编译器的类型分为前端编译器、中间层编译器、后端编译器。1)编译器的分析阶段也称为前端编译器,将程序划分为基本的组成部分,检查代码的语法、语义和语法,然后生成中间代码。2)中间层主要是对源程序代码进行优化和分析,分析阶段包括词法分析、语义分析和语法分析;优化主要是优化中间代码,去掉冗余代码、子表达式消除等工作。3)编译器的合成阶段也称为后端,针对具体的硬件生成目标代码,合成阶段包括代码优化器和代码生成器。

解释器 Interpreter

解释器能够直接执行程序或脚本语言中编写的指令,而不需要预先将这些程序或脚本语言转换成目标代码或者机器码。典型的解释型语言有 Python、PHP 和 Matlab。

解释器的几个重要的特点:

  1. 将一个用高级语言编写的程序代码翻译成机器级语言
  2. 解释器在运行时,逐行转换源代码为机器码
  3. 解释器允许在程序执行时,求值和修改程序
  4. 用于分析和处理程序的时间相对较少
  5. 与编译器相比,程序执行相对缓慢

两者最大的差别在于编译器将一个程序作为一个整体进行翻译,而解释器则一条一条地翻译一个程序。编译器的情况下生成中间代码或目标代码,而解释器不创建中间代码。在执行效率上,编译器比解释器要快得多,因为编译器一次完成整个程序,而解释器则是依次编译每一行代码,非常的耗时。从资源占用方面来看,由于要生成目标代码,编译器比解释器需要更多的内存。

实际上编程的体验差异也非常大,编译器同时显示所有错误,很难检测错误,而解释器则逐个显示每条语句的错误,更容易检测错误。具体的,在编译器中,当程序中出现错误时,它会停止翻译,并在删除错误后重新翻译整个程序。相反,当解释器中发生错误时,它会阻止其翻译,在删除错误后,翻译才继续执行。

2. JIT 和 AOT 编译方式

目前,程序主要有两种运行方式:静态编译动态解释

  • 静态编译的代码程序在执行前全部被翻译为机器码,通常将这种类型称为 AOT(Ahead of time),即“提前编译”;

  • 动态解释的程序则是对代码程序边翻译边运行,通常将这种类型称为 JIT(Just in time),即“即时编译”。

AOT 程序的典型代表是用 C/C++ 开发的应用,其必须在执行前编译成机器码,然后再交给操作系统具体执行;而 JIT 的代表非常多,如 JavaScript、Python 等动态解释的程序。

事实上,所有脚本语言都支持 JIT 模式。但需要注意的是 JIT 和 AOT 指的是程序运行方式,和编程语言本身并非强关联的,有的语言既可以以 JIT 方式运行也可以以 AOT 方式运行,如 Java 和 Python。它们可以在第一次执行时编译成中间字节码,之后就可以直接执行字节码。

也许有人会说,中间字节码并非机器码,在程序执行时仍然需要动态将字节码转为机器码。理论上讲这没有错,不过通常区分是否为 AOT 的标准就是看代码在执行之前是否需要编译,只要需要编译,无论其编译产物是字节码还是机器码,都属于 AOT 的方式。

优缺点对比

下面是 JIT 和 AOT 两种编译方式的优点对比。在 JIT 中其优点为:

  1. 可以根据当前硬件情况实时编译生成最优机器指令
  2. 可以根据当前程序的运行情况生成最优的机器指令序列
  3. 当程序需要支持动态链接时,只能使用 JIT 的编译方式
  4. 可以根据进程中内存的实际情况调整代码,使内存能够更充分的利用

但是 JIT 缺点也非常明显:

  1. 编译需要占用运行时 Runtime 的资源,会导致进程执行时候卡顿
  2. 编译占用运行时间,对某些代码编译优化不能完全支持,需在流畅和时间权衡
  3. 在编译准备和识别频繁使用的方法需要占用时间,初始编译不能达到最高性能

相对而言,JIT 的缺点也是 AOT 的优点所在:

  1. 在程序运行前编译,可以避免在运行时的编译性能消耗和内存消耗
  2. 可以在程序运行初期就达到最高性能
  3. 可以显著的加快程序的执行效率

其 AOT 的优点之下,也会带来一些问题:

  1. 在程序运行前编译会使程序安装的时间增加
  2. 将提前编译的内容保存起来,会占用更多的内存
  3. 牺牲高级语言的一致性问题

在 AI 框架中区别

目前主流的 AI 框架,都会带有前端的表达层,再加上 AI 编译器对硬件使能,因此 AI 框架跟 AI 编译器之间关系非常紧密,部分如 MindSpore、TensorFlow 等 AI 框架中默认包含了自己的 AI 编译器。目前 PyTorch2.X 版本升级后,也默认自带 Inductor 功能特性,可以对接多个不同的 AI 编译器。

静态编译的代码程序在执行前全部被翻译为机器码,这种 AOT(Ahead of time),即提前编译的方式,AOT 更适合移动、嵌入式深度学习应用。在 MLIR + TensorFlow 框架中目前支持 AOT 和 JIT 的编译方式,不过在 AI 领域,目前 AOT 的典型代表有:

1)推理引擎,在训练的之后 AI 编译器把网络模型提前固化下来,然后在推理场景直接使用提前编译好的模型结构,进行推理部署;2)静态图生成,通过 AI 编译器对神经网络模型表示称为统一的 IR 描述,接着在真正运行时执行编译后的内容。

另一方面,动态解释的程序则是对代码程序边翻译边运行,称为 JIT(Just in time),即即时编译。典型的代表有:

1)PyTorch 框架中的 JIT 特性,可以将 Python 代码实时编译成本地机器代码,实现对神经网络模型的优化和加速。2)清华发布的计图(Jittor),完全基于动态编译 JIT,内部使用创新的元算子和统一计算图的 AI 框架,元算子和 Numpy 一样易于使用,并且超越 Numpy 能够实现更复杂更高效的操作。基于元算子开发的神经网络模型,可以被计图实时的自动优化并且运行在指定的硬件上。

3. Pass 和中间表示 IR

编译器是提高开发效率的工具链中不可或缺的部分。但是编译器被很多程序员和开发者视为黑箱,输入高层次的源程序程序,产生语义不变的低层次机器码。此时,编译器的内部结构中,Pass 作为编译优化中间层的一个遍历程序或者模块,中间表示 (intermediate representation,IR) 负责串联起编译器内各层级和模块。

Pass 定义和原理

Pass 主要是对源程序语言的一次完整扫描或处理。在编译器中,Pass 指所采用的一种结构化技术,用于完成编译对象(IR)的分析、优化或转换等功能。Pass 的执行就是编译器对编译单元进行分析和优化的过程,Pass 构建了这些过程所需要的分析结果。

一个 Pass 通常会完成一项较为独立的功能,例如 LoopUnroll Pass 会进行循环展开的操作。但 Pass 与 Pass 之间可能会存在一些依赖,部分 Pass 的执行会依赖于其它一些 Pass 的分析或者转换结果。

现代编译器中,一般会采用分层、分段的结构模式,不管是在中间层还是后端,都存在若干条优化的 Pipeline,而这些 Pipeline,则是由一个个 Pass 组成的,对于这些 Pass 的管理,则是由 PassManager 完成的。

在编译器 LLVM 中提供的 Pass 分为三类:Analysis pass、Transform pass 和 Utility pass。

  • Analysis Pass:计算相关 IR 单元的高层信息,但不对其进行修改。这些信息可以被其他 Pass 使用,或用于调试和程序可视化。换言之,Analysis Pass 会从对应的 IR 单元中挖掘出需要的信息,然后进行存储,并提供查询的接口,让其它 Pass 去访问其所存储的信息。同时,Analysis Pass 也会提供 invalidate 接口,因为当其它 Pass 修改了 IR 单元的内容后,可能会造成已获取的分析信息失效,此时需调用 invalidate 接口来告知编译器此 Analysis Pass 原先所存储的信息已失效。常见的 Analysis Pass 有 Basic Alias Analysis、Scalar Evolution Analysis 等。

  • Transform Pass:可以使用 Analysis Pass 的分析结果,然后以某种方式改变和优化 IR。此类 Pass 是会改变 IR 的内容的,可能会改变 IR 中的指令,也可能会改变 IR 中的控制流。例如 Inline Pass 会将一些函数进行 inline 的操作,从而减少函数调用,同时在 inline 后可能会暴露更多的优化机会。

  • Utility Pass:是一些功能性的实用程序,既不属于 Analysis Pass,也不属于 Transform Pass。例如,extract-blocks Pass 将 basic block 从模块中提取出来供 bug point 使用,它仅完成这项工作。

IR 中间表示

IR(Intermediate Representation)中间表示,是编译器中很重要的一种数据结构。编译器在完成前端工作以后,首先生成其自定义的 IR,并在此基础上执行各种优化算法,最后再生成目标代码。

从广义上看,编译器的运行过程中,中间节点的表示,都可以统称为 IR。从狭义上讲编译器的 IR,是指该编译器明确定义的一种具体的数据结构,这个数据结构通常还伴随着一种语言来表达程序,这个语言程序用来实现这个明确定义的 IR。大部分时间,不太严格区分这个明确定义的 IR 以及其伴随的语言程序,将其统称为 IR。

在编译原理中,通常将编译器分为前端和后端。其中,前端会对所输入的程序进行词法分析、语法分析、语义分析,然后生成中间表达形式 IR。后端会对 IR 进行优化,然后生成目标代码。

例如:LLVM 把前端和后端给拆分出来,在中间层明确定义一种抽象的语言,这个语言就叫做 IR。定义了 IR 以后,前端的任务就是负责最终生成 IR,优化器则是负责优化生成的 IR,而后端的任务就是把 IR 给转化成目标平台的语言。LLVM 的 IR 使用 LLVM assembly language 或称为 LLVM language 来实现 LLVM IR 的类型系统,就指的是 LLVM assembly language 中的类型系统。

因此,编译器的前端,优化器,后端之间,唯一交换的数据结构类型就是 IR,通过 IR 来实现不同模块的解耦。有些 IR 还会为其专门起一个名字,比如:Open64 的 IR 通常叫做 WHIRL IR,方舟编译器的 IR 叫做 MAPLE IR,LLVM 则通常就称为 LLVM IR。

IR 的定义

IR 在通常情况下有两种用途,1)一种是用来做分析和变换,2)一种是直接用于解释执行。

编译器中,基于 IR 的分析和处理工作,前期阶段可以基于一些抽象层次比较高的语义,此时所需的 IR 更接近源代码。而在编译器后期阶段,则会使用低层次的、更加接近目标代码的语义。基于上述从高到低的层次抽象,IR 可以归结为三层:高层 HIR、中间层 MIR 和底层 LIR。

  1. HIR

HIR(High IR)高层 IR,其主要负责基于源程序语言执行代码的分析和变换。假设要开发一款 IDE,主要功能包括:发现语法错误、分析符号之间的依赖关系(以便进行跳转、判断方法的重载等)、根据需要自动生成或修改一些代码(提供重构能力)。此时对 IR 的需求是能够准确表达源程序语言的语义即可。

其实,AST 和符号表就可以满足上述需求。也就是说,AST 也可以算作一种特殊的 IR。如果要开发 IDE、代码翻译工具(从一门语言翻译到另一门语言)、代码生成工具、代码统计工具等,使用 AST(加上符号表)即可。基于 HIR,可以执行高层次的代码优化,比如常数折叠、内联关联等。在 Java 和 Go 的编译器中,有不少基于 AST 执行的优化工作。

  1. MIR

MIR(Middle IR),独立于源程序语言和硬件架构执行代码分析和具体优化。大量的优化算法是通用的,没有必要依赖源程序语言的语法和语义,也没有必要依赖具体的硬件架构。这些优化包括部分算术优化、常量和变量传播、死代码删除等,实现分析和优化功能。

因为 MIR 跟源程序代码和目标程序代码都无关,所以在编译优化算法(Pass)过程中,通常是基于 MIR,比如三地址代码(Three Address Code,TAC)。

三地址代码 TAC 的特点:最多有三个地址(也就是变量),其中赋值符号的左边是用来写入,右边最多可以有两个地址和一个操作符,用于读取数据并计算。

  1. LIR

LIR(Low IR),依赖于底层具体硬件架构做优化和代码生成。其指令通常可以与机器指令一一对应,比较容易翻译成机器指令或汇编代码。因为 LIR 体现了具体硬件(如 CPU)架构的底层特征,因此可以执行与具体 CPU 架构相关的优化。

多层 IR 和单层 IR 比较起来,具有较为明显的优点:

  1. 可以提供更多的源程序语言的信息
  2. IR 表达上更加地灵活,更加方便优化
  3. 使得优化算法和优化 Pass 执行更加高效

如在 LLVM 编译器里,会根据抽象层次从高到低,采用了前后端分离的三段结构,这样在为编译器添加新的语言支持或者新的目标平台支持的时候,就十分方便,大大减小了工程开销。而 LLVM IR 在这种前后端分离的三段结构之中,主要分开了三层 IR,IR 在整个编译器中则起着重要的承上启下作用。从便于开发者编写程序代码的理解到便于硬件机器的理解。

4. 小结

  • 解释器是一种计算机程序,将每个高级程序语句转换成机器代码

  • 编译器把高级语言程序转换成机器码,即将人可读的代码转换成计算机可读的代码

  • Pass 主要是对源程序语言的一次完整扫描或处理

  • 中间表示 IR 是编译器中的一种数据结构,负责串联起编译器内各层级和模块

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

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

相关文章

论文翻译:Scaling Instruction-Finetuned Language Models

Scaling Instruction-Finetuned Language Models https://www.jmlr.org/papers/volume25/23-0870/23-0870.pdf 指令微调语言模型 文章目录 指令微调语言模型摘要1. 引言2. Flan微调2.1 微调数据2.2 微调过程2.3 评估协议 3. 扩展到5400亿参数和1836个任务4. 带有思维链注释的微…

HMI触屏网关-VISION如何与Modbus TCP从机通信

上文:HMI触屏网关-VISION如何与Modbus RTU从机通信-CSDN博客 1. 硬件连接 Modbus TCP协议采用网口通信的方式,因此,只需要保证网关的LAN口IP和Modbus TCP从机的IP在同一网段即可。 Modbus TCP从机参数说明: 2. VISION创建Modbu…

LaViT:这也行,微软提出直接用上一层的注意力权重生成当前层的注意力权重 | CVPR 2024

Less-Attention Vision Transformer利用了在多头自注意力(MHSA)块中计算的依赖关系,通过重复使用先前MSA块的注意力来绕过注意力计算,还额外增加了一个简单的保持对角性的损失函数,旨在促进注意力矩阵在表示标记之间关…

从0到1搭建用户管理系统

手把手教你搭建前后端框架 新手对于很多成熟框架,不知道如何搭建的,不知道如何实现等等,忙碌之余,写了一篇博客 手把手教你搭建前后端框架源码, springbootmysqlelementuivue 从0到1,搭建springboot框架&am…

出租车4G5G无线车载监控系统解决方案(下)

目录 一、项目概述 1.1 项目背景 1.2 设计原则 1.3 设计目标 1.4 实施意义 二、系统总体设计 2.1建设目标 2.2系统模式 2.3设计思路 2.4设计架构 2.5系统组成 2.6优势分析 2.7设备达到的功能要求 2.8系统组成 三、系统详细设计 3.1 出租车车载监控 3.1.1 系统功能设计 3.2系统前…

如何在退出Qt时保存用户配置

如何在退出Qt时保存用户配置 文章目录 如何在退出Qt时保存用户配置一、简介二、 保存配置数据(方法一)2.1 项目实现2.2 运行结果 三、保存配置数据(方法二)3.1 项目实现3.2 运行结果 四、写在最后 ​ 一、简介 在我们使用 Qt 进行…

吹爆上海交大的大模型实战教程!!非常详细收藏我这一篇就够了

各位好,这里是DASOU 今天分享一个上海交大的免费的大模型课程,有相关教程文档和Slides,目前是1.6K星标,还是挺火的 项目动机 《动手学大模型》系列编程实践教程,由上海交通大学2024年春季《人工智能安全技术》课程&…

MySQL集群

一、Mysql 在服务器中的部署方法 1.1源码安装 下载依赖性 dnf install cmake gcc-c openssl-devel ncurses-devel.x86_64 libtirpc-devel-1.3.3-8.el9_4.x86_64.rpm rpcgen.x86_64 解压压缩包并安装 tar zxf mysql-boost-5.7.44.tar.gz cd /root/mysql-5.7.44 cmake \ -DCM…

java写入word表格(poi-tl)

1.导入依赖 <!--poi-tl--> <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.0</version> </dependency>2.代码 自己创建模板。放在&#xff08;resource/file&#xff09;…

非标机械设计项目“规范”笔记

2.自动化设备开发特点与技术文件输出 2.1自动化设备 自动化设备 工业自动化设备类型&#xff1a;标准自动化、非标自动化 载具和治具 焊接治具---汽车行业用的多 压装、压合治具---3C行业 治具种类&#xff1a; 电木&#xff1a;测试治具箱体&#xf…

工作5年,没听过MySQL半同步复制,是我的问题吗?

目录 一、存储高可用二、读写分离三、解决主从复制延迟问题的几种方案1、写操作后的读操作指定发给数据库主服务器2、读从机失败后再读一次主机3、关键业务读写操作全部指向主机&#xff0c;非关键业务采用读写分离4、压缩与批量传输5、优化从库的查询性能6、优化网络延迟7、调…

构建大师:深入理解Linux下的Make和Makefile

引言 在软件开发的世界里&#xff0c;构建过程是一项繁琐而重要的任务。无论是简单的脚本还是复杂的软件项目&#xff0c;都需要一种方式来自动化编译、链接以及测试等过程。在Linux环境下&#xff0c;Make工具和它的配置文件——Makefile&#xff0c;成为了许多开发者构建项目…

RuoYi-Vue 最新 SpringBoot3 前后端分离版本源码分析

RuoYi-Vue 最新 SpringBoot3 前后端分离版本源码分析 RuoYi-Vue 本地环境部署若依菜单类型权限管理SpringSecurity 配置登录接口(认证管理)Authentication 认证token的生成 权限控制 异步任务管理操作日志数据权限 RuoYi-Vue 本地环境部署 直接去 gitee 上拉取最新版本即可&am…

<Rust>egui学习之小部件(三):如何为窗口UI元件设置布局(间隔、水平、垂直排列)?

前言 本专栏是关于Rust的GUI库egui的部件讲解及应用实例分析&#xff0c;主要讲解egui的源代码、部件属性、如何应用。 环境配置 系统&#xff1a;windows 平台&#xff1a;visual studio code 语言&#xff1a;rust 库&#xff1a;egui、eframe 概述 本文是本专栏的第三篇博…

C++和OpenGL实现3D游戏编程【连载7】——文字和汉字的显示

1、本节实现的内容 上一节我们讨论了纹理在二维平面内不规则图形贴图的相关基础操作,本节我们开始了解游戏里文字以及汉字的显示方法。本节课我们将从基本的ASCII字符显示,拓展到中文字符的显示,最后再讲到纹理字符的显示,并对各种文字显示方法的优缺点和使用场景进行分析…

使用Masscan扫描器进行信息搜集

Masscan 是一款极为高效的端口扫描工具&#xff0c;以其卓越的扫描速度和大规模扫描能力而著称。该工具不仅支持 TCP 和 UDP 协议的扫描&#xff0c;还允许用户根据需求灵活指定多个目标和端口。Masscan 通过采用先进的网络性能优化技术&#xff0c;充分利用操作系统的资源和多…

基于北斗+自组网技术的光伏电场人员位置监控系统优化方案

一、方案背景 1.1 用户需求 随着我国经济的快速发展&#xff0c;光伏电场等新能源项目的建设日益增多。然而&#xff0c;这些项目往往位于偏远地区&#xff0c;通信基础设施不完善&#xff0c;导致施工人员在作业过程中难以与外界保持实时联系。特别是在无人区或信号弱的地区…

【Qt 事件】—— 详解Qt事件处理

目录 &#xff08;一&#xff09;事件介绍 &#xff08;二&#xff09;事件的处理 &#xff08;三&#xff09;按键事件 3.1 单个按键 3.2 组合按键 &#xff08;四&#xff09;鼠标事件 4.1 鼠标单击事件 4.2 鼠标释放事件 4.3 鼠标双击事件 4.4 鼠标移动事件 4.5…

101.SAP MII功能详解(15)Workbench-Transaction Logic(Iterator)

目录 1.Logic->Iterator 2.演示 配置连接 Iterator使用示例 1.Logic->Iterator 您可以使用此操作迭代循环浏览List&#xff0c;迭代是指遍历某个List数据结构&#xff0c;逐个访问其元素的过程。迭代使用的场景不多。 2.演示 配置连接 Iterator使用示例 数据源是Lis…

Qt:玩转QPainter序列九(文本,文本框,填充)

前言 继续承接序列八 正文 1. drawImage系列函数 绘制图像 inline void drawImage(const QPoint &p, const QImage &image); 作用: 在指定的点 p 上绘制 QImage 图像。图像的左上角将对齐到 p 点。 inline void drawImage(int x, int y, const QImage &image,…