精华内容
下载资源
问答
  • 代码优化三个阶段

    2011-11-23 00:02:49
    很久以前写的一个PPT,讨论了算法、流程及编码技巧三个层次的代码优化
  • 编译原理之代码优化

    万次阅读 多人点赞 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的值增加1T1的值与I保持线性关系,每次总是增加4。因此,可以把循环中计算T1值的乘法运算变换成在循环前进行一次乘法运算,而在循环中将其变换成加法运算。

    4. 变换循环控制条件
    IT1始终保持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)之间未改变T4T1的值,则将四元式(8)改为T6∶=T5[T1],这种变换称为复写传播。

    6. 删除无用赋值
    (6)T4赋值,但T4未被引用;另外,(2)(11)对I赋值,但只有(11)引用I。所以,只要程序中其它地方不需要引用T4I,则(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

    则按照上面基本块的划分,可以分成四个部分,四个部分的控制流分析可知可以得到一个循环图

    循环块最主要的特点是只有一个数据流和控制流入口,而出口可能有多个。循环优化的主要手段有:循环次数无关性代码外提、删除归纳变量和运算强度削弱。关于这三种手段的理解可以借助此前的描述进行类比,基本并无太多差异。

    展开全文
  • Web前端开发学习3:SEO代码优化

    千次阅读 2015-10-31 18:28:17
    代码优化概述  关于代码优化的知识是纯理论的知识,学习的很枯燥。在学到CSS时,不免遇到CSS+div进行代码优化的知 识,因此在网上看了一些关于这方面的知识,简单的整合一下,梳理自己所了解的代码优化问题。  ...

            一代码优化概述

           关于代码优化的知识是纯理论的知识,学习的很枯燥。在学到CSS时,不免遇到CSS+div进行代码优化的知

    识,因此在网上看了一些关于这方面的知识,简单的整合一下,梳理自己所了解的代码优化问题。

            所谓代码优化是指对程序代码进行等价(指不改变程序的运行结果)变换。程序代码可以是中间代码,也可以是

    标代码。等价的含义是使得变换后的代码运行结果与变换前代码运行结果相同。优化的含义是最终生成的目标代码

    (运行时间更短、占用空间更小),时空效率优化。原则上,优化可以在编译的各个阶段进行,但最主要的一类是

    对中间代码进行优化,这类优化不依赖于具体的计算机。

           简单地说,就是在不改变程序运行效果的前提下,对被编译的程序进行等价变换,使之能生成更加高效的目标代

    码。

           二那么对于我们学习Web前端开发的网页制作来说有哪些要求呢?

           (1)尽量采用div+css布局您的页面,div+css布局的好处是让搜索引擎爬虫能够更顺利的、更快的、更友好的爬完

    您的页面;div+css布局还可以大量缩减网页大小,提高浏览的速度,使得代码更简洁、流畅、更容易放置更多内容。

           (2)尽量缩减您的页面大小,因为搜索引擎爬虫每次爬行您的站点时,存储数据的容量有限,一般建议100KB以

    下,越小越好,但不能小于5KB。网页大小减少还有一个好处,能够促使您的站点形成巨大的内部链接网。

           (3)尽量少用无用的图片和flash。内容索引所派出的搜索引擎爬虫,不认识图片,只能根据图片“ALT,TITLE”等属

    性的内容判断图片的内容。对于flash搜索引擎爬虫更是视而不见。

           (4)尽量满足w3c标准,网页代码的编写满足W3C标准,能够提升网站和搜索引擎的友好度,因为搜索引擎收录标

    准,排名算法,都是在W3C标准的基础上开发的。

           (5)尽量更深层次套用标签h1、h2、h3、h4、h5…..,让搜索引擎能够分辨清晰网页那一块很重要,那一块次之。

           (6)尽量少用JS,JS代码全部用外部调用文件封装。搜索引擎不喜欢JS,影响网站的友好度指数。

           (7)尽量不使用表格布局,因为搜索引擎对表格布局嵌套3层以内的内容懒的去抓取。搜索引擎爬虫有时候也是比

    较懒的,望各位一定要保持代码和内容在3层以内。

           (8)尽量不让CSS分散在HTML标记里,尽量封装到外部调用文件。如果CSS出现在HTML标记里,搜索引擎爬虫

    就要分散注意力去关注这些对优化没有任何意义的东西,所以建议封装到专用CSS文件中。

           (9)清理垃圾代码,要把代码编辑环境下敲击键盘上的空格键所产生的符号;把一些默认属性代码,不会影响显示

    的代码;注释语句如果对代码可读性没有太大影响,清理这些垃圾代码,会减少不少的空间。

           一个网页是由多种语言组成的,最基础的是HTML,CSS和JS。那么代码优化对于这些的要求又是什么?

           三html优化

           html代码是最为基础的网站制作语言,对于网站优化来说,html代码也有一定的影响,是特别需要注意的优化细

    节之一。

           (1)title标签

           title标签就是网页的标题,是一个对于网站优化有很大影响的html标签,每个页面都必须有且内容不同!基本格

    式为<title>网页标题</title>。

           (2)META元素

           meta元素在浏览器界面是无法看到的html标签,对于优化有影响的主要有两个,一个是关键字(keywords)、一

    个是描述(description)。其实,这两个标签随着seoer的胡乱使用,对于网站优化已经没有多少用处了,你完全可以

    不用设定,但我们还是习惯性的设置一下较为妥当。关键字,设定与本页内容相关的主关键词一到三个,之间用英文

    状态下的逗号分割。需要注意的是,不要再滥用关键字,除了给搜索引擎不好的印象外别无他用。描述还是很有用的

    一个东东了,虽然对于网站的优化排名么有多大的影响,但会作为搜索引擎展示网站索引的一个依据,可以把你的主

    关键字链接起来组合成一段通顺的话,一般60到80字即可。格式为:

      <meta name="keywords"content="">

      <meta name="description"content="">

           (3)h标签

           h标签在html代码中是“标题”的意思,就如一篇文章,标题是文章最为重要的一个对象,也是搜索引擎在排名时重

    点考虑的一个对象。html中的h标签一共有六个,分别是h1/h2/h3/h4/h5/h6,分别代表不同的级别,我们称之为一级

    标题、二级标题……当然,一级标题具备更多的权重。需要注意的是h标签是块级元素,默认是粗体显示,独占一

    行,前后会有空行。当然,你可以利用css来改变这些效果。

          关于h标签的使用,需要根据实际情况来使用,不可任意滥用。如一篇文章,不可能出现多个一级标题,所以

    h1,在同一个页面中只能出现一次,而h2等则可以出现多次,根据你需要表现的内容的重要程度,分别使用不同的h

    标签即可。特别注意,h标签中最好出现关键字!还有就是,随其自然,万不可刻意地用h标签来处理某些关键字!

           基本格式为:<h1>这里是标题文字</h1>

           (4)加强和强调

           strong被认为是“加强”,em被认为是“强调”,也就是这两个标签是有特殊含义的,这对于网站优化至关重要。多

    数时候,我们在优化网站时会对关键字进行突出,这时使用strong或em就比使用B或者I好很多,特别谨记!

           (5)alt和title

           alt是图像中的注释,title是图像或链接的标题,这两者对于优化,尤其是图像的优化至关重要,但也不要滥用!

    一般在插入图像时,我们在alt中设置图像的描述内容,其中可以包含关键字但不要故意堆砌,title则当作图像标题来

    处理。特别需要注意的是,这些内容是不可以重复的!也就是说,当你的页面中有多张图像,你不能每张图像的描述

    和标题都设置成一样的,这样很容易被搜索引擎惩罚!

      <img src="test.jpg"alt="一个美女站在黄昏的街头默默等待爱人的回归,眼神中充满了忧伤"title="静待">除此之

    外,title属性在a标签中也有所使用,只是有些泛滥了,视觉效果也不好,影响用户体验,所以笔者并不推荐,除非你

    的a标签中的内容是一张图片。

      <a href="product.html"title="产品展示">产品展示</a>

           (6)缩写abbr(目前我没有遇到过)
           这个标签是自定义的一种缩写方式,可以利用它合理的添加一些关键字,同样不要滥用。如下所示:

      公司的产品涉及<abbr title="以石材为原料的雕刻作品">石雕</abbr>、<abbr title="以铜料为胚,运用雕刻、铸塑

    等手法制作的一种造型艺术">铜雕</abbr>、<abbr title="附属在某一平面上的雕刻艺术">浮雕</abbr>、镂雕等各种雕

    刻形式。
           (7)canonical标签

           Canonical(权威链接标记)是09年,Google,Yahoo及Microsoft三大搜索引擎联合推出了一个旨在减少重复内容的

    一个建议,并不是命令,也就是说这个标签搜索引擎可能不遵守。国内最大的中文搜索引擎百度也已经支持

    Canonical标签。

           部分搜索引擎引入了Link的一个新属性Canonical。A页面声明B为权威链接,B声明C为权威网页,则搜索引擎会

    认为C是A和B共同的首选权威版本。此时Canonical标签起到了301重定向的作用。只能作用于同一个域名所在的网

    址,不能作用于不同域名上的重复内容。也就是说如果文章被其它网站抄袭,也不会因为这个标签而给你的原文章带

    来权重。如果是跨站,可以使用301重定向。该链接标签可用于定义相对地址,也可用于定义绝对地址。但为了保险

    起见,建议使用绝对地址。

           使用方法:为网页指定权威链接(authoritative|canonical URL),以解决副本内容(duplicate content)问题。

           使用样式:<link rel=”canonical” href=”网页权威链接”/>

           四CSS优化

           于网站排名优化来说,css的几乎没有任何影响,但往大的方向如网站优化来说,样式表css的优化就至关重要

    了,其主要作用即是提高网页的响应速度。
           (1)外链css
           css的使用有多种方式,一是嵌入式,即在html标签中直接定义样式表,如下所示:<p style="font-family:arial;font-

    size:16px;font-weight:bold;">Outside now its raining,and tears are falling from my eyes…</p>

    还有一种是直接定义在页面头部的如下:<styletype="text/css">p{ background:#f1f1f1; color:#333; line-height:20px;}

     </style>这两种方式都是把css写在当前html中,这样会造成hml文档变大,降低网页的响应速度,所以我们需要外链

    css,将所有与本页面相关的样式写入到该样式表中:<link href="style/common.css"rel="stylesheet"type="text/css"/>

           (2)精简css

           对于这一点需要一定的css能力才可以做到了。所谓精简,指的是用尽可能少的样式代码实现整个网页的样式效

    果,需要充分利用css的继承和综合使用,举一个简单的例子来说明。如页面中的链接,全部不需要下划线、大部分

    为12像素,但链接的颜色并不相同,个别的字体效果也不相同,我们就可以这样来写:
           a{ text-decoration:none; font-size:12px;}/*定义通用a样式*/
           a.a_red{ color:#e00;}
           a.a_blue{ color:#009;}
           a.a_menu{ color:#fff; font-size:14px; font-weight:bold;}/*针对特殊a标签只指定特殊样式*/
           因为css的继承作用,a_red和a_blue都具备没有下划线、12像素这一样式,而a_menu同样具备没有下划线,但

    因指定了大小,就不再继承12像素的指定而使用14像素。

            (3)整合css

            一般情况下,前端制作人员喜欢把通用样式写成一个文件,把专用样式写成另一个文件以便各个页面调用。如笔

    者,就喜欢把页面通用样式(包括通用的布局样式、文字样式等)写在common.css中,而把专用的写在另一个样式

    表中。如首页,我们就需要调用common.css和index.css两个样式表文件。这样做,对于前端来说是正确的。但对于

    优化,却不太好。多一个文件调用就需要多一次请求,当然也会多耗费一点时间。所以,在网站制作完成后,我们需

    要把页面的所有样式合并大一起以提高网页的响应速度!但需注意,合并css不利于网站后期整改,权衡利弊各取所

    需吧,具体是否合并还需根据你的实际情况而定。
            (4)压缩css

            压缩css其实很简单,就是去掉多余的空格和换行。实现起来也非常的简单,网上有很多工具,请自行搜索“css

    压缩”即可找到很多在线压缩工具。同上面一点,压缩后的css不便于后期整改,需要自己权衡取舍。

            五JavaScript优化编辑

            javascript代码对于网站排名优化同样没有多大影响,但从网站优化的角度来看却是至关重要的一步优化操作,

    优良的javascript代码可以大幅度提升网页的响应速度!

            (1)外链js代码

            js代码跟css的使用差不多,都有三种方式:

           内部定义:<A οnclick="if(confirm('确认?'){...}else{...})"href="#">confirm</A>

         头部插入:<script>...</script>

         外链调用:<SCRIPT language=javascript type=text/javascript src="jquery-1.7.2.min.js"></SCRIPT>

            (2)精简js代码

            这一点需要更为专业的js技术才能做到,尽可能根据需要实现的效果编写js,而不用从网上找一段代码直接拿来

    用,网上的代码很多功能很全,从而质量很大,而其中的很多功能对于我们要实现的效果是没有任何用处的,所以广

    拓企业网站建议你针对需要实现的效果定制js以便得到更为精简的代码,从而提高网页的响应速度。

            (3)压缩js代码
            对于这点,网上也有很多的工具,请百度查询“js压缩工具”即可。

            (4)置底js代码

            一般情况下,我们都是把js放到head之间的,这种方式在页面加载时即会加载,当然也就导致响应速度的降低

    ,百度站长平台建议把js放到页面最底部,也就是</html>之外。等html加载完毕之后才加载js代码,当然,有部分特

    殊功能的js代码是没有办法放到页面底部的,具体请根据实际情况操作。

           补充说明:针对js图像特效等,可能会影响网站关键字排名的!有的特效图像的路径、说明等都是写入到js中的,

    这种特效尽量不要使用。

           上面说到了三种技术对网页优化起到的作用,我们都知道CSS+div是一个重要的优化过程。我们要着重说一下

    CSS+DIV的优化问题。

           六DIV+CSS优化

           对于蜘蛛在爬行一个网站的页面时,若是有太多的垃圾代码,会使搜索蜘蛛对其产生不友好、不信任感,同时蜘

    蛛的爬行速度也会因此而减缓,对于网站SEO而言,可谓一大忌。就如传统的用table页面,对此我们就需要对网站进

    行代码优化,而这便需要动用CSS+div了,下面便来谈谈使用CSS+div进行代码优化的一些益处。

            (1)精简代码,降低重构难度。

            网站使用DIV+CSS布局使代码很是精简,相信大多朋友也都略有所闻,css文件可以在网站的任意一个页面进行

    调用,而若是使用table表格修改部分页面却是显得很麻烦。要是一个门户网站的话,需手动改很多页面,而且看着那

    些表格也会感觉很乱也很浪费时间,但是使用css+div布局只需修改css文件中的一个代码即可。

           (2)网页访问速度

           使用了DIV+CSS布局的网页与Table布局比较,精简了许多页面代码,那么其浏览访问速度自然得以提升,也从

    而提升了网站的用户体验度。

           (3)SEO优化

           采用div-css布局的网站对于搜索引擎很是友好,因此其避免了Table嵌套层次过多而无法被搜索引擎抓取的问

    题,而且简洁、结构化的代码更加有利于突出重点和适合搜索引擎抓取。

           (4)浏览器兼容性

           DIV+CSS相比TABLE布局,更容易出现多种浏览器不兼容的问题,主要原因是不同的浏览器对web标准默认值不

    同。国内主流是ie,firefox及chrome用的较少,在兼容性测试方面,首先需要保证在ie多版本不出现问题,这里涉及

    到一些方法和技巧,可以针对具体问题在网站查找解决办法。

           (5)CSS+DIV网页布局的时候常犯的小错误

           1. 检查HTML元素是否有拼写错误、是否忘记结束标记

            即使是老手也经常会弄错div的嵌套关系。可以用dreamweaver的验证功能检查一下有无错误。

           2. 检查CSS是否书写正确

           检查一下有无拼写错误、是否忘记结尾的 } 等。可以利用CleanCSS来检查 CSS的拼写错误。CleanCSS本是为

    CSS减肥的工具,但也能检查出拼写错误。

           3. 用删除法确定错误发生的位置

           如果错误影响了整体布局,则可以逐个删除div块,直到删除某个div块后显示恢复正常,即可确定错误发生的位

    置。
           4. 利用border属性确定出错元素的布局特性

           使用float属性布局一不小心就会出错。这时为元素添加border属性确定元素边界,错误原因即水落石出。

           5. float元素的父元素不能指定clear属性

           MacIE下如果对float的元素的父元素使用clear属性,周围的float元素布局就会混乱。这是MacIE的著名的bug,倘

    若不知道就会走弯路。

           6. float元素务必指定width属性

    很多浏览器在显示未指定width的float元素时会有bug。所以不管float元素的内容如何,一定要为其指定width属性

    另外指定元素时尽量使用em而不是px做单位。

           7. float元素不能指定margin和padding等属性

           IE在显示指定了margin和padding的float元素时有bug。因此不要对float元素指定margin和padding属性(可以在

    float元素内部嵌套一个div来设置margin和padding)。也可以使用hack方法为IE指定特别的值。

           8. float元素的宽度之和要小于100%

    如果float元素的宽度之和正好是100%,某些古老的浏览器将不能正常显示。因此请保证宽度之和小于99%。

           9. 是否重设了默认的样式?

    某些属性如margin、padding等,不同浏览器会有不同的解释。因此最好在开发前首先将全体的margin、padding设置

    为0、列表样式设置为none等。
           10. 是否忘记了写DTD?

           如果无论怎样调整不同浏览器显示结果还是不一样,那么可以检查一下页面开头是不是忘了写下DTD声明。最

    后,需要注意的是,蜘蛛不喜欢一个页面有太多的css代码,否则同样会影响蜘蛛的爬行,影响搜索引擎的收录,所

    以采用外部调用的方式调用CSS是非常不错的方法。而同时,若非必须太多花哨的网站,采用CSS布局,同样可以到

    达所想要的效果。如网站导航中的文字颜色变化、下拉菜单等。

           

           

           





    展开全文
  • 程序编译与代码优化

    千次阅读 2016-05-31 15:20:23
    一早期(编译期)优化 1概述 Java语言的“编译期”是一段“不确定”的操作过程,因为它可能是指一前端编译器(其实叫“编译器的前端”更准确一些)把*.java文件转变成*.class文件的过程;也可能是指虚拟机的...

    一早期(编译期)优化

    1概述

    Java语言的“编译期”是一段“不确定”的操作过程,因为它可能是指一个前端编译器(其实叫“编译器的前端”更准确一些)把*.java文件转变成*.class文件的过程;也可能是指虚拟机的后端运行期编译器(JIT编译器,just in time compiler)把字节码转变成机器码的过程;还可能是指使用静态提前编译器(AOT编译器,ahead of time compiler)直接把*.java文件编译成本地机器代码的过程。下面列举了这三类编译过程中一些比较有代表性的编译器:                                                     Ø 前端编译器:sun的javac、eclipse JDT中的增量式编译器(ECJ)。

    Ø JIT编译器:HotSpot VM 的C1、C2编译器。

    Ø AOT编译器:GNU Compiler for the java、Excelsior JET。

    这三类过程中最符合大家对java程序编译认知的应该是第一类,在后面的讲解里,提到的“编译期”和“编译器”都仅限于第一类编译过程。限制了编译范围后,对于“优化”二字的定义就需要宽松一些,因为javac这类编译器对代码的运行效率几乎没有任何优化措施(在JDK1.3之后,javac的-O优化参数就不再有意义了)。虚拟机设计团队把对性能的优化集中到了后端的即时编译器中,这样可以让那些不是由javac产生的class文件也同样能享受到编译器优化带来的好处。

    但是javac做了许多针对编码过程的优化措施来改善程序员的编码风格和提高编码效率。相当多新生的java语法特性,都是靠编译器的“语法糖”来实现,而不是依赖虚拟机的底层改进来支持,可以说,java中即时编译器在运行期的优化过程对于程序运行来说更重要,而前端编译器在编译期的优化过程对于程序编码来说关系更加密切。

    2.javac 编译器

    分析源码是了解一项技术实现内幕的最有效的手段,javac编译器不像HotSpot虚拟机那样使用c++语言(包含少量C语言)实现,它本身就是一个由java语言编写的程序,这为纯java的程序员了解它的编译过程带来了很大的便利。

    2.1 javac 的源码与调试

    虚拟机规范严格定义了Class文件的格式,但是对如何把java源码文件转变为class文件的编译过程未作任何定义,所以这部分内容是与具体JDK实现相关的。从sun javac的代码来看,编译过程大致可以分为三个过程,分别是:

    • 解析与填充符号表过程;
    •  插入式注解处理器的注解处理过程;
    • 分析与字节码生成过程。

    Javac编译动作的入口是com.sun.tools.javac.main.JavaCompiler类,上述三个过程的代码逻辑集中在这个类的compile()和compile2()方法里。整个编译最关键的处理是由8个方法来完成的,分别是:

    [JavaScript] view plaincopy

    1. initProcessAnnotations(processors);//准备过程:初始化插入式注解处理器  

    2. delegateCompiler =   

    3.     processAnootations(  //过程2:执行注解处理  

    4.     enterTrees(stopIfError(CompileState.PARSE,    //过程1.2:输入到符号表  

    5.     parseFiles(sourceFileObject))),      //过程1.1:词法分析、语法分析  

    6.     classnames);  

    7.       

    8. delegateCompiler.compile2();   //过程3:分析及字节码生成  

    9.     case BY_TODO:  

    10.     while(! todo.isEmpty())  

    11.     generate(desugar(flow(attribute(todo.remove()))));  

    12.     break;  

    13.     //generate,过程3.4:生成字节码  

    14.     //desugar,过程3.3解语法糖  

    15.     //flow,过程3.2:数据流分析  

    16.     //attribute,过程3.1:标注      

    语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机科学家Peter J. Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

    Java在现代编程语言之中属于“低糖语言”(相对于C#及许多其他jvm语言来说),尤其是JDK1.5之前的版本,“低糖”语法也是java语言被怀疑已经“落后”的一个表面理由。Java中最常用的语法糖主要是泛型、变长参数、自动装箱拆箱,等等,虚拟机运行时不支持这些语法,它们在编译阶段被还原回简单的基础语法结构,这个过程就被称为解语法糖。

    3.java语法糖的味道

    几乎各种语言或多或少都提供过一些语法糖来方便程序员的代码开发,这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高效率,或能提升语法的严谨性,或能减少代码出错的机会。不过也有一种观点认为语法糖并不一定都是有益的,大量添加和使用含糖的语法容易让程序员产生依赖,无法看清语法糖的糖衣背后程序代码的真实面目。

    总而言之,语法糖可以看做是编译器实现的一些小把戏,这些小把戏可能会使得效率有一个大提升,但我们也应该去了解这些小把戏背后的真实世界,那样才能利用好它们,而不是被它们所迷惑。

    3.1泛型与类型擦除

    泛型是JDK1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。

    泛型思想早在C++语言的模板(Template)中就开始生根发芽,在java语言还没有出现泛型时,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化。例如在哈希表的存取中,JDK1.5之前使用HashMap的get()方法,返回值就是一个Object对象,由于java语言里面所有的类型都继承于java.lang.Object,那Object转型成任何对象都是有可能的。但是也因为有无限的可能,就只有程序员和运行期的虚拟机才知道这个Object到底是个什么类型的对象。在编译期间,编译器无法检查这个Object的强制转型是否成功,如果仅仅依赖程序员去保障这项操作的正确性,许多ClassCastException的风险就会被转嫁到程序运行期中。

    泛型技术在C#和java之中的使用方式看似相同,但实现上却有着根本性的分歧,C#里面泛型无论在程序源码之中、编译后的IL中(Intermediate Language,中间语言,这时候泛型是一个占位符)还是在运行期的CLR(common language runtime)中都是切实存在的,List<int>与List<String>就是两个不同的类型,它们在系统运行期生成,有自己的虚方法表和类型数据,这种实现成为类型膨胀,基于这种方法实现的泛型被称为真实泛型。

    Java语言中的泛型则不一样,它只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此对于运行期的java语言来说,ArrayList<int>与ArrayList<String>就是同一个类。所以说泛型技术实际上是java语言的一颗语法糖,java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型被称为伪泛型。

    代码1是一段简单的java泛型例子,我们看一下它编译后的结果:

    1. <span style="font-size:18px;"></span><pre name="code" class="java">public static void main(String[] args){  

    2.         Map<String, String> map = new HashMap<String, String>();  

    3.         map.put("hello", "你好");  

    4.         map.put("how are you", "吃了没");  

    5.         System.out.println(map.get("hello"));  

    6.         System.out.println(map.get("how are you"));  

    7.     }  

    把这段代码编译成class文件,然后再用字节码反编译工具进行反编译后,将会发现泛型都不见了,程序又变回了java泛型出现之前的写法,泛型类型都变回了原生类型:

    1. <span style="font-size:16px;">public static void main(String[] args){  

    2.         Map map = new HashMap();  

    3.         map.put("hello", "你好");  

    4.         map.put("how are you", "吃了没");  

    5.         System.out.println((String)map.get("hello"));  

    6.         System.out.println((String)map.get("how are you"));  

    7.         }</span>  

    当初JDK设计团队为什么选择类型擦除的方式来实现java语言的泛型支持呢?是因为实现简单、兼容性考虑还是别的原因?我们不得而知,但确实有不少人对java语言提供的伪泛型颇有微词,当时甚至连《thinging in java》的作者Bruce Eckel也发表了一篇文章《这不是泛型!》来批评JDK1.5的泛型实现。

    1. public class GenericTypes {  

    2.   

    3.     public static void method(List<String> list){  

    4.         System.out.println("invoke method(List<String> list)");  

    5.     }  

    6.       

    7.     public static void method(List<Integer> list){  

    8.         System.out.println("invoke method(List<Integer> list");  

    9.     }  

    10.     }  

    请想一想,上面这段代码是否正确,能否编译执行?答案是不能被编译的,是因为参数List<Integer>和List<String>编译之后都被擦除了,变成了一样的原生类型List<E>,擦除动作导致这两个方法的特征签名变的一模一样。初步来看,无法重载的原因已经找到了,但是真的如此吗?只能说,泛型擦除成相同的原生类型只是无法重载的一部分原因,请再看一下下面的代码:

    1. public class GenericTypes {  

    2.   

    3.     public static String method(List<String> list){  

    4.         System.out.println("invoke method(List<String> list)");  

    5. return "";  

    6.     }  

    7.       

    8.     public static int method(List<Integer> list){  

    9.         System.out.println("invoke method(List<Integer> list");  

    10.     }  

    11.     return "1";  

    12.     }  

    这两段代码的差别,是两个method方法添加了不同的返回值,由于这两个返回值的加入,方法重载居然成功了,即这段代码可以被编译和执行了。重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。  

    上面代码中的重载当然不是根据返回值来确定的,之所以这次编译和执行能成功,是因为两个method()方法加入了不同的返回值后才能共存在一个class文件中。之前介绍过class文件内容中方法表(method_info),方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名之中,所以返回值不参与重载选择,但是在class文件格式之中,只要描述符不是完全一致的两个方法就可以共存。也就是说两个方法如果有相同的名称和特征签名,但返回值不同,那它们也是可以合法的共存于一个class文件中。

    3.2自动装箱、拆箱与遍历循环

    自动装箱、拆箱与遍历循环是java语言里使用的最多的语法糖。如下代码所示:

    public static void main(String[] args){  

    1.         List<Integer> list = Arrays.asList(1,2,3,4);  

    2.           

    3.         int sum = 0;  

    4.         for(int i:list){  

    5.             sum += i;  

    6.         }  

    7.         System.out.println(sum);  

    8.         }  

    9.     //上述代码编译之后的变化:  

    10.     public static void main(String[] args){  

    11.         List list = Arrays.asList(new Integer[]{  

    12.                 Integer.valueOf(1),  

    13.                 Integer.valueOf(2),  

    14.                 Integer.valueOf(3),  

    15.                 Integer.valueOf(4),  

    16.         });  

    17.           

    18.         int sum = 0;  

    19.         for(Iterator localIterator = list.iterator(); localIterator.hasNext();){  

    20.             int i = ((Integer)localIterator.next()).intValue();  

    21.             sum += i;  

    22.         }  

    23.         System.out.println(sum);  

    24.         }  

    上面第一段代码中一共包含了泛型、自动装箱、自动拆箱、遍历循环与变长参数五种语法糖,上面第二段代码则展示了它们在编译后的变化。泛型就不必说了,自动装箱、拆箱在编译之后被转化成了对应的包装和还原方法,如本例子中的Integer.valueOf()与Integer.intValue()方法,而遍历循环则是把代码还原成了迭代器的实现,这也是为何遍历循环需要被遍历的类实现Iterable接口的原因。最后再看看变长参数,它在调用的时候变成了一个数组类型的参数,在变长参数出现之前,程序员就是使用数组来完成类似功能的。

    这些语法糖虽然看起来很简单,但也不见得就没有任何值得我们注意的地方,下面的代码演示了自动装箱的一些错误用法:

    1. public static void main(String[] args){  

    2.         Integer a = 1;  

    3.         Integer b = 2;  

    4.         Integer c = 3;  

    5.         Integer d = 4;  

    6.         Integer e = 321;  

    7.         Integer f = 321;  

    8.         Long g = 3L;  

    9.           

    10.         System.out.println(c == d);    //false 值不等  

    11.         System.out.println(e == f);    //false  堆位置不同  

    12.         System.out.println(c == (a + b)); //true   "+"运算符拆包  

    13.         System.out.println(c.equals(a + b));//true equals 值比较  

    14.         System.out.println(g == (a+b));//true   "+"运算符拆包  

    15.         System.out.println(g.equals(a+b));//false   类型不同  

    16.     }  

    请思考两个问题:一是代码中的6句打印语句输出时什么?二是6句打印语句中,解除语法糖后参数是什么样?鉴于包装类的“==”运算在没有遇到算数运算的情况下不会自动拆箱,而且它们的equals()方法不会处理数据转型的关系,我们建议在实际编码中应该尽量避免这样使用装箱与拆箱。integer 的范围:-128~127,在这个范围内的integer和int是一样的,还没有在堆中开辟空间。

    3.3条件编译

    许多程序设计语言都提供了条件编译的途径,如C、C++中使用预处理器指示符(#ifdef)来完成条件编译。C、C++的预处理器最初的任务是解决编译时的代码依赖关系(众所周知的#include预处理指令),而在java语言之中并没有使用预处理器,因为java语言天然的编译方式(编译器并非一个一个的编译java文件,而是将所有的编译单元的语法树顶级节点输入到待处理列表后再进行编译,因此各个文件之间能够互相提供符号信息)无需使用预处理器。那java语言是否有办法实现条件编译呢?

    Java语言当然也可以进行条件编译,方法就是使用条件为常量的if语句。如下代码所示,此代码中的if语句不同于其他java代码,它在编译阶段就会被“运行”,生成的字节码之中只包括System.out.println("block 1");一条语句,并不会包含if语句及另外一个分支中的System.out.println("block 2");。

    1. <span style="font-family:'Microsoft YaHei';font-size:16px;">    

    2.     public static void main(String[] args){  

    3.         if(true){  

    4.             System.out.println("block 1");  

    5.         }else{  

    6.             System.out.println("block 2");  

    7.         }  

    8.         }  

    9.     //此代码编译后class文件的反编译结果:  

    10.     public static void main(String[] args){  

    11.             System.out.println("block 1");  

    12.     }</span>  

    只能使用条件为常量的if语句才能达到上述效果,如果使用常量和其他带有条件判断能力的语句搭配,则可能在控制流分析中提示错误,被拒绝编译,如下面代码所示,就会被编译器拒绝编译:

    1. <span style="font-family:'Microsoft YaHei';font-size:16px;">public static void main(String[] args){  

    2.         while(false){  

    3.             System.out.print("");  

    4.             }</span>  

    java语言中条件编译的实现,是java语言的一个语法糖,根据布尔常量值的真假,编译器将会把分支中不成立的代码块消除掉,这一工作将在编译器解除语法糖的阶段完成。由于这种条件编译的实现方式使用了if语句,所以它必须遵循最基本的java语法,只能写在方法体内部,因此它只能实现语句基本块级别的条件编译,而没有办法实现根据条件调整整个java类的结构。

    除了我们介绍的泛型、自动装箱、自动拆箱、遍历循环、变长参数和条件编译之外,java语言还有不少其他的语法糖,如内部类、枚举类、断言语句、对枚举和字符串的switch支持、在try语句中定义和关闭资源等,可以通过跟踪javac源码、反编译class文件等方式了解它们的本质实现。


     

    二.晚期(运行期)优化

    1.概述

    JAVA最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会把这些代码认定为“热点代码”而将它们编译成本地机器码,并进行各种层次的优化,完成这个任务的编译器成为即时编译器(Just In Time Compiler)。

           Java虚拟机规范并没有规定虚拟机内必须要有即时编译器。但是,即时编译器性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一,它也是虚拟机中最核心最能体现技术水平的部分。

    2.HotSpot虚拟机内的即使编译器

           HOTSPOT和J9都是解释器和编译器并存,保留解释器的原因是,加快启动时间,立即执行,当运行环境中内存资源限制较大时,解释器可以节约内存,解释器还可以作为激进优化的编译器的“逃生门”(称为逆优化Deoptimization),而编译器能把越来越多的代码编译成本地代码后,获取更高的执行效率。

           HOTSPOT内置了两个即时编译器,clientcompiler和servercompiler,称为C1,C2(clientcompiler获取更高的编译速度,servercompiler来获取更好的编译质量),默认是采用解释器与其中一个编译器直接配合的方式工作。HOTSPOT会根据自身版本和宿主机器的性能自动选择运行模式,用户也可以使用-client或-server来决这种解释器编译器搭配的方式成为混合模式,用户还可以使用-Xint强制虚拟机使用“解释模式”,也可以使用-Xcomp强制“编译模式”。

    被编译的触发条件:

    1.   被多次调用的方法

    2.   被多次执行的循环体(栈上替换)OSR On StackReplacement

    判断是否是热点代码的行为成为热点探测:hot spotdetection,主要的热点探测方式主要有两种:

    1.   基于采样的热点探测,JVM会周期性检查各个线程的栈顶,如果某个方法经常出现在栈顶,那就认定为热点方法。简单高效,精度不够。

    2.   基于计数器的热点探测,统计方法执行次数。(HOTSPOT使用这种方式)

    HOTSPOT有两个计数器:方法调用计数器和回边计数器

    方法调用计数器client默认1500次,server默认10000次,可以通过参数-XX:CompileThreshold来设定。调用方法时,会先判断是否存在编译过的版本,如果有则调用该版本,否则计数器加1,然后看方法调用计数器和回边计数器之和是否超过方法调用计数器的阈值。超过,则提交编译请求

    方法调用计数器并不是统计方法调用绝对次数,而是一个相对执行频率,超过一定时间,如果方法调用次数不足以让它提交给编译器,则计数器就会被减少一半,这种现象称为热度衰减(Counter Decay)进行热度衰减的动作是在垃圾回收时顺便进行的,而这段时间就被称为半衰周期(Counter Half Life Time可用-XX:-UseCounterDecay来关闭热度衰减,用-XX:CounterHalfLifeTime来设置半衰时间。

    回边计数器用于统计方法中循环体的执行次数。字节码遇到控制流向后跳转 的指令成为回边。建立回边计数器统计的目的就是为了触发OSR编译。回边的控制参数有:

    -XX:BackEdgeThreshold,-XX:OnStackReplacePercentage。

           1.在Client模式下,回边计数器阀值计算公式:方法调用计数器阀值乘以OSR比率,然后除以100.

           2.在server模式下,回边计数器阀值计算公式:方法调用计数器阀值乘以(OSR比率,然后减去解释器监控比率的差值)除以100。

           与方法计数器不同,回边计数器没有计数热度衰减的过程,因此这个计数器统计的就是该方法循环执行的绝对次数。

    编译过程

           对Client Compiler而言,是一个简单快速的三段式编译器,主要关注点在于局部性的优化,放弃了许多耗时较长的全局优化手段。

    1.   第一阶段,一个平台独立的前段将字节码构造成一种高级中间代码表示(HIR)。

    2.   第二阶段,一个平台相关的后端从HIR中产生低级中间代码表示(LIR),而在此之前会在HIR上完成一些优化。

    3.   最后节点是在平台相关的后端使用线性扫描算法在LIR上分配寄存器,并在LIR上座窥孔优化,然后产生极其代码。

           对Server Compiler则是专门面向服务端的典型应用并为服务端的性能配置特别调整过的编译器。它会执行所有的经典的优化动作,如:无用代码消除,循环展开,循环表达式外提,公共子表达式消除,常量传播,基本块重排序等,还会实施一些与Java语言特性密切相关的优化技术,如范围检查消除,空值检查消除。

     

    3编译优化技术

           JDK设计团队几乎把代码的所有优化措施都集中在了即使编译器,所以一般来说即即时编译器产生的本地代码会比Javac产生的字节码更优秀。接下来介绍几种景点优化技术:

           1.公共子表达式消除:如果一个表达式E已经被计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为了公共子表达式。若这种优化仅限于程序基本块内,称为局部公共子表达式消除;若这种优化的范围涵盖了多个基本块,就称为全局公共子表达式消除。

           2.数组边界检查消除:在Java语言中访问数组元素的时候系统将会自动进行上下界的范围检查,即检查i必须满足i>=0 && i<foo.length这个条件。

           3.方法内联:它除了消除方法调用的成本之外,更重要的意义是为其他优化手段建立良好的基础。由于Java语言中默认的实例方法就是虚方法,对于虚方法,编译器做内联的时候根本就无法确定应该使用哪个方法版本。为了解决虚方法的内联问题,引入了“类型继承关系分析”。编译器在进行内联时,如果是非虚方法,那么直接进行内联,如果是虚方法,则会向CHA查询此方法在当前程序下是否有多个目标版本可供选择,如果只有一个版本,那也可以进行内联,不过这种内联就属于激进优化,需要预留一个逃生门,万一加载了导致继承关系发生变化的新类,那就需要退回到解释状态执行,或者重新编译。

           4.逃逸分析:它是为其他优化手段提供依据的分析技术。基本行为就是分析对象动态作用于,当一个对象在方法里面被顶以后,它可能被外部方法所引用,称为线程逃逸。若能证明一个对象不会逃逸到方法或线程之外,就可以进行一些高效的优化,如:栈上分配(对象所占用的内存空间可以随栈帧出栈而销毁,若在堆里分配的话,回收和整理内存都需要消耗时间),同步消除(线程同步本身就是一个相对耗时的过程,若确定不会逃逸出线程,对这个变量就不需要实施同步措施),标量替换(将Java对象拆散,根据程序访问的情况,将其使用到的成员变量恢复原始类型来访问,若不会逃逸的话,执行程序的时候将可能不创建对象,而直接创建它的若干个被这个方法使用到的成员变量来代替)。

     

    4Java与c/c++编译器对比

           Java与c/c++的编译器对比实际上代表了最经典的即时编译器与静态编译器的对比。Java可能会下列原因导致输出本地代码有一些劣势:

    1.   首先,因为即时编译器运行占用的是用户程序的运行时间,具有很大的时间压力,它能提供的优化手段也严重受制于编译成本。

    2.   其次,Java语言是动态的类型安全语言,这就意味着需要由虚拟机来确保程序不会违反语言的语义或访问非结构化内存。

    3.   第三,Java语言中虽然没有virtual关键字,但是使用虚方法的频率却远远大于c/c++语言,这就意味着运行时对方法接受者进行多态选择的频率要远远大于c/c++语言,也以为即时编译器在进行一些优化时的难度远远大于c/c++的静态优化编译器。

    4.   第四,Java语言是可以动态扩展的语言,运行时加载新的类可能改变程序类型的继承关系,编译器不得不时刻注意并随着类型的变化而在运行时撤销或重新进行一些优化。

    5.   第五,Java语言中对象的内存分配都是在堆上进行的,只有方法中的局部变量才能在栈上分撇。

    Java语言的这些性能上的劣势都是为了换取开发效率上的优势,动态安全、动态扩展、垃圾回收这些特性都为Java语言的开发效率做出了很大的贡献。
    展开全文
  • 目标代码生成与代码优化

    千次阅读 2013-12-29 10:49:12
     java语言由于是半编译半解释的语言,之所以能跨平台是因为在编译的阶段只生成java虚拟机能识别的中间代码,而不是目标代码,再由Java虚拟机来解释成机器码。  目标代码的生成需要获取寄存器的个数和

                 目标代码的生成与机器的硬件环境有关,所以有些语言不能跨平台的原因就在于编译时需要了解机器的寄存器等资源,不同的机器寄存器不同,导致代码需要二次编译。

                 java语言由于是半编译半解释的语言,之所以能跨平台是因为在编译的阶段只生成java虚拟机能识别的中间代码,而不是目标代码,再由Java虚拟机来解释成机器码。

                目标代码的生成需要获取寄存器的个数和使用情况,这里需要用到寄存器选择算法。

                为了使目标代码的执行效率更高,需要设置两个数据结构:待用信息,活跃信息。在基本模块之内,记录各个变量的使用情况,然后根据待用信息来确定是否将寄存器的数据替换或者保留。活跃信息是在模块之外确定数据否需要保留或者替换。

                代码优化包括:代码外提,删除无用代码,删除公共子表达式等。

               代码外提:主要在循环结构中使用,这样能成倍的提高代码的运行效率。

               删除公共表达式: 对于相同的三地址代码可以进行合并,删除。

               删除无用代码: 对于一些临时变量和没有使用的代码可以进行删除。

            程序流图的画法:

                     


            DAG图代码优化方法:

                           





      

    展开全文
  • 编译原理过程简述及中间代码优化

    千次阅读 2017-09-28 17:21:23
    一、编译过程图示如下:词法分析作用:找出单词 。...二、中间代码优化所谓代码优化是指对程序代码进行等价(指不改变程序的运行结果)变换。程序代码可以是中间代码(如四元式代码),也可以是目标代码。
  • 从计算机程序出现的第一天起,对效率的追求就是程序天生的坚定信仰,这过程犹如一场没有终点,永不停歇的F1方程式竞赛,程序员试车手,技术平台则是在赛道上飞驰的赛车。 文章目录早期(编译期)优化概述Javac...
  • 一些代码优化的方法

    千次阅读 2016-01-13 14:30:59
    当然不是,C++层次一样可以作代码优化,其中有些常常是意想不到的。在C++层次进行优化,比在汇编层次优化具有更好的移植性,应该是优化中的首选做法。 1 确定浮点型变量和表达式是 float 型 为了让编译器产生更好...
  • DSP代码优化方法(2)

    千次阅读 2011-09-15 12:09:22
    工作流程一般分为三个阶段。  阶段一:直接按照需要用C语言实现功能。在实际的DSP应用中,许多算法都是非常复杂,直接用汇编代码编写,虽然优化效率很高,可是实现的难度却很大,所以一般都采用先用C语言来实现,...
  • java优化代码常见套路

    万次阅读 多人点赞 2019-12-28 11:32:03
    程序员的痛点(烂代码) ...首先说一最重要的优化原则:代码优化是你觉得你代码很繁琐、阅读性很差的时候一定要马上优化,立刻马上,不管你现在有多忙,每天优化才叫重构,每年优化那叫重写 这原则为什么重...
  • JAVA代码效率优化

    千次阅读 2018-05-13 11:28:53
    最近在想自己编程时是否注意过代码的效率问题,得出的答案是:没有。...下面是网上找的一篇关于JAVA代码优化的文章,觉得不错,就转载了。这里面设计到了JAVA基础和J2EE方面的优化建议,有时间会整理一下,现在...
  • 程序优化三个级别

    千次阅读 2005-10-16 19:17:00
    程序优化的三个级别 HouSisong@GMail.com 2004.11.07整理tag:代码优化,程序优化,综级优化,代码调整,新的视角,表驱动状态机文章来源于abp论坛中的一篇帖子:http://bbs.allaboutpro
  • Python 代码性能优化技巧

    千次阅读 2012-10-10 16:37:31
    选择了脚本语言就要忍受其速度,这句话在某种程度上说明了 python 作为脚本的一不足之处,那就是执行效率和性能不够理想,特别是在 performance 较差的机器上,因此有必要进行一定的代码优化来提高程序的执行效率...
  • python 代码 性能优化技巧

    千次阅读 2012-07-24 13:22:55
    Python 代码优化常见技巧 ...代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构、优化、扩展以及文档相关的事情通常需要消耗 80%
  • 目前Vue.js的火爆不亚于当初的React,本人对写代码有洁癖,代码也是艺术。此篇是准备篇,工欲善其事,必先利其器。我们先在代码层面进行优化,对我们完成整个技术架构是起到基础作用的。
  • DSP学习的三个阶段

    千次阅读 2012-03-14 10:46:28
    研究生期间断断续续做过TI DSP研究,从一对嵌入式一点也没有了解的新手到快毕业时完成一自认为满意的项目,其中走过很多弯路,现在把我对DSP学习的心得和一些参考文献列出来,可能对初学者具有帮助。...
  • 关于软件测试的几点反思 - 测试工作的三个阶段

    万次阅读 多人点赞 2014-03-18 23:11:16
    其实这样的三个阶段也是一个粗略的划分,并不一定要逐步的来发展,其实都是一些具体的做法和实践。以我目前经历过的实践只想到这样的层次,应该还有更高级的阶段。 我们越到后面我们发现进一步的努力带来的提升幅度...
  • 智能优化算法:海鸥优化算法-附代码

    万次阅读 2020-07-23 14:24:01
    2019智能算法:海鸥优化算法-附代码 摘要:本文简单介绍智能优化算法-海鸥优化算法 1.原理 海鸥是遍布全球的海鸟,海鸥种类繁多且大小和身长各不相同。 海鸥是杂食动物,吃昆虫、鱼、爬行动物、两栖动物和蚯蚓等。 ...
  • 浮点运算和代码优化, 音频常识, 并行计算
  • 本节对UCC编译器的中间代码生成及优化进行简介,给出基本块BasicBlock、地址码、控制流图CFG的相应数据结构,介绍有条件跳转、无条件跳转和间接跳转等概念。
  • 在公司有一需求需要将前端传过来的10张照片,后端接收过来进行处理以后压缩成一压缩包通过网络流传输出去。之前没有接触过用Java压缩文件的,所以就...未优化压缩文件的代码如下: private static String ZI...
  • 概述在部分的商用虚拟机(Sun HotSpot、IBM J9)中,...为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这任务的编译器称为即时编译器(Just
  • 前言今日头条 iOS 端从 2016 年起就关注到了安装包大小的问题,并启动了包大小优化。2017 年,我们将当时的经验发表为技术文章 《干货|今日头条iOS端安装包大小优化—思路与实践...
  • 智能优化算法:蝗虫优化算法-附代码

    千次阅读 热门讨论 2020-07-30 17:20:04
    智能算法:蝗虫优化算法-附代码 摘要:蝗虫算法( Grasshopper Optimization Algorithm,GOA ) 是 由 Saremi 等[1]于2017 年提出的一种元启发式仿生优化算法,具有较高的搜索效率和较快的收敛速度,且算法本身特殊的...
  • Unity性能优化)-图形渲染优化

    千次阅读 2018-05-22 11:08:12
    Unity性能优化-图形渲染优化 这里有一篇写得很好的文章,请看这: 【Unity技巧】Unity中的优化技术 Unity性能优化-图形渲染优化 渲染流程简介 渲染问题的类型 处理CPU Bound 使用图形作业(Graphics Jobs...
  • 但是,有一种特殊情况,那就是如果经过逃逸分析后发现,一对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收。这也是最常见的堆外存储技术。 如何将堆上的...
  • 算法原理1.1 蜂群的初始化1.2 雇佣蜂阶段1.3 跟随蜂阶段1.4 探索蜂阶段2.算法流程3.算法结果4.参考文献5.MATLAB代码 摘要:人工蜂群算法(artificial bee colony,ABC)是由土耳其学者Karaboga 于 2005 年提出,它是...
  • 我们是如何优化英雄联盟的代码

    千次阅读 2017-05-08 11:44:42
    优化已有的代码库,我们采用了三个基本步骤:鉴别、理解和迭代。 步骤一鉴别 在开始之前,我们首先需要确认哪些代码需要进行优化。即使有些代码看起来明显性能较差,但是由于其对整体性能影响极小,优化...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 218,088
精华内容 87,235
关键字:

代码优化的三个阶段