Golang编译优化——公共子表达式消除

文章目录

  • 一、概述
  • 二、公共子表达式消除
    • 2.1 初始划分等价值
    • 2.2 细分等价值
      • 2.2.1 给所有值标号
      • 2.2.2 根据参数细分等价值
    • 2.3 替换重复表达式

一、概述

公共子表达式消除(Common Subexpression Elimination,CSE)也有书上称为冗余表达式消除,旨在减少程序中重复计算相同表达式的次数,从而提高程序的执行效率。

在程序中,如果同一个表达式在不同的地方多次出现并且具有相同的输入,则这个表达式就是一个公共子表达式。公共子表达式消除的目标是识别这些重复的表达式,并将它们计算一次,然后在需要时重用结果,而不是每次都重新计算。

以下是一个简单的公共子表达式:

x = b + c
y = a - d
z = b + c

在这个例子中,表达式 b + c 在两个地方都出现了,它是一个公共子表达式。如果程序执行这两个语句,那么每次都重新计算 b + c,这可能会浪费计算资源。通过公共子表达式消除优化,可以将这个表达式计算一次,然后将结果存储起来,以后需要时直接使用存储的结果,而不是重新计算。

通过公共子表达式消除优化后的代码如下所示,程序就不再重复计算表达式b + c,而是引用已经计算的值x

x = b + c
y = a - d
z = x

】关于公共子表达式的介绍,在《编译器设计》的局部值编号、可用表达式、缓式代码移动中都有与之相关的讲解。要理解CSE算法还需要知道支配性、支配者树等知识,在《编译器设计》中总结过,后面我会写文章来讲解Golang对这些的实现。

二、公共子表达式消除

Golang中关于CSE的实现在文件src/cmd/compile/internal/ssa/cse.go中,算法的开始函数是cse(f *Func) 函数。CSE算法实现的步骤主要分为:

  • 初始划分等价值:算法会遍历函数中的每个基本块(Block),然后遍历每个基本块中的每个值(Value)。在遍历过程中,根据一组规则,将具有相同特征的值初始的划分为一组等价值,依赖规则在cmpVal(…)函数中。
  • 细分等价值:初始划分后,算法会进一步细分等价值,直到无法继续细分为止。细分等价值的过程主要是根据值的参数进行判断,如果一组等价值的参数不是等价值,则将其分割成不同的等价值。在细分等价值的过程中,算法会对每组等价值按照一定的顺序进行排序,以便进行比较和查找。
  • 替换重复表达式:细分等价值后,算法会对每组等价值选择一个代表值,然后将该等价值中的其他值替换为代表值。替换过程中,算法会检查值的参数是否符合支配关系,以确保替换后不会破坏程序的语义。在替换过程中,算法会记录替换的次数,以便在分析完成后进行统计和优化。

以下是我提取的 SSA IR 代码片段。接下来,将详细介绍 CSE 算法的实现步骤,并在解释过程中引入这段代码,以帮助理解。

b1:
    v1 = InitMem <mem>
    v5 = Const64 <int> [0]
    v6 = Const64 <int> [1]
    v7 = Const64 <int> [2]
    v8 = Const64 <int> [3]
    v9 = Add64 <int> v8 v7
    v10 = Less64 <bool> v5 v6
If v10 → b3 b2

b3: ← b1
    v13 = Add64 <int> v6 v7
Plain → b2

b2: ← b1 b3
    v19 = Phi <int> v9 v13
    v16 = Add64 <int> v6 v7
    v18 = Add64 <int> v7 v8
    v20 = Add64 <int> v19 v16
    v21 = Add64 <int> v20 v18
    v23 = MakeResult <int,mem> v21 v1
Ret v23

2.1 初始划分等价值

在cse(f *Func) 函数中,首先遍历所有基本块的所有值,只要值的返回值类型不是mem,将其都存入数组a中。类型相关的操作会有不稳定性。比如在一个代码中有v3 = Load v1v8 = Load v1两个值,v8的定义看似冗余,可以用v8 = v3去替换,实则不可。因为我们不确定数据从v3v8流动的过程中,是否有Store操作在v1地址写入了新的值。

IR片段中的值存入数组a中后如下:

a = {v5,v6,v7,v8,v9,v10,v13,v19,v16,v18,v20,v21}

以数组a为参数,调用partitionValues函数,对数组中的值排序后再进行初步分类。排序和分类依赖cmpVal(v, w *Value, …)函数,调用其依次比较vw的:opcode、auxint、参数个数nargs、如果值是Phi还需比较两个值是否在同一个块中、aux。如果这些属性全部相等,则vw可划分为一组初始等价值。

将IR代码片段带入到这部分算法中,重排后的数组a和初步划分得到的等价值数组partition如下。partition是个二维数组,每一项元素都是一组等价值。

// 排序后的a
a = {v9,v13,v16,v18,v20,v21,v10,v19,v5,v6,v7,v8}

// 初步划分得到的等价值
partition = {
	{v9,v13,v16,v18,v20,v21},
	{v10},
	{v19},
	{v5},
	{v6},
	{v7},
	{v8}
}

2.2 细分等价值

细分等价值的过程主要是根据值的参数进行判断,如果一组等价值的参数不是等价值,则将其分割成不同的等价值。

2.2.1 给所有值标号

为了更好的完成这一过程,定义了一个数组valueEqClass,其下标Values.ID对应的值是对Value的一个标号,等价值的标号是相同的。valueEqClass数组中对值的标号,在细分等价值的过程中发挥着很重要的作用。

首先将遍历函数的所有值,执行valueEqClass[v.ID] = -v.ID,将每个值在数组valueEqClass中对应的项初始化为-v.ID。然后再遍历等价值数组partition,将等价值在valueEqClass数组中对应的项改成相同的值。

var pNum ID = 1
for _, e := range partition {
	for _, v := range e {	// 等价Value在valueEqClass中对应项的值相等
		valueEqClass[v.ID] = pNum
	}
	pNum++
}

经过上面操作后,valueEqClass中的值如下所示。valueEqClass是个一维数组,我为了方便理解写成了下面这种形式,实际上写成[v1.id], [v5.id], ... ,[v21.id], [v23.id]这种形式更贴合其排列结构。

valueEqClass[v1.ID] = -1
            [v23.ID] = -23
            [(v9,v13,v16,v18,v20,v21).ID] = 1
            [v10.ID] = 2
            [v19.ID] = 3
			[v5.ID] = 4
			[v6.ID] = 5
			[v7.ID] = 6
			[v8.ID] = 7

2.2.2 根据参数细分等价值

根据参数细分等价值,就是比较等价值的参数,如果一组等价值的参数是非等价值,则将其拆分成多组等价值。重复这一动作,直到所有的等价值都不可拆分。

下列代码就是重复拆分等价值直至不可拆分的逻辑。当遍历一次数组partition后,如果changed的值没有被改成true,说明等价值已经不可拆分(留意代码满足什么条件时不会改变changed的值)。遍历数组partition的每一组等价值时,对一组等价值做一下操作:确定值的参数位置按照参数的valueEqClass值为等价值排序寻找一组等价值的拆分点按照差分点拆分等价值

for {
	changed := false
	for i := 0; i < len(partition); i++ {
		// 确定值的参数位置
		// 按照参数的valueEqClass值为等价值排序
		// 寻找一组等价值的拆分点
		// 按照差分点拆分等价值
		changed = true
	}
	if !changed {
		break
	}
}

确定值的参数位置,是为了消除具有交换性的操作(加法、乘法等)给细分等价值带来的错误判断。如a + bb + a其实是等价的,但是算法判断时会误以为其不等价,我们要避免这种情况。代码中结构type opInfo structcommutative字段用来表示一个值的参数是否具有交换性,true为可交换,false为不可交换。

  • 如果一个值的参数不具有交换性,则不用对其进行任何操作;如v10 = Less64 <bool> v5 v6
  • 如果一个值的参数具有交换性,则将参数valueEqClass[value.ID]值较小的放在前面。如:
    v20 = Add64 <int> v19 v16
    valueEqClass[v19.ID] = 3
    valueEqClass[v16.ID] = 1
    
    // 因为 3>1 ,所以将两个参数的位置调换
    v20 = Add64 <int> v16 v19
    

对于等价值{v9,v13,v16,v18,v20,v21},确定参数位置前后的情况如下:

// 参数交换之前
v9 = Add64 <int> v8 v7
v13 = Add64 <int> v6 v7
v16 = Add64 <int> v6 v7
v18 = Add64 <int> v7 v8
v20 = Add64 <int> v19 v16
v21 = Add64 <int> v20 v18

// 每个参数具体的valueEqClass值
v9 => 7 6	// can commutative
v13 => 5 6
v16 => 5 6
v18 => 6 7
v20 => 3 1	// can commutative
v21 => 1 1

// 参数交换之后
v9 = Add64 <int> v7 v8		// commutative
v13 = Add64 <int> v6 v7
v16 = Add64 <int> v6 v7
v18 = Add64 <int> v7 v8
v20 = Add64 <int> v16 v19	// commutative
v21 = Add64 <int> v20 v18

按照参数的valueEqClass值为等价值排序。一组等价值对应的valueEqClass值是相等的,如{v9,v13,v16,v18,v20,v21}valueEqClass[(v9,v13,v16,v18,v20,v21).ID] = 1;而每一个值的参数的valueEqClass值不一定相等,所以需要对交换参数后的等价值排序。

对于两个等价值,按照参数的个数,排序规则如下:

  • 将两个值的第一个参数比较,valueEqClass值小的放在前面,大的放在后面,相等则保持位置不变。
  • 如果第一个参数相等,再比较第二个参数,valueEqClass小的放在前面,大的放在后面。
  • 第一、二个参数都相等,再比较第三个。最多只能有三个参数

对于{v9,v13,v16,v18,v20,v21}这一组交换参数后的等价值,排序前后的变化如下:

// 排序之前						
v9 => 6 7
v13 => 5 6
v16 => 5 6
v18 => 6 7
v20 => 1 3
v21 => 1 1

// 排序之后						
v21 => 1 1
v20 => 1 3
v13 => 5 6
v16 => 5 6
v9 => 6 7
v18 => 6 7

接下来就是在确定参数位置、且按照参数valueEqClass值排序后的等价值中查找拆分点。查找方式是:依次比较相邻的两个值的参数,如果值所有对应位置参数的valueEqClass值相等,则这两个值之间没有拆分点,否则这两个值之间就是一个拆分点; 将找到的拆分点存放在数组splitPoints中,等一组值的所有拆分点找完后,再利用splitPoints作拆分操作。

对于等价值{v9,v13,v16,v18,v20,v21},找到的拆分点是{1,4},所以数组splitPoints = {0,1,4}。拆分点就是等价值数组的下标。

最后是按照差分点拆分等价值,也就是将一组等价值按照拆分点拆分为多组等价值。如果一组等价值没有拆分点,则结束当前遍历不执行循环中的后续代码,即不进行拆分操作。不执行拆分操作,也就不会执行changed = true。如果所有的等价值都找不到拆分点,则changed = false的值不会改变,说明所有的等价值都不可拆分。

拆分等价值时,将存放所有等价值的数组partition的最后一项(最后一组等价值),移到当前遍历的等价值位置。实现代码如下:

partition[i] = partition[len(partition)-1]
partition = partition[:len(partition)-1]

再将拆分的多组等价值从partition的最后一项位置(会将其覆盖,因为已经移到前面)开始追加(append)至其中。

2.3 替换重复表达式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3gp转MP4怎么转?简单的3个方法~

3gp最初是由第三代合作伙伴计划&#xff08;3rd Generation Partnership Project&#xff09;设计的&#xff0c;旨在满足移动设备对高效传输音频和视频的需求。它的起源可以追溯到20世纪初&#xff0c;当时移动通信技术的飞速发展使得人们对更加高效的多媒体文件格式有了迫切需…

几种免费SSL证书申请方式

目录 DV单域名免费证书的获取渠道&#xff1a; DV多域名免费证书获取渠道&#xff1a; DV通配符免费证书获取渠道&#xff1a; 随着现在网络安全意识的逐渐提升&#xff0c;越来越多的网站都在相继配对部署SSL证书&#xff0c;用以实现https访问。 大家都知道SSL证书好&…

【linux】基础IO(软硬链接)

上一节我们已经搞懂了已经被打开的文件&#xff0c;还有没有被打开的文件都是怎样被管理起来的&#xff0c;同样&#xff0c;路径的重要性也不言而喻&#xff0c;是确定文件在那个分区&#xff0c;进而可以解析到目标文件与目录内容的关系&#xff0c;从而找到inode&#xff0c…

微带线设计细节的模拟仿真分析

微带线设计在很多PCB设计场景中被应用&#xff0c;但是&#xff0c;有些工程师往往并不注重设计细节&#xff0c;导致最后的设计指标与预期相差甚远&#xff0c;比如设计中&#xff0c;会将丝印、散热金属等放在走线的上方&#xff0c;设计检查时会有人产生质疑&#xff0c;至于…

3DTiles生产流程与规范

一篇19年整理的比较老的笔记了。更多精彩内容尽在数字孪生平台。 瓦片切分 标准的四叉树切分对于均匀分布的地理数据切片非常有效&#xff0c;但是这样均等的切分不适用于随机分布、不均匀分布的地理数据&#xff0c;当地理数据稀疏分布的时候&#xff0c;均等的四叉树就不再高…

java-spring-mybatis -学习第一天-基础知识讲解

目录 前置条件(创建一个项目) Mybatis 定义 可能出现的问题 这边如果连接不上数据库 ​编辑 Dao接口设计 Mybatis流程 创建实体类 User 和其属性 创建Mapper的接口类 测试类测试 实例数据库数据的更新 实例数据库数值的删除 最重要的是有一个原始的数据库 -我这边…

使用 vllm 本地部署 cohere 的 command-r

使用 vllm 本地部署 cohere 的 command-r 0. 引言1. 安装 vllm2. 本地部署 cohere 的 command-r3. 使用 cohere 的 command-r 0. 引言 此文章主要介绍使用 使用 vllm 本地部署 cohere 的 command-r。 1. 安装 vllm 创建虚拟环境&#xff0c; conda create -n myvllm python…

Oracle Linux 8.8 一键安装 Oracle 11GR2 RAC(231017)

前言 Oracle 一键安装脚本&#xff0c;演示 Oracle Linux 8.8 一键安装 Oracle 11GR2 RAC&#xff08;231017&#xff09;过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&…

kafka大数据采集技术实验(未完待续)

Kafka环境搭建 下载地址&#xff1a;https://link.zhihu.com/?targethttps%3A//kafka.apache.org/downloads解压启动zookeeper bin/zookeeper-server-start.sh config/zookeeper.properties需要注意的是 : " c o n f i g / z o o k e e p e r . p r o p e r t i e s &q…

维态思(上海)环保科技有限公司 | 2024全国水科技大会暨技术装备成果展览会

嘉宾简介 胡建龙 维态思&#xff08;上海&#xff09;环保科技有限公司 总经理 报告题目&#xff1a;微生态滤床 植物工厂——小城镇生活污水生态净化及零排放案例分享 国家注册设备工程师&#xff08;给排水&#xff09;、上海市&#xff08;合作交流&#xff09;五四青年…

BUUCTF---misc---[ACTF新生赛2020]outguess

1、下载附件&#xff0c;解压之后得到下面信息 2、查看图片属性&#xff0c;发现有个核心价值观编码&#xff1b;解码为abc 3、flag.txt提示 4、结合题目&#xff0c;这是一个outguess隐写 5、用kali先下载安装隐写库 6、使用命令-k(密钥)&#xff1b;-r(将图片里面的隐写信息…

InstantMesh:利用稀疏视图大规模重建模型从单张图像高效生成3D网格

作者&#xff1a;Jiale Xu&#xff0c;Weihao Cheng&#xff0c;Yiming Gao等 编译&#xff1a;东岸因为一点人工一点智能 InstantMesh&#xff1a;利用稀疏视图大规模重建模型从单张图像高效生成3D网格在这项工作中&#xff0c;我们提出了InstantMesh&#xff0c;一个开源的…

免费在英伟达官网使用多个开源AI大模型

英伟达官网能体验到多个聊天AI和图片生成AI&#xff0c;不废话直接上链接 AI开源大模型&#xff08;https://build.nvidia.com/explore/discover?api-keytrue&#xff09; 开源的AI大模型有meta的llama3-8b和llama3-70b、snowflake的arctic、microsoft的phi-3-mini、mistral…

【Linux系统编程】第九弹---权限管理操作(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、目录权限 2、粘滞位 总结 1、目录权限 首先提出一个问题&#xff0c;删除一个文件需要什么权限呢&#xff1f;&#xff1f…

虚拟机软件哪个好用 虚拟机软件哪个可以玩暗区突围 虚拟机软件排名 PD19虚拟机 Mac类虚拟机运行Windows程序 CrossOver支持的热门游戏

随着跨系统互联的需求不断增长&#xff0c;越来越多的用户会选择在电脑系统中安装虚拟机软件&#xff0c;进而更加便捷地访问和操作其他系统。一款好用的虚拟机软件能够提高系统互联的效率&#xff0c;进而实现了资源共享、测试环境搭建等多种用途。而在众多的虚拟机软件当中&a…

张驰咨询:降本增效企业突破市场重围的制胜法宝

企业在快速发展的过程中&#xff0c;降本增效是永恒不变的主题。毕竟&#xff0c;在竞争激烈的市场环境中&#xff0c;只有不断提高效率和降低成本&#xff0c;才能在竞争中立于不败之地。那么&#xff0c;为什么企业需要降本增效呢&#xff1f; 首先&#xff0c;降本增效是企业…

vue+springboot的登录图片验证码(前端对接报错)

tip:这个只是一个效果实际要运用&#xff0c;还是需要改改滴&#xff01; 后台Java自带的 本来我是打算用第三方库的&#xff0c;没有整出来&#xff0c;就跟沈某人说不会来着&#xff0c;他说最好用Java自带的&#xff0c; 不然换个系统第三方的就不能用了&#xff0c;大概…

不可以论文查重,也包含了查AI率吗?

临近毕业&#xff0c;完成一篇符合学术规范的毕业论文是一项繁琐又具挑战性的任务。撰写完论文后&#xff0c;反复的查重降重已让人心身疲累。今年&#xff0c;学校又提出了新要求&#xff0c;论文还需要通过AIGC检测系统&#xff08;www.checkaigc.com&#xff09;才行&#x…

Vue2学习笔记(尚硅谷天禹老师)

目录 一、入门案例 二、模板语法 三、数据绑定 四、el和data的两种写法 五、MVVM模型 六、Object.defineproperty方法 七、Vue中响应式原理 八、数据代理 九、methods配置项 十、Vue中的事件处理 十一、Vue中的键盘事件 十二、计算属性 十三、监视属性watch 十四、绑定Class样式…

【echarts】数据起点不从X轴的原点开始【不从0开始】

echarts折线图x轴不从0开始怎么办&#xff1f; 或者说为什么有些图是这样的 有些却是这样的 原因出在这里&#xff1a; boundaryGap: false 默认是true&#xff0c;是指坐标轴两边留白。改为false&#xff1a;不留白即从原点开始。 看一下官方的说明
最新文章