-
2018-05-14 18:27:41
Unity性能优化(一)-脚本代码优化
英文原文:https://unity3d.com/cn/learn/tutorials/topics/performance-optimization
脚本代码优化
托管代码和托管运行时简介
在Unity工程的 构建(Build) 过程中,C# 代码首先会被编译成 CIL(Common Intermediate Language),进而根据工程的目标运行平台被编译成相应的 本地代码(Native Code)。
被编译成CIL的代码(即C#),称为 托管代码(Managed Code)。当托管代码被编译成本地代码时,它被集成到 托管运行时(Managed Runtime)。托管运行时将会处理自动内存管理和安全性检查等事项。
引起代码性能低下的原因
- 第一种可能的原因:代码结构欠佳。例如,重复调用无需多次执行的方法。
- 第二种可能的原因:调用其他代码引起的不必要开销。例如,托管代码和引擎代码(C++)之间不必要的调用。
- 第三种可能的原因:调用不需要调用的代码。例如,在玩家离敌人(AI)很远时调用模拟敌人视线的方法。
- 最后的可能的原因:代码过分严谨(过犹不及)。例如,对大量使用复杂AI的代理进行十分细致的模拟。
提升代码效率
-
减少循环的执行次数和循环中的指令数目。
-
将不需要频繁执行的方法从
Update()
中移出。 -
只在变化发生时才去执行代码。例如,不对未变化的量进行反复的输出。
-
将需要频繁执行且不能通过事件触发的方法改为每隔几帧执行一次。例如,记录帧数(Time.frameCount)并通过模运算跳过某些帧,通过该方式,也能做到让几个方法不在同一帧中执行。
-
使用缓存存储需要反复获取和使用的值。例如,将GetComponent()方法的返回值存储到变量中,避免反复获取。
-
根据需求选用正确的数据结构,尤其是在数据驱动的开发模式中。相关教程
-
使用对象池优化内存使用和降低CPU开销。创建和销毁对象的开销通常要比激活(SetActive(true))和取消激活对象的开销更高,尤其是对于那些含有初始化内容的对象。相关教程
避免调用高开销的Unity API
-
SendMessage()和BroadcastMessage()。在了解项目结构的前提下,SendMessage()和BroadcastMessage()方法使用起来非常灵活而且容易实现,但它使用了反射,而反射会造成更多的CPU开销。在清楚要调用哪个组件的哪个方法时应该通过组件的引用直接调用方法;在不清楚具体要用的组件和方法时,考虑使用 事件 和 委托 替代。
-
Find()。Find()方法会遍历内存中的每个GameObject和组件,随着项目规模的扩张,它的开销将会越来越大。不要频繁的使用Find()和与其类似的方法,可以考虑在Inspector中设置对对象的引用,或者创建一个专门用于管理需要搜索的对象的引用的脚本。
-
Transform。设置position和rotation会触发内部的OnTransformChanged事件并传播到所有的子级对象中,对于含有非常对子物体的对象来说,这种操作开销很大,应该 减少对position和rotation的修改。如果某个方法中要分别设置position的xyz,可以创建一个Vector3然后分别将xyz设置到该Vector3中,最后将Vector3设置给position,而不要每次分开设置position的xyz。尝试使用localPosition替代position。localPosition存储在transform中,访问该值时,Unity会直接将其返回,而position在每次访问时都会重新计算,如果要经常获取position,可以将其缓存起来。
-
Update()。Update()和LateUpdate()等事件方法的每次调用都需要引擎代码与托管代码之间进行通信,还要Unity进行安全检查(GameObject状态是否合法等),即使这些事件方法的方法体是空的,引擎任然会对其进行调用。因此,为避免浪费CPU时间,应该 删除空的事件方法。另外,如果某个活动状态(gameObject.active == true)的GameObject上的脚本中含有Awake()方法,即使这个脚本没有被启用(enabled==false),Awake()方法也会执行。如果游戏中含有非常多的带有Update()方法的MonoBehavior,应该尝试改变代码结构来减少开销,这有一篇相关的博客。
-
Vector2和Vector3()。向量的数学运算要比普通的浮点数和整数的数学运算更加复杂,访问向量的某些属性可能会带来隐含的开销,例如magnitude属性(不仅针对向量,例如上文提到的Transform.position)。每次访问magnitude,引擎都会进行开平方运算,虽然单次计算并不会消耗多少时间, 但当数量级足够大时,这种开销将变得明显。可以尝试使用sqrMagnitude(即magnitude的平方)替代magnitude,减少开平方操作。
-
Camera.main。Camera.main所引起的问题与Find()方法类似,应该避免使用Camera.main并且手动管理对相机的引用。
-
其他Unity API调用和优化。
对象的某些成员看起来像是个变量,但其实它是个访问器(get,set),除了返回值,它其中还包含了额外的操作,例如数学计算、触发事件或在托管代码中调用引擎代码。上文中提到的Transform中的position、向量中的magnitude和Camera.main等都属于访问器,在编码过程中,应该注意经常访问的数据中是否含有访问器。
这里对Unity的性能优化进行了更多的指导。仅在有需要时才去运行代码
-
视截体剔除(Frustum Culling)。仅在对象可见时才执行与其视觉效果相关的代码,可以通过
GetComponent<Renderer>().isVisible
得知对象是否可见,或者通过事件方法OnBecameInvisible()和OnBecameVisible()来修改对象变得可见/不可见时的代码执行方式。 -
细节层次(Level of Detail,LOD)。通过LOD技术,为离玩家近的对象渲染高精度的网格和贴图,为离玩家远的对象渲染低精度的网格和贴图。可以在代码中进行类似于LOD的优化,例如,只为离玩家近的AI进行高精度的模拟操作,而远处的AI则不进行复杂的模拟运算。CullingGroup手册介绍了更多关于如何使用LOD的内容。
更多相关内容 -
深入应用C++11 代码优化与工程级应用.pdf
2019-07-23 20:17:16祁宇 深入应用C++11 代码优化与工程级应用.pdf -
【Unity优化篇】 | Unity脚本代码优化策略,快速获取 游戏对象 和 组件 的方法【文末送书】
2021-12-30 17:45:30本文是 Unity优化篇 系列的一篇文章,同时也包是含在 『Unity系统学习专栏⭐️』里的文章。 本专栏是我总结的Unity学习类的文章,适合Unity入门和...本篇文章就来讲一下 Unity中的脚本代码优化策略,一起来学习一下吧!- 🎬 博客主页:https://xiaoy.blog.csdn.net
- 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉
- 🎄 学习专栏推荐:Unity系统学习专栏
- 🌲 游戏制作专栏推荐:游戏制作专栏
- 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
- 📆 未来很长,值得我们全力奔赴更美好的生活✨
目录
-
【编译原理笔记16】代码优化:流图,常用代码优化方法, 基本块的优化
2020-08-14 19:29:05以基本块为单位,进行运算上的推导优化。堪称妙!原来编译器这么强大!本次笔记内容:
8-1 流图
8-2 常用代码优化方法一
8-3 常用代码优化方案二
8-4 基本快的优化本节课幻灯片,见于我的 GitHub 仓库:第16讲 代码优化_1.pdf
文章目录
流图
基本块(Basic Block)
基本块
是满足下列条件的最大
的连续
三地址指令序列:- 控制流只能从基本块的
第一个指令
进入该块。也就是说,没有跳转到基本块中间或末尾指令的转移指令 - 除了基本块的
最后一个指令
,控制流在离开基本块之前不会跳转或者停机
基本块划分算法
输入:
- 三地址指令序列
输出:
- 输入序列对应的
基本块列表
,其中每个指令恰好被分配给一个基本块
方法:
- 首先,确定指令序列中哪些指令是
首指令
(leaders),即某个基本块的第一个指令
- 指令序列的
第一个三地址指令
是一个首指令 - 任意一个条件或无条件
转移指令的目标指令
是一个首指令 - 紧跟在一个条件或无条件
转移指令之后的指令
是一个首指令
- 然后,每个首指令对应的基本块包括了从它自己开始,直到
下一个首指令
(不含)或者指令序列结尾
之间的所有指令
例
如上是快速排序法的部分源代码。根据跳转指令找首指令(如跳转指令后的一条指令)。
流图(Flow Graphs)
- 流图的
结点
是一些基本块
- 从基本块B到基本块C之间有一条
边
当且仅当基本块C的第一个指令可能
紧跟在B的最后一条指令之后执行
此时称B是C的
前驱
(predecessor) ,C是B的后继
(successor)。有两种方式可以确认这样的边:
- 有一个
从B的结尾跳转到C的开头
的条件或无条件跳转语句 - 按照原来的三地址语句序列中的顺序,C紧跟在之B后,且B的结尾不存在无条件跳转语句
例
感觉像是描述各个运算部分的关系。常用的代码优化方法(1)
优化的分类
- 机器无关优化:针对中间代码
- 机器相关优化:针对目标代码
- 局部代码优化:单个基本块范围内的优化
- 全局代码优化:面向多个基本块的优化
常用的优化方法
- 删除公共子表达式
- 删除无用代码
- 常量合并
- 代码移动
- 强度削弱
- 删除归纳变量
①删除公共子表达式
公共子表达式:
- 如果表达式
x op y
先前已被计算过,并且从先前的计算到现在,x op y
中变量的值没有改变,那么x op y
的这次出现就称为公共子表达式(common subexpression)
例
将 B3 重构如黄色区域。
由 B3 “逆流而上”,发现 4 ∗ i 4*i 4∗i 没有被修改过,则其是一个公共子表达式。
进行了再次的画家如上。
发现 a [ t 2 ] a[t_2] a[t2] 与 a [ t 4 ] a[t_4] a[t4] 也是公共子表达式。
a [ t 1 ] a[t_1] a[t1]能否作为公共子表达式?把 a [ t 1 ] a[t_1] a[t1]作为公共子表达式是不稳妥的,因为控制离开 B 1 B_1 B1进入 B 6 B_6 B6之前可能进入 B 5 B_5 B5,而 B 5 B_5 B5有对 a a a的赋值。
关键问题:如何自动识别公共子表达式?会在后面的课程详细介绍。
常用的代码优化方法(2)
②删除无用代码
复制传播:常用的
公共子表达式消除算法
和其它一些优化算法会引入一些复制语句
(形如x=y
的赋值语句)
复制传播:在复制语句x= y之后尽可能地用y代替x。
无用代码
(死代码Dead-Code):其计算结果永远不会被使用
的语句。例
程序员不大可能有意引入无用代码,无用代码通常是因为前面执行过的某些转换而造成的。如何自动识别无用代码?
也将在后文详细介绍。
如上,通过删除公共子表达式
与删除无用代码
,将 B5 与 B6 简化了不少。③常量合并(Constant Folding)
如果在
编译时刻
推导出一个表达式的值是常量
,就可以使用该常量来替代这个表达式。该技术被称为常量合并
。④代码移动(Code Motion)
这个转换处理的是那些
不管循环执行多少次都得到相同结果的表达式
(即循环不变计算
,loop-invariant computation) ,在进入循环之前就对它们求值。例
如何自动识别循环不变计算?循环不变计算的相对性
对于多重嵌套的循环,循环不变计算是相对于某个循环而言的。可能对于更加外层的循环,它就不是循环不变计算。
⑤强度削弱(Strength Reduction)
用较快的操作代替较慢的操作,如用加代替乘。
循环中的强度削弱
对于一个变量x ,如果存在一个正的或负的
常数
c使得每次x被赋值时它的值总增加c ,那么x就称为归纳变量
(Induction Variable)。例
归纳变量可以通过在每次循环迭代中进行一次简单的增量运算(加法
或减法
)来计算。⑥删除归纳变量
在沿着循环运行时,如果有
一组归纳变量
的值的变化保持步调一致
,常常可以将这组变量删除为只剩一个。
如上, i i i 与 j j j 都无用了。基本块的优化
很多重要的
局部优化技术
首先把一个基本块
转换成为一个无环有向图
(directed acyclic graph,DAG)。基本块的 DAG 表示
基本块中的每个
语句
s都对应一个内部结点
N:- 结点N的
标号
是s中的运算符
;同时还有一个定值变量表
被关联到N ,表示s是在此基本块内最晚对表中变量进行定值的语句 - N的
子结点
是基本块中在s之前、最后一个对s所使用的运算分量
进行定值的语句对应的结点
。如果s的某个运算分量在基本块内没有在s之前被定值,则这个运算分量对应的子结点就是代表该运算分量初始值的叶结点(为区别起见,叶节点
的定值变量表中的变量加上下脚标0) - 在为语句x=y+z构造结点N的时候,如果x已经在某结点M的定值变量表中,则从M的定值变量表中删除变量x
例,有基本块:
a = b + c b = a - d c = b + c d = a - d
对于形如 x=y+z 的三地址指令,如果已经有一个结点表示 y+z,就不往 DAG 中增加新的结点,而是给已经存在的结点附加定值变量
x。基于基本块的 DAG 删除无用代码
从一个DAG上删除所有
没有附加活跃变量
(活跃变量是指其值可能会在以后被使用的变量)的根结点
(即没有父结点的结点) 。重复应用这样的处理过程就可以从DAG中消除所有对应于无用代码的结点。
数组元素赋值指令的表示
如上,因为有可能出现 i = j i=j i=j ,因此不能轻易把 a [ i ] a[i] a[i] 算作公共子表达式。- 对于形如a[j] = y的三地址指令,创建一个运算符为“
[]=
”的结点,这个结点有3个子结点,分别表示a、j和y - 该结点没有定值变量表
- 该结点的创建将
杀死
所有已经建立的、其值依赖于a的结点 - 一个被杀死的结点
不能再获得任何定值变量
,也就是说,它不可能成为一个公共子表达式
根据基本块的DAG可以获得一些非常有用的信息
- 确定哪些变量的值在该基本块中赋值前被
引用
过:在DAG中创建了叶结点
的那些变量 - 确定哪些语句计算的值可以在基本块外被引用:在DAG构造过程中为语句s(该语句为变量x定值)创建的节点N,在DAG构造结束时x仍然是N的定值变量
从 DAG 到基本块的重组
对每个具有若干定值变量的节点,构造一个
三地址语句
来计算其中某个变量的值。倾向于把计算得到的结果赋给一个在基本块
出口处活跃
的变量(如果没有全局活跃变量的信息
作为依据,就要假设所有变量都在基本块出口处活跃,但是不包含编译器为处理表达式而生成的临时变量)。如果结点有
多个附加的活跃变量
,就必须引入复制语句
,以便给每一个变量都赋予正确的值。例
构建 DAG 如右边。常量直接标记出来。
最终,根据 DAG 得到优化后的基本块如下:D = A + C E = A * C F = E + D L = 15 + F
- 控制流只能从基本块的
-
编译原理之代码优化
2017-12-18 10:43:49编译原理出于代码编译的模块化组装考虑,一般会在语义分析的阶段生成平台无关的中间代码,经过中间代码级的代码优化,而后作为输入进入代码生成阶段,产生最终运行机器平台上的目标代码,再经过一次目标代码级别的...前面介绍完了词法分析、语法分析和语义分析,以及各阶段如何利用符号表来实现代码合理性确认以及代码地址拉链式回填等工作。编译原理出于代码编译的模块化组装考虑,一般会在语义分析的阶段生成平台无关的中间代码,经过中间代码级的代码优化,而后作为输入进入代码生成阶段,产生最终运行机器平台上的目标代码,再经过一次目标代码级别的代码优化(一般和具体机器的硬件结构高度耦合,复杂且不通用)。故而出于理解编译原理的角度考虑,代码优化一般都是以中间代码级代码优化手段作为研究对象。
代码优化按照优化的代码块尺度分为:局部优化、循环优化和全局优化。即
1. 局部优化:只有一个控制流入口、一个控制流出口的基本程序块上进行的优化;
2. 循环优化:对循环中的代码进行的优化;
3. 全局优化:在整个程序范围内进行的优化。1. 常见的代码优化手段
常见的代码优化技术有:删除多余运算、合并已知量和复写传播,删除无用赋值等。采用转载自《编译原理》教材中关于这些优化技术的图例快速地展示下各优化技术的具体内容。针对目标代码:
P := 0 for I := 1 to 20 do P := P + A[I]*B[I]
假设其翻译所得的中间代码如下
1. 删除多余运算
分析上图的中间代码,可以发现(3)
和式(6)
属于重复计算( 因为I并没有发生变化),故而式(6)
是多余的,完全可以采用T4∶=T1
代替。2. 代码外提
减少循环中代码总数的一个重要办法是循环中不变的代码段外提。这种变换把循环不变运算,即结果独立于循环执行次数的表达式,提到循环的前面,使之只在循环外计算一次。针对改定的例子,显然数组A和 B的首地址在计算过程中并不改变,则作出的改动如下
3. 强度削弱
强度削弱的本质是把强度大的运算换算成强度小的运算,例如将乘法换成加法运算。针对上面的循环过程,每循环一次,I
的值增加1
,T1
的值与I
保持线性关系,每次总是增加4
。因此,可以把循环中计算T1
值的乘法运算变换成在循环前进行一次乘法运算,而在循环中将其变换成加法运算。
4. 变换循环控制条件
I
和T1
始终保持T1=4*I
的线性关系,因此可以把四元式(12)
的循环控制条件I≤20
变换成T1≤80
,这样整个程序的运行结果不变。这种变换称为变换循环控制条件。经过这一变换后,循环中I
的值在循环后不会被引用,四元式(11)
成为多余运算,可以从循环中删除。变换循环控制条件可以达到代码优化的目的。5. 合并已知量和复写传播
四元式(3
)计算4*I
时,I
必为1
。即4*I
的两个运算对象都是编码时的已知量,可在编译时计算出它的值,即四元式(3)
可变为T1=4
,这种变换称为合并已知量。四元式
(6)
把T1
的值复写到T4
中,四元式(8)
要引用T4
的值,而从四元式(6)
到四元式(8)
之间未改变T4
和T1
的值,则将四元式(8)
改为T6∶=T5[T1]
,这种变换称为复写传播。
6. 删除无用赋值
式(6)
对T4
赋值,但T4
未被引用;另外,(2)
和(11)
对I赋值,但只有(11)
引用I
。所以,只要程序中其它地方不需要引用T4
和I
,则(6)
,(2)
和(11)
对程序的运行结果无任何作用。我们称之为无用赋值,无用赋值可以从程序中删除。至此,我们可以得到删减后简洁的代码
2. 基本块内的局部优化
1. 基本块的划分
入口语句的定义如下:
① 程序的第一个语句;或者,
② 条件转移语句或无条件转移语句的转移目标语句;
③ 紧跟在条件转移语句后面的语句。
有了入口语句的概念之后,就可以给出划分中间代码(四元式程序)为基本块的算法,
其步骤如下:
① 求出四元式程序中各个基本块的入口语句。
② 对每一入口语句,构造其所属的基本块。它是由该入口语句到下一入口语句(不包括下一入口语句),或到一转移语句(包括该转移语句),或到一停语句(包括该停语句)之间的语句序列组成的。
③ 凡未被纳入某一基本块的语句、都是程序中控制流程无法到达的语句,因而也是不会被执行到的语句,可以把它们删除。2. 基本块的优化手段
由于基本块内的逻辑清晰,故而要做的优化手段都是较为直接浅层次的。目前基本块内的常见的块内优化手段有:
1. 删除公共子表达式
2. 删除无用代码
3. 重新命名临时变量 (一般是用来应对创建过多临时变量的,如t2 := t1 + 3
如果后续并没有对t1
的引用,则可以t1 := t1 + 3
来节省一个临时变量的创建)
4. 交换语句顺序
5. 在结果不变的前提下,更换代数操作(如x∶=y**2
是需要根据**
运算符重载指数函数的,这是挺耗时的操作,故而可以用强度更低的x∶=y*y
来代替)
根据以上原则,对如下代码进行优化t1 := 4 - 2 t2 := t1 / 2 t3 := a * t2 t4 := t3 * t1 t5 := b + t4 c := t5 * t5
给出优化的终版代码
t1 := a + a t1 := b + t1 c := t1 * t1
显然代码优化的工作不能像上面那样的人工一步步确认和遍历,显然必然要将这些优化工作公理化。而一般到涉及到数据流和控制流简化的这种阶段,都是到了图论一展身手的时候。
3. DAG(无环路有向图)应用于基本块的优化工作
在DAG图中,通过节点间的连线和层次关系来表示表示式或运算的归属关系:
① 图的叶结点,即无后继的结点,以一标识符(变量名)或常数作为标记,表示这个结点代表该变量或常数的值。如果叶结点用来代表某变量A的地址,则用addr(A)作为这个结点的标记。
② 图的内部结点,即有后继的结点,以一运算符作为标记,表示这个结点代表应用该运算符对其后继结点所代表的值进行运算的结果。
(注:该部分内容转载自教材《编译原理》第11章DAG无环路有向图应用于代码优化)DAG构建的流程如下
对基本块的每一四元式,依次执行: 1. 如果NODE(B)无定义,则构造一标记为B的叶结点并定义NODE(B)为这个结点; 如果当前四元式是0型,则记NODE(B)的值为n,转4。 如果当前四元式是1型,则转2.(1)。 如果当前四元式是2型,则:(Ⅰ)如果NODE(C)无定义,则构造一标记为C的叶结点并定义NODE(C)为这个结点,(Ⅱ)转2.(2)。 2. (1) 如果NODE(B)是标记为常数的叶结点,则转2.(3),否则转3.(1)。 (2) 如果NODE(B)和NODE(C)都是标记为常数的叶结点,则转2.(4),否则转3.(2)。 (3) 执行op B(即合并已知量),令得到的新常数为P。如果NODE(B)是处理当前四元式时 新构造出来的结点,则删除它。如果NODE(P)无定义,则构造一用P做标记的叶结点n。置NODE(P)=n,转4.。 (4) 执行B op C(即合并已知量),令得到的新常数为P。如果NODE(B)或NODE(C)是处理当前四元式时新构造出来的结点,则删除它。如果NODE(P)无定义,则构造一用P做标记的叶结点n。置NODE(P)=n,转4.。 3. (1) 检查DAG中是否已有一结点,其唯一后继为NODE(B),且标记为op(即找公共子表达式)。如果没有,则构造该结点n,否则就把已有的结点作为它的结点并设该结点为n,转4.。 (2) 检查DAG中是否已有一结点,其左后继为NODE(B),右后继为NODE(C),且标记为op(即找公共子表达式)。如果没有,则构造该结点n,否则就把已有的结点作为它的结点并设该结点为n。转4.。 4. 如果NODE(A)无定义,则把A附加在结点n上并令NODE(A)=n;否则先把A从NODE(A)结点上的附加标识符集中删除(注意,如果NODE(A)是叶结点,则其标记A不删除),把A附加到新结点n上并令NODE(A)=n。转处理下一四元式。
说着很复杂,下面看一个案例
(1) T0∶=3.14 (2) T1∶=2 * T0 (3) T2∶=R + r (4) A∶=T1 * T2 (5) B∶=A (6) T3∶=2 * T0 (7) T4∶=R + r (8) T5∶=T3 * T4 (9) T6∶=R - r (10) B∶=T5 * T6
其DAG图的构建过程如下
通过DAG图可以发现诸多的优化信息,如重复定义、无用定义等,则根据上图的DAG图可以构建最后的优化代码序列
(1) S1∶=R+r (2) A∶=6.28*S1 (3) S2∶=R-r (4) B∶=A *S2
3.循环优化
根据上面基本块的定义,我们将诸多基本块组装在一起,构建成程序循环图,如针对下面这个例子(1) read x (2) read y (3) r∶=x mod y (4) if r=0 goto (8) (5) x∶=y (6) y∶=r (7) goto (3) (8) write y (9) halt
则按照上面基本块的划分,可以分成四个部分,四个部分的控制流分析可知可以得到一个循环图
循环块最主要的特点是只有一个数据流和控制流入口,而出口可能有多个。循环优化的主要手段有:循环次数无关性代码外提、删除归纳变量和运算强度削弱。关于这三种手段的理解可以借助此前的描述进行类比,基本并无太多差异。
-
编译原理(十六)——中间代码优化(1)
2020-10-23 11:38:21一、代码优化的阶段 欲提高源程序的运行速度,需要经过几个阶段的优化: 用户对源程序进行优化(和编译器无关,与coder设计的算法有关) 编译器前端对中间代码进行优化 编译器后端对目标代码进行优化 两个编译器... -
JAVA代码优化
2018-10-21 23:27:45一、类、方法、变量尽量指定final修饰 public static void test(){ final String x=&amp;quot;1&amp;...二、字符串拼接背后不一定是StringBuilder、上面代码没有用StringBuilde -
【编译原理】中间代码优化(三) 循环优化
2020-06-29 18:02:54也正是由于这部分代码序列可能会被反复执行,所以在进行中间代码优化时应着重考虑循环优化,这对提高目标代码的效率起到很大的作用。为了进行循环优化,首先需要确定的是程序流图中哪些基本块构成一个循环。按照结构... -
【编译原理】中间代码优化(二) 局部优化
2020-06-01 10:23:21对于一个给定的程序,我们可以把它划分为一系列的基本块。...局限于基本块范围内的优化称为基本块内的优化,或者称为局部优化。所谓基本块,是指程序中一个顺序执行的语句序列,其中只有一个入口和一个出口。 ... -
【编译原理】代码优化,流图/DAG优化
2020-12-11 20:52:32代码优化就是被优化程序进行一种语义保持的变换 优化位置 中间代码优化(与机器无关) 目标代码优化(与机器有关) 优化分类 局部优化 循环优化 全局优化 优化技术 删除公共子表达式:t1... -
MATLAB中代码优化的两种方法
2020-05-28 16:26:27MATLAB中的代码优化 MATLAB中的代码优化有两种重要的方法:预分配组和向量化循环。 我们举一个简单的例子来看,创建一个MATLAB函数来计算f(x) = sin(x / 100π): function y = sinfun1(M) x = 0: M - 1; for k = 1:... -
java代码优化六大原则
2017-05-29 20:35:29单一职责代码优化第一步,单一职责原则 (Single Responsibility Principle)。对于一个java类,应该仅有一个引起它变化的原因,也就是说,一个类中,应该是一组相关性很高的函数、数据的封装。但是这个原则的界限... -
【Unity优化篇】 | Unity脚本代码优化策略,空引用快速检索、使用合适的数据结构、禁用脚本和对象等 性能...
2022-01-06 10:42:36Unity脚本代码优化策略学习,如何正确的编写代码和逻辑。 世界上没有绝对的公平,如果我们起点就比别人第一步,那就更需要比别人努力了。 每天比别人努力多一点点,就会有很大的突破。你必须特别努力,才能显得毫不... -
常见的代码优化技术
2019-10-01 16:35:25常见的代码优化技术有:复写传播,删除死代码, 强度削弱,归纳变量删除,代码外提 1.复写传播: 复写语句:形式为f = g 的赋值 优化过程中会大量引入复写 复写传播变换的做法是在复写语句f = g后,尽可能用g代表f ... -
编译原理习题(含答案)——16-19代码优化——哈工大陈鄞配套版本
2018-06-14 11:54:40代码优化_11 优化可生成()的目标代码。A. 运行时间较短B. 占用存储空间较小C. 运行时间短但占用内存空间大D. 运行时间短且占用存储空间小 2 基本块内的优化为 ( )。A. 代码外提,删除归纳变量B. 删除多余运算,... -
【编译原理】中间代码优化(一) 优化技术大观
2020-05-31 18:07:27优化其实可以在编译的各个阶段进行,但最主要的一类优化是在目标代码生成以前,对语法分析、语义分析后产生的中间代码进行优化。这是因为中间代码的形式不依赖于具体的计算机,它可以是三地址码的形式,所以相应的... -
编译原理 代码优化
2018-10-22 01:34:29代码优化: 因为抽象语法树中可能包括错误,因此不能在抽象语法树阶段进行优化。 函数式的优化:输入一个抽象语法树,输出一个抽象语法树: 在循环中,如果E仍在缩小,就持续常量折叠。 本来... -
vs代码优化
2018-07-31 11:42:38VS-IDE代码优化: 属性->配置属性->C/C++->代码生成:启用增强指令集,可选用 流式处理 SIMD 扩展 2 (/arch:SSE2) (/arch:SSE2)、流式处理 SIMD 扩展 2 (/arch:SSE2) (/arch:SSE2) 进行加速... -
Verilog 代码优化技巧
2019-06-26 15:23:57Verilog HDL 代码优化技巧 ALU 逻辑资源共享 always@(*)begin If(sel) begin result <= a + b ; else result<= a + c ; end end 可以看出两条加法运算分别综合出两个加法器 上面if语句是互... -
前端代码优化基本方法
2017-10-12 13:51:20前端性能优化 -
代码 离散型遗传算法求解组合优化代码
2022-06-04 17:08:13代码 离散型遗传算法求解组合优化代码代码 离散型遗传算法求解组合优化代码代码 离散型遗传算法求解组合优化代码代码 离散型遗传算法求解组合优化代码代码 离散型遗传算法求解组合优化代码代码 离散型遗传算法求解... -
编译原理过程简述及中间代码优化
2017-09-28 17:21:23一、编译过程图示如下:词法分析作用:找出单词 。...二、中间代码优化所谓代码优化是指对程序代码进行等价(指不改变程序的运行结果)变换。程序代码可以是中间代码(如四元式代码),也可以是目标代码。 -
Keil的代码优化产生的问题
2017-05-10 10:31:20用单步调试就看出来了,不过我的问题还是没解决,,,12864调了4年,,,...Keil的代码优化产生的问题 转载这篇文章的原因: 今天和昨天一直为一个BUG困扰,明明我有写程序语句(还是很关键的那种),为什么 -
keil C51代码优化等级介绍
2018-11-08 09:35:25keil优化等级图示: 0级 Constan folding 常数合并:编译器预先计算结果,尽可能用常数代替表达式。包括运行地址计算;...死代码删除:没用的代码段被删除; 拒绝跳转:严密的检查条件跳转,以确定是否... -
SEO优化之HTML代码优化最重要的5个标签
2019-04-27 08:10:44在我们之前的文章中也或多或少地向大家介绍了有关HTML代码的优化技巧,接下来将系统地讲解HTML代码的几个优化重点。 1.Title 标签 Title 标签能够告诉用户和搜索引擎网页页面的主题思想是什么,一直是搜索引擎的... -
一些代码优化的方法
2016-01-13 14:30:59当然不是,C++层次一样可以作代码优化,其中有些常常是意想不到的。在C++层次进行优化,比在汇编层次优化具有更好的移植性,应该是优化中的首选做法。 1 确定浮点型变量和表达式是 float 型 为了让编译器产生更好... -
王者荣耀2.0代码优化版C++
2018-12-14 21:15:19由于2.0的代码过长,所以作者决定将代码优化,现已优化完成: #include&amp;amp;lt;bits/stdc++.h&amp;amp;gt; //编个游戏,不顾一切的用万能 #include&amp;amp;lt;windows.h&amp;... -
JS代码优化的几种方式
2016-08-05 20:45:13如果你问我网站中最影响网站打开速度的是什么...细读完这篇文章,够你优化大半天的了,关于JS优化方法大都脱离不了这三种方法。 (网页总大小为155.k,而JS就占了100.3K) 是时候优化下JS了 关于JS优化的 -
Java 代码优化:使用构造函数和使用一个个setter的效率差别
2019-06-18 10:13:06在对Java代码进行优化的时候,想方设法的要提高整体的效率,使用JProfiler看代码的时间占比,然后,看看哪些部分是可以优化的,减少运行时间的。下面有这么几个方向。 ... 1,能使用构造函数一步到位的,就尽量使用...