编译器基础介绍

news2024/11/15 8:07:25

        随着深度学习的不断发展,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.coloradmin.cn/o/2097311.html

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

相关文章

中资优配:什么股票容易涨停?放量涨停意味着什么?

什么股票简单涨停: 1、抢手题材股。每当商场出现新的方针改变、职业改造或严重利好事情时,相关股票往往会遭到资金的会合注重,从而引发涨停潮。例如一些职业股票在方针支撑和技术打破的两层驱动下,很简单成为商场短线资金追逐的抢…

qt实现三原色滑动条变色

在qt中有这样一个控件: 就是这个Horizontal Slider他的作用相信大家都知道了,也就是通过滑动来改变数值。今天我们就使用这个控件实现一个三原色滑动变色。 实现效果: 1.创建UI界面 这个就不用多说了,这个大家就按照我的这个去…

数据结构-双链表-详解

数据结构-双链表-详解 1.前言2.结构2.1双向2.2带头2.3循环 3.实现3.1结构体3.2初始化与删除初始化删除 3.3插入尾插头插 3.4删除尾删头删 3.4查找3.5pos位置的插入删除 1.前言 链表总共有八种:双向、单向;带头、不带头;循环、不循环。(8 2 …

C++第四十四弹---Lambda表达式的妙用:高效解决编程中的匿名函数问题

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1 lambda表达式 1.1 C98中的一个例子 1.2 lambda表达式 1.3 lambda表达式语法 1.4 函数对象与lambda表达式 1 lambda表达式 1.1 C98中的一个例…

上门家政系统

上门家政系统是基于likeadmin开发的上门家政预约系统,提供全部前后台无加密源代码,拥有强大的地图定位、在线预约、系统派单、指定派单、下单支付、核销订单等功能模块,用户端和师傅端完美融合,随时随地都能接单;可自定…

vs中在工具箱添加自定义控件numberTextBox

在winform中没有numberTextBox控件,为了将numberTextBox添加到工具箱当中,于是参考以下博客封装了一个自定义numberTextBox控件: winform 自定义数值(数字)输入框_c#如何设置一个框输入值-CSDN博客 接下来介绍以下封装和引用的过程&#xf…

淘宝直通车投放优化(进阶版本)

相爱相杀何时休,只看今篇论英雄! 看目录上电梯,各去对应楼层耍。顶楼的风景最迷人。38719字诸君细读。 第一阶段:基础认识和推广(新手可从头看起) 第二阶段:明确原理与逻辑(运营/推广同学从此看做到心中有数) 第三阶段:实战中涅槃(知其然,知其所以然) 第一阶…

MySQL之DQL简单查询

1、结构化查询语言 1. 什么是SQL 结构化查询语言(Structured Query Language),后续通常简称SQL。SQL是用于存取数据以及查询、更新和管理关系数据库系统的标准语言。20世纪70年代由IBM公司开发,目前应用于各种关系型数据库。SQL是一套标准,…

黄金市场步入数据驱动的关键周:多空角力聚焦非农就业

一、市场情绪分化,黄金波动趋于平稳 本周黄金市场在经历了一系列波动后,价格维持在2500美元/盎司上方波动,显示出相对稳定的交易状态。Kitco新闻黄金调查显示,分析师对下周金价走势的看法分歧较大,而零售投资者看涨情绪…

TCP/IP网络编程:第18章聊天室

服务端&#xff1a;负责连接客户端&#xff0c;转发客户端的信息给其他客户 客户端&#xff1a;发送信息给服务端&#xff0c;接收服务端传来的其他客户的信息 服务端代码&#xff1a; #include <stdio.h> #include <stdlib.h> #include <unistd.h> #incl…

OpenCV开发笔记(八十):基于特征点匹配实现全景图片拼接

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/141790116 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

YOLOS:大道至简,直接使用预训练ViT进行实时目标检测 | NeurIPS 2021

论文探索了在中型ImageNet-1k数据集上预训练的普通ViT到更具挑战性的COCO目标检测基准的可迁移性&#xff0c;提出了基于Vision Transformer的You Only Look at One Sequence(YOLOS)目标检测模型。在具有挑战性的COCO目标检测基准上的实验结果表明&#xff0c;2D目标检测可以以…

Windows系统通过WSL2安装Ubuntu22.04系统及图形化界面

1.通过WSL2安装Ubuntu22.04系统及图形化界面 WSL&#xff08;Windows Subsystem for Linux&#xff09;是一个为Windows用户设计的兼容层&#xff0c;它允许用户在Windows10和Windows11操作系统上直接运行GNU/Linux环境。WSL提供了一个微软开发的Linux兼容内核接口&#xff0c…

MySQL中的时间与当前时间相差8个小时,两步解决。

前言&#xff1a; 有朋友后台私信问&#xff0c;我服务器中的MySQL时间与本地时间差了8个小时&#xff0c;我在给表中字段设置根据时间戳自动更新时会出现时间与当前时间不符的原因。灰常简单&#xff0c;一起来看看吧。 第一步&#xff1a; 在MySQL中&#xff0c;输入命令&…

基于asp.net软件缺陷跟踪系统设计与实现

系统分析 本章讲述了本软件缺陷跟踪系统的前期分析方法和分析结论。 3.1 可靠性分析 在软件研发企业或研发团队中&#xff0c;开发人员和测试人员所应用的软件缺陷管理的主要方法流程是&#xff1a; 1&#xff0e;测试人员发现软件存在的缺陷&#xff0c;填写缺陷报告&…

【正点原子K210连载】第三十四章 image图像滤波实验 摘自【正点原子】DNK210使用指南-CanMV版指南

第三十四章 image图像滤波实验 在上一章节中&#xff0c;介绍了image模块中元素绘制方法给的使用&#xff0c;本章将继续介绍image模块中图像滤波方法的使用。通过本章的学习&#xff0c;读者将学习到image模块中图像滤波的使用。 本章分为如下几个小节&#xff1a; 34.1 imag…

Transformer直接预测完整数学表达式,推理速度提高多个数量级

前言 来自 Mata AI、法国索邦大学、巴黎高师的研究者成功让 Transformer 直接预测出完整的数学表达式。 转载自丨机器之心 符号回归&#xff0c;即根据观察函数值来预测函数数学表达式的任务&#xff0c;通常涉及两步过程&#xff1a;预测表达式的「主干」并选择数值常数&am…

jfif怎么改成jpg格式?这几种转换方法请务必学会!

jfif怎么改成jpg格式&#xff1f;JFIF&#xff0c;作为一种相对不常见的图像存储格式&#xff0c;其在实际应用中确实存在一系列不容忽视的局限&#xff0c;首要问题在于&#xff0c;当尝试将JFIF图片转换为其他格式时&#xff0c;往往会伴随着图像压缩的副作用&#xff0c;这意…

Vue学习笔记 一

Vue学习笔记 1、Vue基础指令 1.1 什么是Vue? Vue.js 是一套响应式的 JavaScript 开发库。Vue.js 自问世以来所受关注度不断提高,在现在的市场上,Vue.js 是非常流行的 JavaScript 技术开发框架之一。 Vue是一款国产前端框架,它的作者尤雨溪(Evan You)是一位美籍华人,…

Android使用addr2line分析Native Crash

NDK提供的工具将函数地址解析为具体的函数名和行数才能进一步分析问题。 常用的地址转换工具有addr2line、ndk-stack等&#xff0c;个人比较喜欢addr2line&#xff0c;所以接下来介绍下该工具的基本使用方式 日常使用过程中&#xff0c;只需要关注-C -f -e三个参数即可 // -…