我已经在各种编译器的功能,阅读起来,我遇到了“积极的优化”一词,许多编译报导进行。 LLVM,例如列举了以下编译时优化功能:
- 存储器/指针特定
- 循环变换
- 数据流
- 算术
- 死代码消除
- 内联
这是什么具体内容是? 说你有下面的代码段,你怎么可以优化生成的字节码比编译器生成的运行速度没有? 我在优化JIT供电运行时,如C#,Java和闪存的字节码特别感兴趣。 这是棘手的,因为JIT只支持该处理器通常不会操作码,这限制了优化,你可以做的量的一个子集。 不过,我很感兴趣,看看什么可能的,正是变革可能推动VM的限制。
的代码块虚构:
for (i = 0; i < 100; i++){
in = dataIn[i];
if ((in % 5) == 0){
out = ((in / 2) >> 16) - 10;
}else{
out = ((in << 5) / 2) * 50 + 10;
}
dataOut[i] = out;
}
由编译器生成近似的伪代码,基于堆栈的JIT虚拟机如Flash播放器:(原谅我的任何错误,这完全是手写的!)
// i = 0
label: "forInit"
push 0
writeTo "i"
// while i < 100
label: "forStart"
push "i"
push 100
jumpIfMoreThan "forEnd"
// in = dataIn[i];
push "i"
push "dataIn"
readProp
saveTo "in"
// if ((in % 5) == 0)
push "in"
push 5
mod
push 0
jumpIfNotEquals "ifPart2"
label: ifPart1
// out = ((in / 2) >> 16) - 10;
push "in"
push 2
divide
push 16
rightshift
push 10
minus
writeTo "out"
goto "ifEnd"
// else
label: ifPart2
// out = ((in << 5) / 2) * 50 + 10;
push "in"
push 5
leftshift
push 2
divide
push 50
multiply
push 10
add
writeTo "out"
// dataOut[i] = out;
label: ifEnd
push "out"
push "i"
push "dataOut"
writeProp
// i++
push "i"
increment
writeTo "i"
// while i < 100
goto "forStart"
label: "forEnd"
这里有两个简单的优化编译器可以使:
out = ((i / 2) >> 16) - 10;
可以减少到
out = (i >> 17) - 10;
和
out = ((i << 5) / 2) * 50 + 10;
可以减少到
out = (i << 4) * 50 + 10;
要回答你的问题:“你怎么可以优化生成的字节代码运行任何比编译器生成的更快吗?” 这里是有一些优化的字节码的另一个版本。
// i = 0
label: "forInit"
push 0
writeTo "i"
// while i < 100
label: "forStart"
push "i"
push 100
jumpIfMoreThan "forEnd"
// in = dataIn[i];
push "i"
push "dataIn"
readProp
saveTo "in"
// if ((in % 5) == 0)
push "in"
push 5
mod
push 0
jumpIfNotEquals "ifPart2"
label: ifPart1
// optimization: remove unnecessary /2
// out = ((in / 2) >> 16) - 10;
push "in"
push 17
rightshift
push 10
minus
// optimization: don't need out var since value on stack
// dataOut[i] = out;
push "i"
push "dataOut"
writeProp
// optimization: avoid branch to common loop end
// i++
push "i"
increment
writeTo "i"
goto "forStart"
// else
label: ifPart2
// optimization: remove unnecessary /2
// out = ((in << 5) / 2) * 50 + 10;
push "in"
push 4
leftshift
push 50
multiply
push 10
add
// optimization: don't need out var since value on stack
// dataOut[i] = out;
push "i"
push "dataOut"
writeProp
// optimization: avoid branch to common loop end
// i++
push "i"
increment
writeTo "i"
goto "forStart"
label: "forEnd"
我也一直在做这个,的完整列表是LLVM执行变换 ,在头组织:
- 删除死代码
- 积极的死代码消除
- 死代码消除
- 死消除争论
- 死类型消除
- 死指令消除
- 死存储清除
- 死了全球消灭
- 删除死循环
- 不需要的数据删除
- 从模块中去除所有的符号
- 未使用的符号地带调试信息
- 未使用的条函数原型
- 地带的所有llvm.dbg.declare内部函数
- 去除所有的符号,除了DBG符号,从一个模块
- 合并重复的全局常量
- 删除未使用的异常处理信息
- 内联函数
- 循环优化
- 环路封闭SSA形式通行证
- 循环不变代码运动
- 提取循环放入新功能
- 提取最多一个循环到一个新的功能
- 循环强度
- 旋转循环
- 规范化自然循环
- 循环展开
- Unswitch循环
- 杂项
- 提倡“参照”参数标量
- 结合说明,以形成基本块中的向量指令
- 档案导引基本块布局
- 打破CFG临界边
- 优化代码生成
- 简单常量传播
- 演绎功能属性
- 全局变量优化
- 全球价值编号
- 。规范化归纳变量
- 插入的边缘分析仪器
- 插入的边缘轮廓最佳仪器
- 结合冗余指令
- 内部化全局符号
- 进程间的传播
- 过程间稀疏有条件的常数传播
- 跳转线程
- 下原子的内在函数来非原子形式
- 较低的调用和放松,为unwindless代码生成器
- 下SwitchInst对分支机构
- 促进记忆到注册
- 优化的memcpy
- 统一函数出口节点
- 重新关联表达式
- 降级的所有值到堆栈槽
- 聚集体(DT)的标量替换
- 稀疏有条件的常数传播
- 简化知名库调用
- 简化CFG
- 代码下沉
- 促进SRET参数多RET值
- 尾部调用消除
- 尾复制
虽然这并不能回答你的问题,我碰到的是一个C ++编译器进行优化生成机器码下列转换:
- 用作数据索引强度减少 ---迭代变量在匹配于数据单元的大小的速率递增
- 隐藏Paremeters ---它返回的结构实际上是由一个隐藏的参数写入到一个区域指向一个功能
- 整数除法 ---某些fornulas可用于以已知的除数的情况下更有效地评估整数除法
- 浮动条件 ---浮点状态变成指令设置和查询浮点状态复杂的序列
- 复杂的数学 ---一个复杂的乘除变成库调用
- 天然例程 ---一个的memcpy(),memset的(),的strcmp()或strlen的()操作被转换成代表MOV,代表STO,代表ZCMP,或代表zscas
- 短路 ---一个复杂的条件是基本块树评估
- 联盟Ambiguation ---信息是关于哪个成员联盟旨在丢失
- 复制碎片 ---大双人床或聚合值由字抄词
- 测试碎片 ---上的长整型值的条件是由单独的测试该值的各个单词
- 开关碎片 --- switch语句被替换的条件巢值
- 环头复制 ---一个循环增强与决定是否进入循环的条件
- 循环展开 ---一环是由循环体的复制拷贝替换
- 内联函数 ---一个函数调用由函数体的副本替换
文章来源: Specifically what does a compiler do to aggressively optimize generated bytecode?