首页 理论教育 软件性能优化指南

软件性能优化指南

时间:2023-06-09 理论教育 版权反馈
【摘要】:特别是在某些情况下,性能优化显得格外重要。因此,性能优化是保证程序安全的一个重要方面。可以对嵌套可能的值依照发生的可能性进行排序,把最有可能的放在第一位,这样可以提高效率。因此,Exception会降低系统性能。

软件性能优化指南

安全编程技术,其本质是要编写安全的程序。但是,由于安全是一个广泛的概念,除了在功能上需要能够不出现隐患外,在性能上也需要能够阻止隐患出现的可能。特别是在某些情况下,性能优化显得格外重要。如果程序性能不好,也可能导致某些方面的安全问题。因此,性能优化是保证程序安全的一个重要方面。

1.数据优化

(1)优化变量赋值

一般来说,由于局部变量用完之后释放,因此有些作用范围较大的变量操作,可改为局部变量来实现,有助于节省宝贵的系统资源。代码如下:

上述代码是求1~1000的和,变量sum作为类成员变量,在循环中对其进行反复读取,由于对局部变量进行读取,消耗资源较少,因此,可以将这个读取过程交给局部变量去做,变更代码如下:

(2)优化字符串

由于字符串的特殊性和灵活性,字符串的优化应用较广。首先,由于字符串的池机制,字符串的初始化(分配内存过程)可以优化。代码如下:

String str=new String("Apple");

上述代码中,系统实例化一个新的对象str,为其分配内存空间。但是由于字符串使用了池机制,可以将上面的代码优化如下:

String str="Apple";

此代码中,系统首先检查池中有无“Apple”,如果有,系统将直接使用池中的字符串,而不用重分配内存空间。

值得一提的是,在对多个字符串进行操作或对一个字符串进行修改时,用StringBuffer比用String要好。代码如下:

str3将保存strl和str2连接在一起的结果,系统将为str3额外分配内存。为了避免这个额外的资源消耗,代码可以优化如下:

这样,就不需要为两个字符串连接的结果额外分配内存。

(3)选择合适的数据结构

在实际的开发过程中,选择一种合适的数据结构很重要。例如,有一堆随机存放的数据,如果经常在其中进行插入和删除操作,使用链表较好;如果要经常进行读取,并且数据个数固定,则使用数组较好。这里需要注意的是,在高级语言中,大部分语言虽然提供了同样功能的API,但是底层实现机制不同,操作性能大不相同,这不是从表面就可以看出来的。如Java语言中:

●ArrayList和LinkedList,提供了功能类似的API,如对元素的增添删除、修改、查询。但是前者采用数组方式存储数据,后者采用链表方式存储数据,在进行数据大量添加或删除时效果不一样。

●ArrayList和Vector,后者实现了线程同步,在没有线程要求时适合用前者,因为速度较快;多个线程访问同一个Vector时适合用后者,因为可以保证数据安全。

又如,在C语言中,数组与指针语句具有十分密切的关系,一般来说,指针的好处是比较灵活、简洁,而数组则比较直观,容易理解。与数组索引相比,指针一般能使代码速度更快,占用空间更少。另外,对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。这种情况下,使用多维数组时差异更明显。

下面的代码作用是相同的,但是效率不同。

2.算法优化

(1)优化基本运算

很多代码都可以进行优化,其中最常见的是对乘法和除法的优化。观察下面的代码:

此处如果使用移位来代替乘法运算,可以使性能提高。重写的代码如下:

此外,整数除法是整数运算中最慢的,所以应该尽可能避免。对于连除,有时可以由乘法代替。以下是不推荐使用的代码:

推荐使用的代码如下:

(2)优化流程

流程主要包括以下两类:选择和循环。

1)选择结构的优化。

在选择结构的语句中,可以利用一些手段提高运行性能,如充分将可能性大的分支写在前面、充分利用短路判断运算符等。如下代码是根据学生的分数判断其等级:

该程序中,根据学校以往的统计经验,如果学生不能通过的概率较大,那么elseif分支就可以调到前面去。

另外,短路运算符有时也可以提高性能。如if(条件1&&条件2),可以将不成立概率较大的条件放在前面;又如if(条件1||条件2),可以将成立概率较大的条件放在前面。

在用if判断某些值是否相等时,尽量将变量作为比较的对象。如if(a==3)改成if(3==a)更好,这是为了消除程序员将“==”写成“=”造成的安全隐患。

在选择流程的嵌套上,代码会按照顺序进行比较,匹配时就跳转到满足条件的语句上执行。可以对嵌套可能的值依照发生的可能性进行排序,把最有可能的放在第一位,这样可以提高效率。

综上所述,当if-else语句中的分支很多时,为了减少比较的次数,明智的做法是把多分支if-else语句转为嵌套if-else语句。把发生频率高的情况放在一个if语句中,并且是嵌套if-else语句的最外层,发生频率相对低的情况放在另一个if语句中。

2)循环结构的优化。

循环的特点是可能反复执行一些代码,因此,在循环中,有很多可以优化的地方。优化得好,可以大大提高系统性能。代码如下:

该代码中,循环内i<v.size()语句;会反复执行,系统会重复计算v的大小。因此,这段代码可以优化。优化方法是可以让系统只调用v.size()一次。代码如下:

3.应用优化

(1)优化异常处理

异常处理给开发程序带来较大的方便,但是因为一个异常抛出首先需要创建一个新的对象,所以异常处理需要消耗底层资源。因此,Exception会降低系统性能。在异常操作的过程中,有时候可以对其进行优化,代码如下:

该代码相当于抛出NullPointerException时处理非正常操作。但是,该代码也可写成如下形式:

比较上述两段代码,从可读性和安全性上来讲,第一段代码比较好;但是从性能上,却是第二段代码比较好。因此,在实际开发的过程中,需要仔细权衡。一般来说,异常在需要抛出的地方抛出,try-catch能整合就整合。注意,不到万不得已,不要在循环中使用异常捕捉块。代码如下:

应该改为:

(2)线程同步中的优化

由于线程的同步可能造成系统性能的降低,因此,关于线程同步的操作,要注意以下几个方面:

●能够不用同步的地方就不要用同步,在程序中避免使用过多的同步。如果将不必要同步的代码块同步,同步的安全性优势不但没有体现出来,反而会造成程序性能的下降。因此,如果程序是单线程或者在多线程中不需要同步代码段,就一定不要使用同步代码块。

●同步的范围尽量小一些。很明显,如果同步代码范围大,则在较大的范围内只能被一个线程独占,性能降低的程度较大,因此,同步的范围应该尽量小一些。一般情况下,如果可以对某个方法或函数进行同步,就尽量不要对整个代码段进行同步。

4.数据库的优化

(1)设计上的优化

在数据库设计上,应适当采用相应措施,可以大大提高访问效率。

1)尽量给表设置主键与外键。

很多数据库允许数据表不设置主键,即使表中实体有主键和外键关系,也允许不设置外键。但是,从查询性能优化上讲,一个实体不能既无主键又无外键,因为很多与索引有关的操作都要基于主键与外键来进行。实际上,主键是实体的高度抽象,外键表达了实体之间的某种对应关系。主键与外键的配对表示了实体之间的连接。

2)适当降低范式标准,以空间换取时间。

一般来说,表及其字段之间的关系,应尽可能满足第三范式。但是,为了提高数据库的运行效率,有时可以降低范式标准,适当增加一些冗余,提高查询性能,以空间换时间。

3)将多对多关系分解成一对多关系。

在数据库设计的过程中,一对多情况下的设计比较容易,多对多情况下的设计相对复杂一些。若实体之间存在多对多的关系,就可以将其转化为若干个一对多关系,简化设计。

以最简单的两个实体之间的多对多关系为例,可以在两者之间增加第三个实体:关系实体。原来的两个实体都和这个关系实体发生联系。换句话说,原来多对多的关系转变为两个一对多的关系。(www.xing528.com)

4)科学地进行主键取值。

在数据库中,主键唯一确定一条记录,这也是表间连接和索引建立的依据。主键可以由如下方法赋值:

●某个唯一确定记录的列,如学生表中的学号。

●好几个列的组合,如选课表中的课程编号和学生学号的组合。

●一个无物理意义的数字串,当增加一个行时,程序自动给一个新串。

一般来说,建议采用第3种做法。很明显,第3种做法花费的空间较少,在生成索引时,占用空间小,查询速度快。不过,如果一定要用字段组合或者使用单独字段作为主键,字段个数最好不要太多,或者选用宽度较小的字段作为主键。

5)适当利用视图来保证数据安全性。

视图是一种虚表,本身并不存储数据,依赖于实际的表而存在,是实际表的一种映像。可以通过以下手段来设计视图:

●不将数据表中的保密数据显示在视图中,而将非保密数据在视图中公布,源表不对用户开放,只开放视图。

●在有必要的情况下,可以设置多层视图。特别是在一些权限系统中,如果用户可以查询同一个表中的列,权限越大的用户可以查询的列数越多,这种情况下,就可以首先基于源表创建第一层视图,公布的列数较多,面向权限最大的用户;然后在第一层视图的基础上建立第二层视图,公布的列数少一些,面向权限相对较小的用户。

6)适当使用“列变行”技术,减少不必要的数据冗余,提高性能。

实际上,一个表的列个数越少越好。将列数变少,是减少不必要的数据冗余的重要手段。

(2)SQL语句优化

从编程的角度讲,对数据库的访问主要是对数据库数据的操作,如添加、删除、修改和查询等。由于添加、删除和修改操作,主要还是要基于查询,因此,数据库访问上的优化主要指查询上的优化。一般来说,开发人员的查询工作多用SQL语句来实现。这里基于Oracle数据库,来介绍一些和SQL语句优化有关系的方案。

1)SELECT子句中尽量不使用“*”,而用列名替代。

在Oracle数据库中,解析SQL语句时,会将“*”转换成表中所有的列名,该工作意味着另一次查询,具有一定的时间损耗。

2)充分利用内部函数来提高SQL语句的效率。

很多SQL语句的功能可以用内部函数来实现,内部函数往往实现了优化,因此,如果能够使用的话,尽量使用内部函数。

3)在查询过程中,尽量使用表别名。

在SQL语句多表查询中,可以不给源表指定别名,但是推荐使用表的别名,并在每个列前加上别名,减少解析时间。当然,这种方法也能避免由于列名相同引起的歧义。

4)合理使用过滤操作子句。

在数据库中,过滤操作子句一般有如下几个。

●ON:用于连接过程中的过滤。

●WHERE:用于对检索出来的结果进行过滤。

●HAVING:用于在有聚合函数的情况下对检索结果进行过滤。

一般情况下,ON最先执行,WHERE次之,HAVING最后。这里给出的建议是,尽量一步步缩小过滤的范围,ON、WHERE和HAVING要有条理地分布在SQL语句中。不过,在某些情况下,如HAVING中有聚合函数进行计算时,WHERE子句的运行速度快于HAVING。因此,这种情况下,如果能够通过一些手段使用WHERE子句,就不要用HAVING子句。

另外,要尽量减小GROUP BY的范围,以提高GROUP BY语句的效率。如果在GROUP BY中需要过滤掉一些数据,尽量在GROUP BY之前用WHERE子句过滤,在GROUP BY之后用HAVING子句过滤。

从员工表中统计工资的代码如下:

SELECT姓名+AVG(工资)

FROM员工表

GROUP BY岗位

HAVING岗位='管理部门'OR岗位='行政部门'

上述语句将数据的过滤放在GROUP BY之后来做,可以改为:

SELECT姓名,AVG(工资)

FROM员工表

WHERE岗位='管理部门'OR岗位='行政部门'

GROUPBY岗位

5)SQL语句尽量大写。

在很多数据库中,系统遇到了小写的SQL语句,也会转换为大写,耗费资源。

6)适当利用关联子查询。

在关联子查询过程中,外层查询和内层查询一起进行,记录量减少较快,因此,在能够使用关联子查询的地方,应尽量使用。代码如下:

SELECT学号FROM学籍表WHERE年龄>20

AND班级号IN(SELECT班级号FROM班级表WHERE班主任='唐云')

因为要进行两次查询,即分别对学籍表和班级表进行全表查询,效率较低,所以可以改为:

SELECT学号FROM学籍表WHERE年龄>20ANDEXISTS

(SELECT*FROM班级号WHERE班级表.班级号=学籍表.班级号AND班主任='唐云')

7)利用索引提高效率。

索引可以用来提高数据的检索效率。通过索引查询数据比全表扫描要快。注意关于索引的使用,在有些数据库中,如果对索引进行了一些计算,索引将被使用,转而进行全表扫描,因此,要避免在索引上使用计算。

8)WHERE子句后条件顺序的考虑。

WHERE子句后可能有多个条件进行限制,此时可以先弄清楚条件执行的顺序,然后将可能筛选掉较多数据的条件先执行。

SELECT学号FROM学籍表

WHERE学费状态='未交'

AND性别='女'

如果未交学费的学生比例很小,那么尽量让“学费状态='未交'”这个条件先执行,如果数据库对两个WHERE条件的执行是从右到左的话,那么就可以改为:

SELECT学号FROM学籍表

AND性别='女'

WHERE学费状态='未交'

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈