程序性能范文

2024-07-25

程序性能范文(精选9篇)

程序性能 第1篇

软件的质量可以站在不同的角度来考虑:从用户的角度主要体现为软件的功能性、可靠性、易使用性等;从开发人员的角度主要体现为软件的可维护性、可移植性和可复用性。对于大型应用系统而言, 系统的性能显得极为重要。这里的性能主要指稳定性和效率。功能再强, 而性能很差, 频繁宕机, 系统便失去了可用性。因此性能设计是软件中必不可少的组成部分, 检验性能设计效果的过程需要测试或试运行。

设计程序性能首先必须了解性能目标, 例如数据的存储及并发访问量和网络状况等。如果系统需要在不同的环境下运行, 那么还必须注意环境之间的差异。分析用户的性能目标就是要明确可靠性、可用性、效率、可维护性、可移植性等不同的质量属性的重要级别, 按照重要程度从高到低排序, 优先解决那些重要程度较高的问题, 并运用各种技术管理手段进行实现。软件的质量性能指标, 目前国家标准和国际标准仅有框架性要求, 而未提出具体指标。因此软件性能的检验, 即测试过程, 还需依据行业长时间的技术沉淀确定的行业通用指标进行。

1 编程语言对程序性能的影响因素

提高软件质量性能的因素包括管理因素、技术因素和辅助开发工具。技术因素包括软件分析、设计、实现的技术方法, 如软件的编程实现语言。

编程语言对程序的影响首先可以反映在复杂数据结构的表达及数据处理上。以散列表为例, 某一马尔可夫链问题, 由前缀pref和多个由前缀决定的后缀suf组成的状态state集合, statetab[]用散列表表示, 如果用c语言定义此数据结构如下:

可见数据结构的定义是十分复杂的, 若想对散列表进行建立及查询操作等, 还需要自己定义繁琐的操作。如此复杂的操作会影响到程序的可靠性。

如果用Java来实现这个数据结构并进行数据的处理就容易多了。由于Java提供比c语言丰富的类库, 对于以上问题用容器类Vector和Hashtable类就可解决数据定义和处理的问题。容器类Vector提供一种动态增长的数组, 可以存储任何Object类型的东西;Hashtable类允许以任何类型的对象作为关键码, 存储或提取某种类型的值。散列表数组直接可以用Hashtable类进行定义Hashtable statetab=new Hashtable () ;, 前缀只需定义类Prefix, 并在其中说明为Vector容器类:public Vector pref;。对前缀取后缀的操作直接用Hashtable类提供的get方法和put方法实现。

如果用C++处理这个问题, 则可以用双端队列deque容器实现前缀, 用map容器存储关键码-值对的作用实现散列表, 而不必写任何处理的代码。有关问题的前缀和散列表的定义分别如下:

由以上几种语言的对比可以看出, 应用Java和C++提供的组件, 相关的代码可以很流畅地写出来, 提高了编程的效率。相比于c语言, 面向对象的Java和C++使用的类也提高了程序的可维护性。具体的可维护性效果还要根据类加权方法数, 继承树深度, 子类数, 对象类间耦合数、类的响应以及方法的内聚缺乏度等进行度量。

程序性能的另一个重要性能是程序的时间性能, 虽然Java和C++程序的编程效率高, 但执行的时间效能反而不如c语言。对程序时间性能的改善首先要找到程序可能的性能瓶颈, 这些瓶颈是性能设计的关键部位。应着重找出那些被频繁访问或运行复杂数据处理的部分, 改进这些部分设计可以达到事半功倍的效果。在unix类系统中, 可以将一个程序指定为time命令的参数, 以获取该程序执行完所需的用户时间、系统时间及实际时间。在windows系统中, taskmgr任务管理器命令可以列出进程的CPU时间。

程序的移植性问题很多是由编程语言的麻烦特征引起的。其中之一是对基本数据类型的定义。在c和C++里, 基本的数据类型的大小并没有明确的定义, 只规定char至少8位, short和in至少16位, long至少应该是32位, 并满足如下的规则:

标准里有意遗留下的定义, 通常是给写编译系统的人留下更大的自由度, 但程序在不同的编译系统运行就会产生不同的结果, 带来可移植性的问题。相比较任意一个JAVA程序, 不论它运行在何种CPU、操作系统或JAVA编译器上, 都将产生同样的结果。Java对所有的基本数据类型都明确的定义了大小, byte是8位, char和short是16位等。Java作为编程语言实现了源代码的可移植性。

程序的可靠性有些由程序与外界交换数据的接口问题造成, 如没有正确地检测函数的返回值, 比如因为malloc函数的缺陷, 会造成申请空间失败而程序没有进行相应处理的错误。因此使用emalloc函数代替是明智的。其他的问题如在求值顺序上, c和C++也没有Java定义的明确, 也会产生副作用。因此使用运算符的表达式都应该加上括号, 明确优先级。其他的有关可靠性的容错及并发与时序等问题, Java语言提供的异常处理机制及线程编程模式的排他性同步exclusion synchronization和使用wait和notify方法的条件同步condition synchronization对程序的可靠性也提供了很好的解决方法。

程序的安全性方面, 在检查c/C++代码时, 最常见的安全漏洞就是缓冲区溢出 (buffer overflow) 。对已经被利用的安全漏洞所做的分析表明, 多达50%的漏洞源于缓冲区溢出。缓冲区溢出产生的原因是粗心的代码在特定数据结构的范围之外进行写入操作。这种安全漏洞可以使程序运行时产生不可重现的错误, 这种非确定性的错误行为是最难对付的, 会给排错工作带来困惑。

不管使用何种语言, 不小心的话都会出现这种问题。在c C++程序中这个问题尤为严重, 因为当前大多数c/C++编译器与运行时环境的设计, 允许使用指针和数组下标对应用的数据做不受限制的访问, 并且还允许执行位于应用数据区与栈区的代码。因此在c/C++程序中, 一个指向数组范围之外的数组下标将会存取对应内存位置外的数据。同样的情况如果是Java程序的话, 会导致ArrayIndexOutOfBoundsException异常。Java对程序行为的定义比c严格, 它提供了内存自动回收功能 (Garbage Collection) , 使程序不能访问越界内存。

程序中的缓冲区溢出可被攻击者利用造成安全性问题。在执行该程序的计算机上执行任意代码的方法有很多种。最简单的办法是直接将一个位于栈上的 (函数局部的) 缓冲区末端的函数返回地址覆盖, 让它指向该缓冲区中特别编制的代码, 其他的攻击方法包括利用存储在堆中或静态存储中的缓冲区。

另一个程序安全性漏洞是由问题API引起的。很多函数使用调用者提供的缓冲区来返回变长的结果, 但是他们的接口没有提供制定缓冲区长度的手段。如c库的字符串函数strcpy、strcat;I/O函数gets、sprintf、scanf、fscanf。此外还有一些操作系统的特定函数, 如Unix函数getwd以及Windows函数OemToChar等。使用这些函数要格外小心, 所以应使用更加安全的替代函数, 如对gets改用fgets (buf, sizeof (buf) , stdin) , 对不加限制的scan ("%s", buf) 改用带有长度限制的scanf ("%20s", buf) 。或者是保证传递给函数的缓冲区足够大。

2 结束语

以上就编程语言对程序的性能影响做了一下分析, 程序的性能需要系统开发团队的所有人的关注:开发者个人的性能质量控制;项目负责人的质量管理和以专业性能测试人员通过不同的测试进行的软件质量检验。对于开发信息系统, 提高程序的性能, 除了对编程语言的研究, 还需要进一步了解所使用平台或者中间件的特性。

摘要:软件质量的重要性是不言而喻的, 软件的功能性、可靠性、可用性、效率、可维护性、可移植性等质量属性从各个方面反映了软件的质量。就编程语言对程序性能的影响因素, 从内存管理及语言的麻烦特性对程序可靠性、移植性和安全性的影响进行了分析。

关键词:软件性能,编程语言,可靠性,移植性,安全性

参考文献

[1][希腊]Diomidis Spinellis.高质量程序设计艺术 (第1版) [M].韩东海, 译.北京:人民邮电出版社, 2008.

[2][美]Brian W.kernighan.程序设计实践 (第1版) [M].裘宗燕, 译.北京:机械工业出版社, 2005.

[3]李宽.软件测试VS质量管理[J].金融电子化, 2007 (10) .

[4]崔晓东.软件的性能设计[J].信息技术与信息化, 2008 (1) .

[5]赵丽莉.软件性能测试面面观[J].软件工程师, 2006 (11) .

程序性能 第2篇

本文Shell程序运行环境:

◆程序运行环境Redhat Linux As3

◆GNU bash, version 2.05b.0(1)-release (i386-redhat-linux-gnu)

◆代码清单:shellcode.txt

问题描述:有一个普通的通话话单文件(包括“计费号码”,“主叫号码”,“被叫号码”,“开始时间”,“结束时间”,“时长”,“费用”等其它字段),要求根据另外一个号段配置文件(由“号段下限”和“号段上限”两个字段组成)将此话单文件进行分拣过虑,

分拣规则:如果通话话单文件中的“计费号码”位于号段文件的某个号段内,则将此条记录计入结果文件1,否则计入结果文件2。

通话话单文件样例:

901333|9013320003|9918128025|0814163420|20060814163450|30|20|00|01|005

9926645208|9926645208|9918188065|20060814163415|20060814163545|90|30|00|01|005

9934877207|9934877207|993697|20060814163620|20060814163930|190|50|00|01|005

......

......

号段配置文件样例:

9013305000,9013327999

9013767000,9013768999

9923670000,9923679999

9928998000,9928999999

9932310000,993239

9932333400,9932333599

9936034000,9936036999

9936084000,9936084999

9998537000,9998537999

9998620000,9998629999

9998690000,9998699999

例如:

对于通话话单文件的第一条记录中的“计费号码”为9013320000,此号码正好属于号段配置文件的第一个号段9013305000,9013327999中,即:条件9013305000<= 9013320000 <=9013327999成立,所以应该将通话话单文件的第一条记录计入结果文件1中;对于通话话单文件中的第二条记录的“计费号码”为9926645208它不属于号段文件中的任何一个段,所以应该将通话话单的第二条记录计入结果文件2中。

对于这样一个简单的问题首先想到的解决方法为:

解决方法1:

写一个双重循环,外层循环为逐条读取“通话话单文件”并获取每条记录的第一个字段的值“计费号码”,内层循环:根据外层循环获得的“计费号码”在“号段文件”中循环比较,判断此号码是否属于相应号段。

程序代码如下(省略了文件存在性判断等语句):

while read f

do

rg=“$(expr substr ${f} 1 10)”#取得“计费号码”存入变量org中

while read numseg

do

nglow=“$(expr substr ${numseg} 1 10 )”#将号段下限存入变量nglow

ngtop=“$(expr substr ${numseg} 12 10 )”#将号段上限存入变量ngtop

if [ “$org” > “$nglow”-a “$org” < $ngtop ]

#判断“计费号码”是否在此号段内

then

echo “${f}” >>./resultfile1.cdr #如果在此号段内,将此记录计入结果文件1中

else

echo “${f}” >>./resultfile2.cdr #如果不在此号段内,将此记录计入结果文件2中

fi

done < ./numseg.txt

done < ./rttest.txt

解决方法1对于号段文件和通话话单的记录数都比较少的情况下基本可以完成工作,但是当两个文件的记录数较多(例如号段文件>50条,话单文件>10000条)的时候,这种方法就会花费几个小时甚至几天的时间才能得出处理结果。此脚本程序执行慢的原因是对第二个循环内的比较运算只用了最简单的顺序比较方法,所以当号段文件的记录增多的时候,脚本的执行速度会急剧下降。

解决方法2:

将内层循环的逐个比较的方法改为二分查找法进行判断,程序代码如下:

#!/bin/bash

#Author Xshdate:08-15-2006

#程序中使用了二分查找法进行号码的范围判断

#为突出重点,省略了文件存在性判断和异常捕获以及帮助提示等程序语句

#程序的工作目录为当前目录

echo “Time:$(date)==>Strat to processing.........” #显示程序开始运行时间

while read numseg

do

tmplow=“${tmplow} $(expr substr ${numseg} 1 10 & >/dev/null ) ”

tmptop=“${tmptop} $(expr substr ${numseg} 12 10 & >/dev/null ) ”

done < ./numseg.txt

#读取号段文件,下限号段存入变量tmplow,上限号段存入变量tmptop

arr_lownug=(${tmplow}) #将下限号段存入数组arr_lownug

arr_topnug=(${tmptop})#将上限号段存入数组arr_topnug

#定义函数checknum,输入参数为需要检查的“计费号码”,输出参数为0或者1

#若checknum()输出为0 表示“计费号码” 不在号段文件的所有号段范围内

#若checknum()输出为1 表示“计费号码” 在号段文件的所有号段范围内

# checknum()函数中用二分搜索法进行号码的判断

checknum(){

thisnum=$1

ckresult=0

lowflag=0

topflag=$(expr ${#arr_lownug[*]} - 1 )#标注1

MaxIndex=$(expr ${#arr_topnug[*]} - 1 ) #标注2

midflag=0

midflag=$(expr ${topflag} / 2 )#标注3

if [ “${thisnum}” < “${arr_lownug[0]}” -o “${thisnum}” >

“${arr_topnug[${MaxIndex}]}”]

then

return 0

else

while [ “$lowflag” != “$midflag” ]

do

if[ “$thisnum” > “${arr_lownug[${midflag}]}” -o “$thisnum” ==

“${arr_lownug[${midflag}]}” ]

then

lowflag=${midflag}

midflag=$(expr `expr ${topflag} + ${lowflag}` / 2 ) #标注4

elif[“$thisnum”<“${arr_lownug[${midflag}]}” -o “$thisnum” ==

“${arr_lownug[${midflag}]}” ]

then

topflag=${midflag}

midflag=$(expr `expr ${topflag} + ${lowflag}` / 2 ) #标注5

else

echo “Error!”

fi

done

if [ “$thisnum” < “${arr_topnug[${lowflag}]}” -o “$thisnum” ==

“${arr_topnug[${lowflag}]}” ]

then

return 1

else

return 0

fi

fi

}#函数定义完毕

while read f

do

rg=“$(expr substr ${f} 1 10)” #标注6

checknum ${org}

returnval=$?

if [ “$returnval” == “1”]

then

echo “${f}” >>./Match_result.cdr#将匹配的记录存入结果文件1

else

echo “${f}” >>./NoMatch_result.cdr #将不匹配的记录存入结果文件2

fi

done < ./rttest.txt

echo “Time:$(date) ==>Proclearcase/” target=“_blank” >ccess is end! “

exit 0;

共2页: 1 [2] 下一页

编写高性能程序技术探讨 第3篇

1 时间考量

谈到程序性能,首先想到的是时间问题。通俗地说,一个程序处理任务花费的时间越少,就说这个程序的性能越高。程序运行耗费的时间都花在了什么地方呢?一般来说,程序运行耗费的时间分为3类:CPU时间、I/O时间和等待时间。

(1)CPU时间。CPU时间是指处理器为执行某个进程而花费的时间。某些程序需要执行大量的分析和复杂的计算,这就需要大量的CPU时间。需要注意的是,大量地占用CPU时间会使系统中所有进程都慢下来。CPU时间可以进一步划分为系统时间和用户时间。系统时间是指进程陷入内核态执行所花费的CPU时间,用户时间是指进程在用户态执行时所花费的CPU时间。

(2)I/O时间。I/O时间是指进程在处理输入输出数据方面所花费的时间。I/O时间是许多程序中最慢的部分,为了提高I/O性能,现代的操作系统大多采用了缓存和异步更新技术,将I/O操作推迟到系统比较空闲的时候执行。

(3)等待时间。等待时间是指进程为等待某个事件发生而花费的时间,比如等待输入的到达、中断的产生等。在等待过程中,CPU可以处理其他事务。

2 编程技巧及原理

下面从CPU时间、I/O时间、等待时间和编译优化几个方面讨论提高程序性能的技巧及原理。

2.1 节省CPU时间

2.1.1 查表法

要提高程序性能,首先要考虑的是降低算法复杂度,其次才是代码优化。代码写得再好,如果采用的算法不好,程序性能也不会高。某些情况下,通过使用查表法,可以有效降低算法复杂度。例如:给定n,要求计算y=2n,(其中0<=n<=10),试比较下面两种实现方式。

方式一:计算方式

方式二:查表方式

显然,查表方式比计算方式要快得多,特别是当上述代码位于循环体之内的时候,效果会更加明显。如果表格很大,计算很复杂,可以在程序里写一个初始化函数,对表格进行一次性计算。查表方式往往因为表格比较大,需要更多的存储空间,但这会换来较高的执行效率,这也就是我们常说的用空间换时间。

2.1.2 循环处理

循环是经常导致性能问题的地方。因为代码可能会在循环内执行很多次,一个小的问题在循环内会被扩大化,所以最简单有效的办法,就是尽可能地减少循环内代码的复杂度和数量,用更快捷的指令实现循环体的功能,把不需要每次都执行的代码搬到循环体外。

2.1.3 预计算

有些程序需要进行大量的计算,这时可以通过避免重复计算来提高性能。例如:用软件实现DES加密算法,一般分两步:(1)圈密钥计算。(2)用圈密钥加解密数据。

如果在应用过程中密钥不需要改变,就可以先进行圈密钥计算,并保存圈密钥,以后每次加解密只需要做第二步即可。

2.1.4 用廉价操作代替高价操作

高价操作与廉价操作是相对而言的,高价操作意味着需要更多的时间。在编程时如果能合理地用廉价操作代替高价操作,就能提高程序的性能。

(1)用整数运算代替浮点运算

用整数运算代替浮点运算的思路是这样的:先将整数放大10的N次方倍,再进行运算,计算完成后,再缩小10的N次方倍,换算出结果。例如A*3.14,可以改写成A*314/100。

(2)用位操作代替乘除法

除乘法指令的执行周期数远大于位操作指令的执行周期数。因此,如果能用位操作代替乘除法,就能达到优化的目的。

1)用移位代替乘法,如A*256,可改写为A<<8。

2)用移位代替除法,如A/256,可改写为A>>8。

3)用与操作代替取模,如A%256,可改写为A&0xFF。

(3)用乘法代替除法

除法指令的执行周期约是乘法指令的2~3倍,所以编程时应尽量用乘法代替除法。如A/B/C,可改写为A/(B*C),减少了一次除法运算。

2.1.5 减少内存拷贝

虽然CPU对内存的访问速度很快,但是如果程序中存在大量的数据拷贝,也会影响程序的性能。为了避免数据拷贝,需要定义合适的数据结构,并尽量使用地址方式传递数据块。例如:一个函数要向另一个函数传递一个比较大的结构体,如果采用值传递的方式,会造成数据拷贝,如果采用地址传递方式,只需要传递一个地址指针即可。

2.1.6 减少系统调用

很多系统函数,如open、fork、ioctl、lock等,会陷入内核态执行,需要进行进程上下文切换。进程上下文切换会占用相当多的CPU时间,从而影响性能,所以应减少对这类函数的使用频率。需要注意的是,不是所有的系统函数都会陷入内核态执行,如memcpy等字符串函数只在用户态执行。

在开发驱动程序时,为了避免反复进行系统调用,可以将驱动程序的某一功能封装在内核态一次完成,这样,一次系统调用就能完成一个功能,而不要通过多次系统调用来完成同一功能。另外,加锁函数也是较常用的,编程时尽量采用无锁算法或者粗粒度的锁来减少此类函数调用。

2.1.7 内嵌汇编

程序中对时间要求苛刻的部分如果用内嵌汇编来实现,可以显著提高运行速度。但是笔者认为,除非万不得已,不要使用汇编。这不仅因为开发和测试汇编代码是一件辛苦的事情,费时费力,更主要的是这种代码与CPU架构相关,缺乏可植性,另外,汇编代码的可理解性很差,很难维护。

2.2 I/O操作处理

对于某些程序而言,与外设交换数据可能是最花费时间的部分,也是最容易出现性能瓶颈的地方,需要妥善地进行处理。

2.2.1 使用DMA

DMA是直接内存存取的缩写,如果硬件支持的话,应尽量使用DMA。因为使用DMA方式传送数据,在传输过程中不需要CPU的干涉,CPU可以干其他的活,因此可以提高性能。

2.2.2 存储器镜像

存储器镜像就是在高速存储器中建立低速存储器的拷贝,把对低速存储器的读操作变成对高速存储器的读操作,但执行写操作时,对两者都要写入。

CPU对各种存储器的访问速度各不相同。例如PC机,对存储器的访问速度排序如下:CPU缓存CACHE>内存RAM>硬盘>光盘。CPU对这些存储器的访问速度往往存在数量级的差别,因此如果使用存储器镜像,把对低速存储器的访问换成对高速存储器的访问,就可以大幅提高程序性能。

在编程时,对于某些保存在低速存储器中的数据,如果需要经常访问,最好将其转存到高速存储器中。例如保存在磁盘数据库中的表格,如果需要频繁访问,就可以先将其读入内存,以后直接从内存中读取就可以了。

2.2.3 使用I/O缓存

I/O操作很费时,为了提高性能,应降低I/O操作的频率。在C语言中,有两套读写函数,一套是write/read函数,一套是fwrite/fread函数。write/read函数使用的是直接I/O,每次调用都会陷入内核态执行,操作系统会尽快地进行数据读写;fwrite/fread函数使用了缓存技术,只有当缓冲区填满时才会真正地进行数据读写,这有效地减少了I/O操作的次数,有助于性能的提高。

2.3 等待时间处理

为了等待某个事件发生或是某种条件满足,程序往往不得不进行长时间的等待。为了提高性能,在等待期间,最好不要使用CPU,让CPU去处理其他事务。

(1)用中断代替轮询。中断就是当某事件发生了,才通知CPU去处理,否则不会去打扰CPU。而轮询需要CPU定期去查询某事件是否发生,会占用大量的CPU时间。

(2)用阻塞代替非阻塞。当等待某条件满足时,阻塞类似于中断,非阻塞类似于轮询。

(3)让出处理器。如果不得不使用轮询方式进行等待,在等待期间可以使用休眠或者主动让出处理器的方法,减少CPU占用率。C语言中有usleep、sched_yield等函数可以实现该功能。

2.4 编译优化

现代的编译器都具有代码优化功能,能够大大优化所生成的代码。程序员可以选择优化代码的执行速度,也可以选择优化代码的大小,但两者一般不能同时选择。虽然编译器具有自动优化功能,但毕竟功能有限,如果程序员在编程时做一些事情,会帮助编译器更好地进行优化。常用的方法有以下几种:

(1)使用内嵌函数。将经常调用的小函数用inline声明为内嵌函数,这样编译器在编译时会将该函数的实际代码插入到调用函数的地方,而不是插入一条跳转到函数入口地址的语句。这样做有两个好处,一是程序的控制流不会被打断,从而可以让CPU的流水线更加顺畅地执行指令;另外,利用内嵌函数时,因为减少了堆栈操作、函数调用和返回值处理的指令,因此执行的指令也就减少了。

(2)使用const关键字。对保持不变的变量使用const关键字,不仅可以防止对该变量的错误赋值,还可以让编译器对与该变量相关的代码进行优化。编程时,如果函数的输入参数在函数调用过程中不会改变,请使用const关键字;对于一些只包含常量的数组,也可以使用const。

(3)使用register关键字。当一个变量被频繁使用时,需要反复访问内存,从而花费大量的存取时间。如果将这种变量放入CPU寄存器中,使用时,不需要访问内存,而是直接从寄存器中读写,就可以提高效率。用register定义的变量,编译器会尽量将该变量放入寄存器中来进行优化。需要注意的是,CPU寄存器数量有限,如果没有足够的寄存器来存放register变量,编译器仍然能够将register变量放入高速存储器中,例如cache。实际编程中,可以对循环计数器变量使用register关键字。

(4)switch条件排序。编译器对switch语句的转化有多种形式,其中最常见的是转化成比较链和跳转表。当switch被转化成比较链时,编译器会产生if-else-if的嵌套代码,并按照顺序进行比较,匹配时就跳转到满足条件的语句执行。如果依照条件发生的概率进行排序,把最有可能发生的条件放在第一位,就可以提高性能。

3 定位性能瓶颈

对所有代码都进行优化通常是没有必要的,在适当的地方进行合理的优化,往往可以达到事半功倍的效果。作为程序员,应该清楚软件运行时执行了哪些操作,哪些操作占用了较多的时间。只有清楚了这些,才能找到性能“瓶颈”,对症下药,对程序进行有目的的优化。

现在有很多代码分析工具,可以用来帮助程序员分析代码的运行速度,统计每个函数调用所花费的时间,并生成性能报告。运用这些工具,可以方便地找到程序的性能瓶颈,从而进行有目的优化。例如在Linux下可以使用gprof工具,分析C程序中每个函数调用所花费的时间,从而找到最花费时间的代码,进行优化。gprof的使用过程如下所示:

(1)用gcc编译代码时加入选项-g-pg选项,生成可执行文件sms4。

(2)运行sms4,在执行结束时会生成一个名为gmon.ou的文件。

(3)使用gprof分析gmon.out,生成报告文件profile。

gprof sms4 gmon.out>profile

(4)查看报告文件profile,找到性能瓶颈。

下面是profile文件的部分内容,统计出了每个函数的调用次数和所花费的时间。

对程序进行性能优化的原则是:优先优化存在性能瓶颈的代码,达到性能要求后即可考虑停止优化。程序性能优化的基本思想是:(1)降低算法复杂度,使用更高效快捷的指令,让CPU在尽可能短的时间内完成任务。(2)提高I/O操作的效率,减少I/O操作的频率。(3)程序等待期间尽量少用CPU,让CPU能够更多地处理其他事务。(4)熟悉编译器的特性,编写更有利于优化的代码。

摘要:为提高程序的性能,提出了一些行之有效的编程方法和技巧,并分析了其原理。这对于从事软件开发,特别是底层软件开发的程序员,具有很好的借鉴意义。

关键词:CPU时间,IO时间,等待时间,编译优化,瓶颈

参考文献

[1][美]John Goerzen.Linux编程宝典.电子工业出版社,2000.

[2][美]Randal E.Bryant.深入理解计算机系统.中国电力出版社,2004.

程序性能 第4篇

地理空间信息网格高性能调度技术中应用程序调度模型的研究

地理空间信息网格调度技术,要比传统的高性能计算中的.调度技术复杂,原因是如果将全部网格资源作为一个应用程序的调度和执行目标,必将导致通信延迟、成本昂贵、执行低效等.为此,综合考虑应用程序特性、机器特性等,研究设计了地理空间信息网格高性能调度技术中的应用程序调度模型,包括地理空间信息网格应用程序分析;资源特性分析;应用程序分解;性能预测;资源调度;机器选择;任务映射和任务调度;任务调度器和调度器管理等九个模块.以实现为不同的应用程序匹配不同的计算资源,提高计算资源的利用率和应用程序的执行效率.

作 者:龚强 GONG Qiang 作者单位:中国地震局工程力学研究所,黑龙江省信息产业厅,哈尔滨,150090刊 名:测绘科学 ISTIC PKU英文刊名:SCIENCE OF SURVEYING AND MAPPING年,卷(期):32(2)分类号:P208关键词:地理空间信息网格 高性能调度技术 应用程序调度模型

Web应用程序性能测试方法研究 第5篇

1 Web应用程序性能测试概述

1.1 性能测试

性能测试是指为了获取软件处理事务的速度及确定系统性能的瓶颈而进行的测试[1]。Web应用程序性能测试主要集中在三个方面,即应用于客户端性能的测试,应用在网络上的性能测试和应用在服务器上的性能测试[2]。应用在客户端性能的测试包括并发性能测试、负载/压力测试、大数据量测试、速度测试等;应用在网络上的性能测试主要是对网络环境的测试,包括网络仿真、网络故障分析、性能优化、网络应用性能监控和网络预测等;应用在服务器上的性能测试是对服务器上应用程序的性能进行测试,资源的占用情况、数据库性能、应用自身的性能、故障报警及排除。

1.2 压力测试

压力测试是一种需要反常数量、频率或资源的方式下,执行可重复的负载测试,以检查程序对异常情况的抵抗能力,找出性能瓶颈[3]。异常情况主要指的是峰值、大量数据的处理能力,长时间运行等情况。压力测试总是迫使系统在异常的资源配置下运行。

1.3 压力测试和性能测试的关系

压力测试的重点在于发现功能测试所不易发现的系统方面的缺陷;而性能测试着力于提供性能方面的数据,以供程序开发者参考。虽然两者侧重点不同,但是两者的测试手段和测试方法非常相似,常常是一起进行的。由于性能测试贯穿于整个测试过程,而压力测试的目的是找出性能瓶颈,所以可以认为压力测试是性能测试的一个阶段。

1.4 Web应用程序性能测试存在问题

性能测试是为了获取Web应用程序性能的瓶颈,其测试过程中也存在需要解决的瓶颈问题。如:真实环境与测试环境差异较大,测试环境难以模拟真实环境,难以模拟真实用户行,负载难以确定,测试效率低等。要解决这些问题,需要采用相应的测试方法加以解决。

2 Web应用程序性能测试方法

一般Web应用程序性能测试都是利用测试工具模仿大量的实际用户操作,系统在超负荷情形下运作。测试的基本策略是自动负载测试,通过在一台或几台PC机上模拟成百或上千的虚拟用户同时执行业务的情景,对应用程序进行测试,同时记录下每一事务处理的时间、中间件服务器峰值数据、数据库状态等。通过可重复的、真实的测试能够彻底地度量应用的可扩展性和性能,确定问题所在以及优化系统性能。

2.1 目标驱动性能测试方法

不同领域的Web应用存在不同的性能需求,即使是在同一领域的Web应用,其性能需求也存在差异。这些差异是由Web应用业务处理流程确定的。比如某一拥有大量业务的公用电子病历平台,其建立电子病历时的业务数量多,但分布往往比较均匀;而对于电子病历的汇总,月底和年末则非常集中。构造和测试这样的Web应用,就必须考虑这个Web应用在汇总业务时所能承受的负载,而不能以建立电子病历为准。所以,性能需求分析依赖于整个系统分析。

每一个性能测试计划中第一步都会制定目标和分析系统构成。只有明确目标和了解系统构成才能确定测试范围,并采用相应的测试方法和策略。不同的系统构成性能测试就会得到不同的结果。目标驱动的Web应用程序性能测试方法是根据不同应用对性能的不同要求制定相应的测试目标,为测试过程提供依据。

2.2 目标驱动性能测试方法目标分析

根据Web应用程序性能需求分析,就可以对性能测试目标进行确定。性能测试的目标包括:

1)总体目标:找出Web应用可能存在的性能瓶颈或软件缺陷,确认其是否达到用户使用需求[4]。

2)具体目标:可以细化为每一测试过程的性能测试指标,包括:

(1)Processor time服务器CPU占用率。

(2)Response delay应答延迟,一个请求从建立网络连接到应答结束拆除连接之间的时间。

(3)Hits per second每秒点击次数。

(4)Average response per second平均每秒钟响应次数。

(5)Database CPUprocessor time数据库服务器CPU占用率。

(6)Response time响应时间。

其中:Response time这一项指标是前五项性能测试指标的综合反映。

2.3 目标驱动性能测试方法测试流程

因为性能需求分析依赖于整个系统分析,所以首先根据系统分析获取性能需求,制定测试计划,确定性能测试目标,然后部署测试环境,设计测试用例和脚本进行测试。输出测试结果后,根据分析,首先看测试是否达到测试具体目标,如果没有达到,则使用新的测试用例和脚本,直到完成。最后,如果达到测试总目标,则表明已经找到Web应用程序性能瓶颈,可以提交测试报告,从而对Web应用程序进行修改或优化。其流程如图1所示。

3 结束语

Web应用程序应满足一定的性能需求,其性能测试不能依靠传统测试方法。Web应用程序目标驱动性能测试方法针对Web应用程序性能测试存在问题,根据应用程序性能需求,将测试目标进行分解,易于找出应用程序性能瓶颈。在《西部地区公用电子病历平台》项目的测试和优化过程中,Web应用程序性能测试与优化方法得到了应用,事实证明,该测试和优化方法使测试过程清晰、测试工作高效有序。

参考文献

[1]Avritzer A,Weyuker E J.The Role of Modeling in the Performance Testing of E-commerce Applications[J].IEEE Transactions on Soft-ware Engineering,2004,30(12):1072-1083.

[2]王会青,冯秀芳.Web应用软件测试方法的研究[J].太原理工大学学报,2007,38(4):304-306.

[3]叶新铭,冯晓利.软件压力测试流程[J].内蒙古大学学报:自然科学版,2002,33(1):107-110.

[4]赫建营,晏海华.一种有效的Web性能测试方法及其应用[J].计算机应用研究,2007,24(1):275-277.

[5]邓小鹏,邢春晓.Web应用测试技术进展[J].计算机研究与发展,2007,44(8):1273-1283.

[6]杨根兴,宗宇伟.软件测试不确定性研究及解决途径[J].计算机工程,2004,30(7):52-54.

[7]熊忠阳,李光勇.Web集群系统性能测试与优化[J].计算机应用研究,2008,25(3):826-828.

[8]邹建峰,李律松.ASP.NET开发技术详解[M].北京:人民邮电出版社,2005:267-271.

程序性能 第6篇

关键词:Web应用,性能测试,研究

近年, 随着Internet的迅猛发展, Web技术越来越多的应用到搜索引擎、远程教育、电子商务等领域。Web应用程序以其便利性、实用性等特点, 被许多单位所采用。但是, Web应用程序自身尚存的缺陷也给人们带了不小的损失, Web应用程序有必要经过彻底地测试, 这样才能确保其可靠性和稳定性, 因此Web应用程序性能测试技术的探讨十分必要, 本文对此进行了初步的探讨。

1 Web应用程序测试技术研究现状

We b以其广泛性等特点迅速风靡世界。目前, We b已经成为互联网上进行信息交互的标准平台, 并且由于Web应用程序能提供支持所有类型内容连接的信息发布, 易被用户获取, 使其具有广泛的应用价值。随着Web应用程序变得更加复杂, Web应用程序可靠性也显得尤为重要, 同时对它的测试也变得比较困难。现阶段从事Web测试研究的大学国外有美国George Mason等大学, 国内有清华大学、重庆大学等高校。

国外研究者Ricca和Tonella等提出了一种分析和测试Web应用程序的技术。该技术定义了一种分析模型, 分析了Web应用程序, 且可以捕获静态和动态的HTML页面。在这个模型基础上, 更进一步采用传统的数据流测试技术对导航的正确性以及HTML页面之间的数据流进行了必要的验证。国内目前涉及Web测试的研究较少。主要是根据Web应用的特点, 按应用端, 中间网络和服务端三个方面对性能测试进行探讨, 对性能测试的指标进行了总结, 对性能测试方面进行理论性的研究。在Web性能方面, 中国科学院研究者马琳等设计出一种推理预测算法, 能够对Web应用程序的性能进行有效的预测, 国内的We b应用尽管在近些年中取得了很大发展, 但不得不承认国内We b应用的水平与国外还有差距, 这在很大程度上影响着Web应用程序测试的研究。相信随着国内Web应用程序应用的发展, Web应用程序的测试技术会进一步发展。

2 Web应用程序性能测试的关键问题及分析

We b应用程序性能测试的关键问题是一个很重要的问题, 这包括配置、负载、服务器选择的硬件与软件、响应时间等, 这样直观的描述代表了Web应用程序性能测试需求, 也决定了测试目的。可以概括成以下几个方面:首先是检测Web应用程序系统性能, 评估系统性能以及服务等级的满足情况。这样一个看起来简单的过程, 当成百上千的终端同时执行这样的操作时情况就不一样了, 这对应用程序本身, 操作系统、中心数据库服务器、网络设备的承受力都是严峻考验。决策者需要模拟系统负载压力, 这是在Web应用程序性能测试阶段就应该解决的重要问题。

其次是预见Web应用程序的系统负载压力承受力, 在实际应用前评估系统性能。各类应用环境不同造成难以预知的用户负载和越来越复杂的应用程序, 检测系统性能强调对系统当前性能评估, 这种测试在于知道系统总体设计避免浪费和硬件和软件的设计不匹配, 使系统具有更长生命力。由于对系统性能检测是被动监控一些性能指标, 则不可避免地借助自动化的负载压力测试工具。

最后是介绍一下Web应用程序性能测试的盲点, 性能测试的盲点就是在Web应用程序性能测试中, 不进行系统功能校验, 每当功能发生错误时, 测试工具不能记录错误。目前Web技术的发展扩大了这些盲点, 所以性能测试期间必须进行功能内容校验, 如果没有正确的功能保证, 性能测试就失去了意义, 如何做功能内容校验, 一般认为在性能测试过程中记录所有虚拟用户的操作和服务器的响应, 有助于判断功能错误, 这就是当前性能测试技术发展的最大挑战。

3 Web应用程序性能测试的解决方案及方法

We b应用程序性能测试是一个很大的概念, 包括执行效率、资源占用、稳定性、安全性、可靠性等, 而负载压力测试是Web应用程序性能测试的重要方面。系统的负载压力主要包括并发负载性能测试、疲劳强度性能测试以及大数据量性能测试等。

并发性能测试的过程是一个负载测试和压力测试的过程, 即逐渐增加负载, 直到Web应用程序的瓶颈, 通过综合分析交易执行指标和资源监控指标来确定系统并发性能, 从而来确定能够接收的性能过程。并发性能测试的目的是以真实的业务为依据, 选择有代表性的、关键的业务操作设计Web应用程序测试案例, 通过模拟用户, 重复执行和运行测试, 可以确认Web应用程序性能瓶颈并优化和调整应用。

目前主要有3种Web应用程序性能测试方法, 虚拟用户方法、WUS方法和对象驱动方法。虚拟用户方法是通过模拟真实用户的行为来对被测程序施加负载, 以测量性能指标值, 如事务的响应时间等。用较少的硬件资源模拟成千上万虚拟用户同时访问AUT, 并模拟不同IP地址以及不同网络连接方式的请求, 同时可实时监控Web应用程序性能指标, 帮助测试人员分析测试结果。该方法比较直观, 适合电子商务应用程序的测试, 但确定负载的信息要靠人工收集, 准确性不高。

还有一个是WUS方法, 它包括每小时浏览的页面数/点击数以及页面请求分布等。同时也包括一些影响负载的客户端变量, 如用户对延迟的忍耐程度和客户端连接速度等。该方法的优点是Web应用程序性能测试负载来源于网站实际的运行数据, 能反映和代表实际情况。缺点是太依赖于日志文件, 不适用于测试新开发的Web应用程序。

最后是对象驱动方法, 基本思想是将AUT的行为分解成可测试的对象。一个Web页用对象来递归定义, Web应用程序性能测试的过程也就变为测试每个对象或某些对象的集合, 该方法使测试结构化程度高、结果清晰、适合页面组件类型较丰富、业务复杂的Web应用程序, 但过于强调局部组件的性能难以反映用户对性能的实际感受。

4 结语

近十几年来, 随着Web软件应用的迅速推广, 对软件测试也变得迫切需要, 各种针对性的测试方法和技术不断的出现, 而Web应用程序性能测试相对于软件测试有其自身的特殊性和难点, 本文分析并总结出Web应用程序性能测试的关键问题及解决方案。

参考文献

程序性能 第7篇

基于性能导航 (Performance Based Navigation, 简称“PBN”) 技术设计的飞行轨迹, 犹如在天空中铺设了一条铁轨, 能让飞机像火车一样在能见度极差的条件下安全、精确地着陆, 进而大大提高飞行的精确度和安全水平。然而, 我国PBN飞行程序设计的研究尚不够成熟, 程序设计工作仍局限于二维阶段, 其可视化程度较低。因此能够直观、清晰地表达航线、保护区与地形、地貌之间的位置关系的三维呈现方法将会受到广大飞行程序设计人员的青睐。

Google Earth是一款虚拟地球软件, 它把航拍照片、卫星图片和GIS数据整合在一起, 形成一个地球的三维模型, GoogleEarth提供免费和及时更新高精度卫星遥感影像和地形DEM数据, 作为三维呈现平台GoogleEarth的优势非常明显[1]。因此, 本文选择了Google Earth作为飞行程序设计的三维呈现平台。由于GoogleEarth并不是专业的绘图软件, 对复杂的线、面的编辑功能有限。而Auto CAD软件具有完善的绘图、编辑、三维建模以及强大的二次开发功能, 已成为大多数飞行程序设计人员的工具。应用Auto CAD软件的三维模块建立跑道、航线和保护区的三维模型, 然后利用Auto CAD内置的开发语言VBA在Auto CAD中实现将基于空间直角坐标系的Auto CAD图元转化为基于WGS84坐标系统KML文件, 从而可利用Google Earth直接打开KML文件, 以便结合地形、地貌进行观察, 以达到三维显示的效果。

2. 三维呈现的实现

在飞行程序设计过程中, 应用Auto CAD绘制的航线、保护区图均可在其三维建模工作空间中打开, 这时会发现Auto CAD使用的笛卡尔坐标系增加了Z轴, 将图元赋予高度参数后即可绘制出三维的航线、保护区。常用的绘图命令有点 (POINT) 、直线 (LINE) 、构造线 (XLINE) 、样条曲线 (SPLINE) 、三维多段线 (3DPOLY) 、三维面 (3DFACE) 等。然后应用Auto CAD中渲染功能将三维图元美化, 增强显示效果, 便于观察。

2.1 坐标转换的算法

目前, 我国飞行程序设计中主要应用Auto CAD软件绘制航线、保护区等, 其图中坐标系为空间直角坐标系, 而Google Earth支持WGS84坐标系统。由于WGS84坐标系统为大地坐标系, 因此在三维呈现的过程中需要将空间直角坐标向大地坐标的转换。WGS-84坐标系的地球椭球参数如表1所示。

WGS-84为地心坐标系, 其大地坐标与空间直角坐标的数学关系, 如图1所示。

图1中P点为测量点, P0为其在地球椭球上的投影点。由图1测量点P点空间直角坐标P (X, Y, Z) 与大地坐标P (B, L, Hn) 的几何关系通过简单矢量运算和三角函数运算即可得出空间直角坐标 (X, Y, Z) 转换到大地坐标 (B, L, H) 的公式:

2.2 坐标转换的实现

作为Auto CAD内置的开发语言, VBA是一种面向对象的可视化编程工具, 具有快速的开发环境, 其方便、快捷的窗体创建功能可开发出与Auto CAD风格完全一致的应用程序, 同时还可以弥补Auto CAD在其他方面如数据处理、数据库建设、界面设计等等的不足。因此, 本文应用VBA开发工具对Auto CAD进行二次开发。

根据公式 (1) ~ (3) 等, 利用VBA编程后, 生成一个.dvb的文件, 即下文所说的源程序。通过Auto CAD标准菜单中的<工具>-<宏>-<加载工程>选项, 加载源程序, 然后选择Auto CAD标准菜单中的<工具>-<宏>-<宏>选项, 显示宏对话框, 高亮之前加载源程序后单击运行按钮执行该代码[3]。即在Auto CAD中运行宏代码, 界面如图2所示。

将A u t o C A D中图元的基准点与用户坐标系统 (UCS) 的原点重合, 然后将所选图元的基准点的纬度、经度坐标分别输入到图2界面中相应的位置, 设置好其他参数, 单击确定按钮运行程序。

3. APV航段保护区三维呈现

本文以遵义机场18号跑道类精密进近航段为例计算气压垂直导航 (Baro-VNAV) 程序相关参数、绘制三维VSS面与APV-OAS面,

并将其在Google Earth中呈现, 来分析、验证以上方法的可行性。

3.1 目视保护面 (VSS)

APV程序假定不能有障碍物穿透VSS面, 在评估目视保护面时, 高于入口平面不足15m的障碍物可以忽略。遵义机场的机场代码为4, 因此, VSS面在跑道入口的宽度为300m;起点在跑道入口前60m;终点在VSS面的高度达到OCH处;左右两侧按15%扩张;坡度角为程序公布的角减1.12°。遵义机场18号跑道VSS面评估如图3。经评估, 没有障碍物穿透VSS面, 可以考虑建立APV Baro-VNAV程序;若有障碍物穿透VSS面则应对障碍物进行处理, 否则修正最低温度限制等参数, 重新评估障碍物是否穿透VSS面。从图3中可以看出将二维VSS面经过三维处理后更能清晰的表达其与障碍物、跑道之间的位置关系。

3.2 障碍物评价面 (OAS)

气压垂直导航 (Baro-VNAV) 是一种导航系统, 它利用气压高度表的数据计算出条从RDH开始延伸到FAF的垂直航径角, 该垂直航径角 (VPA) 标称下滑角度为3°[4]。为飞行员提供较为精确的垂直引导 (VNAV) , 与LNAV共同工作完成导航运行。使用Baro-VNAV的优势在于能够提供较为精确的下滑引导, 同时能够实施连续的下降进近, 有较高的安全性。

确定了垂直航径角 (VPA) 后可以建立类精密进近障碍物评价面 (APV-OAS) , APV-OAS的起点为最后进近点 (FAP) , 位于垂直航径与此前航段的最低规定高相交的位置。FAP通常在入口前且距入口处不得超过10NM, 当距离超过5NM时, FAS面需要考虑W面。APV-OAS的终点为复飞等待点 (MAHF) 和复飞转弯点 (MATF) 中的最早者。APV-OAS面由最后进近面 (FAS) 、水平面、中间和最后复飞面组成, 其中各航段的分界为FAF面的起点 (XFAS) 、Zi面的起点 (Xzi) 、Zf面的起点 (Xzf) 。FAS面、水平面、中间复飞面、最后复飞面以水平导航性能 (LNAV) 的主区边界为水平边界, 相应侧面的内边界为其对应的LNAV主区边界和对应处标高以上最低超障余度 (MOC) 处的LNAV副区外边界。其中, 水平面侧面靠下/靠内的边界为LNAV在入口高度的主区边界。相应侧面靠上/靠外的边界为LNAV在FAS起点高于入口75m的副区外边界和LNAV在至入口距离为XZi的中间复飞面起点高于入口30m的保护区边界[5]。

在绘制APV-OAS面前应先确定构成APV-OAS面的关键参数, 计算如下:

FAS面的角度由公式 (4) 计算,

FAS面起点由公式 (5) 计算,

中间复飞面起点由公式 (6) 计算,

最后复飞面起点由公式 (7) 计算,

式 (4) ~ (7) 中HFAP为FAP的高;H修为最低温度修正;MOC为最后进近的最低超障余度;RDH为基准高;ATT为沿航迹容差;d为飞行员反应时间对应的距离;X为过渡容差对应的距离;Tan Z为复飞面梯度。

将遵义机场18号跑道基础数据代入以上 (4) ~ (7) 公式即可计算出绘制APV-OAS面的关键参数, 然后绘制出三维APV-OAS面。Google Earth中呈现的三维APV-OAS面如图4所示。

4. 结语

本文结合了Google Earth的三维呈现的优势与Auto CAD强大的绘图、编辑、三维建模及二次开发功能来完成遵义机场类精密进近航段的三维呈现工作。虽然将Auto CAD中图元转换到Google Earth过程中有一定的误差, Google Earth作为一款虚拟的地球软件与真实地理环境比较也存在一些偏差, 但是经与遵义机场基于性能导航飞行程序设计结果进行对比、验证, 以上误差均为可接受范围, 从而达到三维呈现的目的。为飞行程序三维呈现提供新的思路与方法。

摘要:目前飞行程序设计的呈现方式仍停留在二维阶段, 表达空间信息不直观, 信息量小等缺点。为解决以上问题, 本文以遵义机场类精密进近航段为例, 选取Google Earth作为三维呈现的平台, 应用AutoCAD软件的三维模块绘制跑道、航线和保护区的三维模型, 然后用VBA开发工具对AutoCAD进行二次开发, 使得AutoCAD中图元能够在Google Earth中呈现, 以达到三维显示的效果, 为飞行程序设计三维呈现提供新的思路与方法。

关键词:三维呈现,基于性能导航 (PBN) ,气压垂直导航,目视保护面 (VSS) ,障碍物评价面 (OAS)

参考文献

[1]薛亚婷.基于GoogleEarth及KML的数字校园设计与实现方法研究[D].兰州:兰州大学, 2007.

[2]仝巧珍, 赵计环.谈大地坐标系和空间直角坐标系转换的研究, 山西建筑, 2011 (08) .

[3]AutoCAD VBA从入门到精通/ (美) 考订汉姆 (Cottingham.M.) 著:孔祥丰等译.-北京:电子工业出版社, 2001.

[4]朱代武, 何光勤.目视和仪表飞行程序设计.成都:西南交通大学出版社, 2004:162—164

程序性能 第8篇

在程序设计的发展历史中, 先后出现了许多的程序设计语言, 例如早期的机器语言、 汇编语言, 再到后来的FORTRAN、 COBOL、 BASIC、 C、 C++, 以及今天非常热门的Java、 C# 等等。 C和C++在所有程序设计语言中是非常突出的存在, 它们对程序设计的发展起到了极大的推动作用, 其中C++是在C语言上演化和发展而来的, 它继承了C语言的优势, 弥补了C语言的不足。 支持各种编程范式, 如可以进行面向过程编程、 泛型编程以及面向对象编程, 应用非常之广, 各种各样的软件开发、 系统开发以及引擎开发都可应用C++, 其编程性能非常灵活而强大, 支持类、 封装、 重载等特性。

在实际的程序设计中, 为了支持C++语言灵活的功能, 编译器会生成程序中没有显式出现的数据结构和代码。 程序设计人员是看不到这些数据结构和代码的。 只有经验丰富的软件工程师才能全面了解编译器自动生成代码的时机和条件, 所以C++程序的时间性能和空间性能非常难以把握。 在当前, 硬件技术的发展使得程序设计所受的空间性能限制减小, CPU运算速度越来越快, 内存空间越来越大, 成本却越来越低, 但在时间性能方面, 却还是需要在程序设计中加以注意的。

2 C++程序设计中的时间性能优化

2.1 减少对临时对象的使用

在C++程序设计中, 有一种对象属于是临时对象, 这种对象没有定义, 是直接由编译器在程序编译过程当中所生成的对象, 它经常在以下两种情况中出现: (1) 一个对象被作为参数被传递到函数时; (2) 由函数将对象作为结果返回时[1]。虽然C++支持各种编程范式, 但主要还是被作为一种面向对象的程序设计语言, 所以在C++程序设计中会频繁地使用到类, 在这些类中, 包含了若干的函数, 一些是构造函数还有一些则是析构函数。 在进行程序设计的时候, 我们经常都会使用到类, 获得类的实例, 这些实例在被用到上述的两种情况中时, 临时对象便会产生, 这就会涉及到对构造函数和析构函数的调用。

在上面的这一段代码中, 有一个类 “A”, 它虽然没有直接定义复制构造函数, 但编译器在进行程序编译的过程中, 会自动为 “A” 生成默认的复杂构造函数。 该函数将会对string类的复杂构造函数进行调用, 以复制变量name, 并调用Int的构造函数复制变量number。 可以看出, 虽然代码非常简单, 但其对于空间性能和时间性能是由一定要求的。 在A fun函数中, A类的一个实例对象t1 将被作为参数传入, 并且还会返回一个A类的实例对象t2, 按照临时对象产生的情况来看, 在这里就会分别产生两个临时对象, 如果A类的构造非常的复杂, 就会给程序的运行带来一定的压力, 抛去空间性能不谈, 时间性能必定会被消耗得更多, 这会使得程序的运行效率降低, 为此非常有必要对A fun函数进行优化。

在上面这个被优化后的fun函数中, 因为所使用的是常量, 没有了A类实例对象参数的传入与结果返回, 最终仅返回一个简单的整型数据作为应答, 提示执行成功或错误, 所以可以减少两次临时对象的产生, 这就在很大程度上提升了程序运行的时间性能。

2.2 重视对初始化列表的使用

当需要使用一个类的时候, 首先要对其构造函数进行调用, 以便初始化成员变量、 基类构造。 其实, 在类的构造函数中, 所有的成员变量就已经被创建成功了, 调用构造函数的时候, 只是在进行变量的重新赋值操作[2]。 如果使用初始化列表, 则相当于使用参数初始化成员变量, 仅仅调用一次成员变量的构造函数。

在上面的这一段代码中, 当进入此构造函数的时候, Name其实就已经被创建成功了, 当执行到 “Name=p” 时, 其实就是在对Name进行重载。 在这整个过程中, 一共调用了两个函数, 一个是Name成员变量的构造函, 另一个是 “=” 重载函数。 其实可以用更加高效的代码来实现同样的效果, 以提高程序的时间性能。

这样一来, 程序运行所需的时间就会显著缩短, 时间性能由此提升。

2.3 合理使用inline

使用inline, 可以使程序在编译的时候, 由函数体代码来替代调用指令, 也就是说在经编译后的可执行文件中, 原本为调用函数的地方, 被直接替换成了该函数的代码, 这可以减少函数调用时的进栈、 出栈等操作, 程序运行时间性能便可由此提升[3]。

下面对inline是到底如何提升程序时间性能的进行分析: (1) 如前面所提到的, 由于采用了函数代码直接替代了函数调用, 所以就减少了函数调用时的进栈、 出栈操作; (2) 在函数代码直接插入到了调用处后, 程序的编译便可对代码上下文信息有更多的了解, 有了这些信息作为支持。 编译器就能够帮助人们进行代码优化, 当中的优化过程和优化内容不一定是我们所清楚的; (3) 在不使用inline的情况下程序执行至函数调用处, 需要转而去执行函数体所在位置的代码。一般函数调用位置与函数代码所在位置在代码段中并不相近, 这样很容易形成操作系统的缺页中断。 操作系统需要把缺页地址处的代码从硬盘移人内存, 所需时间将成数量级增加。而使用inline则可以减少缺页中断发生的机会。

虽然inline的使用具有相当多的优势, 可以优化程序设计的时间性能, 但是在实际的操作时, 还需要注意到一些特殊情况的存在。 例如, inline采用了函数代码直接替代了函数调用, 但如果使用过多, 则会致使程序占据大量的内存空间, 虽然今天的硬件技术已经给予了程序非常大量的内容空间, 但如果不合理使用仍会导致内存空间不足, 既会降低程序的空间性能。 在调用一个函数的时候, 该函数进行进栈、 出栈操作, 也需要由代码来执行, 这些代码由编译器自己生成, 其主要的作用是现场保存、 恢复、 参数传递以及栈指针调整等, 在实际的程序设计过程中, 如果编译器所生成的代码量比直接插入的函数实体代码量多的话, 就可以使用inline函数, 但如果编译器所生成的代码量比直接插入的函数实体代码量少的话, 就应当谨慎的使用inline, 因为它可能反而会增加操作, 降低程序的时间性能。 但还有一种情况存在, 如果有一个函数非常的复杂, 而且在程序的整个执行流程中, 将会非常频繁地调用这个函数, 这个函数已经成为了程序时间性能提升的一个瓶颈, 也可以考虑使用inline, 以牺牲空间性能的方法, 来提升程序的时间性能。

2.4 谨慎使用虚函数

作为一种从C语言发展和演化过来的程序设计语言, C++具有其自身非常鲜明和重要的特性, 例如虚函数, C++的虚函数实现了运行时多态, 这是此前很多程序设计语言都不具备的功能, 但它也并不是就完全没有缺点的, 因为虚函数的使用会增加较大的空间压力和时间压力, 使程序的空间性能与时间性能降低。

在实际的程序设计和运行中, 每一个虚函数类都有一张虚函数表, 该表主要存放的是函数指针, 只要对虚函数类进行一次实例化操作, 就会增加一张虚函数表, 这个过程会消耗一定量的时间和空间。 为此, 如果虚函数的使用会降低程序的时间性能, 就应当考虑其他方法, 放弃对虚函数的使用, 以保障程序的时间性能。

2.5 合理使用智能指针

指针是C++程序中常用的类型。 若合理使用指针, 既可以节省内存, 也可以节省运行时间。 例如, 若需要把很多复杂的信息存储在程序内部。 我们把一个信息单元存储在一个类中, 然后把该类的对象存储在某种STL容器中。 若直接存储对象, 一方面会使一个信息单存储在内存中多个对象中, 造成内存空间的浪费;另一方面对象从容器中存人、 取出时都会造成大量的运行时间浪费。 而如果存储对象的指针, 则一个信息单元只需要一个对象存储, 且指针从容器中存人和取出的时间性能更高。 但指针有一个致命的缺陷:容易造成内存泄露, 不利于软件设计。 软件工程师必须准确控制指针在何时何处被释放。 若指针没有释放, 会造成内存泄露;若同一个指针被多次释放, 程序会产生异常[4]。

C++的开源程序库Boost库中定义了智能指针, 包括普通智能指针shared_ptr和智能数组指针shared_ array。 智能指针实际上是一个类, 其中成员变量包括普通指针和其引用计数。 引用记数记录程序引用该指针的次数。 该类可以在指针不再被使用时自动将其释放。 C++11 标准已经把智能指针加人到标准中。智能指针既具有普通指针节省内存和时间的优点, 又将程序设计从控制指针的释放这一繁重任务中解脱出来。 因此, 程序中涉及到对象的存储、 转发等应用时, 应合理使用智能指针[5]。

3 结语

时间性能是决定程序设计与开发质量的一个重要指标, 在空间性能已经不再严重限制程序设计与开发时, 应当将更多的注意力集中在程序时间性能的优化上, 除了上面提到的几种方法外, 在实际的程序设计中, 还应当通过对数据结构和算法的改进, 来优化程序时间性能。

参考文献

[1]廖桂梅.C++基础上的加密/解密置换算法的实现研究[J].无线互联科技, 2015, 24:9-10.

[2]周建儒.C语言与C++语言几点差异的应用实例分析[J].河北软件职业技术学院学报, 2016, 01:52-54.

[3]高枚, 龚沛曾, 孙丽君.构建多层次思维培养的C/C++程序设计教学[J].计算机教育, 2016, 01:81-83.

[4]周建儒.C++语言中易错应用的实例分析[J].电脑编程技巧与维护, 2016, 02:15-16.

程序性能 第9篇

关键词:OpenMP,并行程序,FDTD,影响因素

在科学计算中,并行方式通常由于其较低的开销和较短的运算时间而受到人们的广泛关注。目前,并行计算的实现主要基于2种并行标准:MPI(Message Passing Interface)标准[1],用于消息传递方式的并行编程,允许不共享内存的多处理器合作进行并行计算;OpenMP标准[2],用于共享存储方式的并行编程,可以实现在SMP(Symmetrical Multi-Processor)系统内多处理器的多线程并行计算。

OpenMP是一个应用程序接口,包括一套编译指导语句和一个用来支持它的函数库,通过与标准的Fortran,C和C++结合工作。使用OpenMP开发共享存储的多线程程序非常简单和容易,但却很难保证所开发的程序具有很高的性能。同时,若使用不当,也会使程序发生混乱,产生诸如数据竞争,死锁等现象[3],从而导致最终的运算结果有误。OpenMP程序的一些因素对其执行效率也有很大影响[4]。因此,在使用OpenMP语句编写并行代码时,需要对其进行详实而细致的分析,从而保证程序的正确性和高效性。

时域有限差分法(FDTD)是模拟计算电磁场的一种基本算法,其串行方式通常由于问题的复杂程度而花费大量的计算时间。如果使用并行方式进行计算,则能够在很大程度上减少运算所用时间,提高运算效率。本文以一个二维平面波导为例,讨论如何使用OpenMP语句设计电磁场的FDTD并行程序,同时分析各种不同的并行方法对程序性能的影响,从而找到最为高效的并行方式,使之能够在最短的时间内得到正确的结果。

1 电磁场的计算过程

电磁场FDTD算法的基本思想是[5]:使用差分网格,将连续的电磁场问题变为离散系统问题,用各离散点上的数值解来逼近连续场域内的真实解。使用YEE网格得到一系列离散的电磁场计算公式,采用蛙跳格式,利用前一时刻已知的电场和磁场求出当前时刻的电磁场。随着时间的推移,计算过程不断进行下去。

本文所讨论的二维波导结构如图1所示。其中波导的长为3 m,宽为0.18 m,波导左端施加1 GHz的高斯脉冲激励源。设波的传播方向为z方向,垂直方向为x方向。当用FDTD处理该问题时,为将边界点落在网格点上,选取网格步长ΔL为0.015 m将整个场域离散成一个个正方形网格,如图2所示。

电场存在于网格的边线上,磁场位于网格的中心

点。由此得到TM模式上的3个电磁场的计算公式:

(Ηy)i+1/2,k+1/2n+1/2=(Ηy)i+1/2,k+1/2n-1/2+Δtμ×((Ez)i+1,k+1/2n-(Ez)i,k+1/2nΔx+(Ex)i+1/2,kn-(Ex)i+1/2,k+1nΔz)(1)(Ex)i+1/2,kn+1=(Ex)i+1/2,kn+Δtε(Ηy)i+1/2,k-1/2n+1/2-(Ηy)i+1/2,k+1/2n+1/2Δz(2)(Ez)i,k+1/2n+1=(Ez)i,k+1/2n+Δtε(Ηy)i+1/2,k+1/2n+1/2-(Ηy)i-1/2,k+1/2n+1/2Δx(3)

为保证计算数值的稳定性,选取时间步长Δtl/2c,其中c为光速。对左端口,当时间步数小于160步时,使用高斯脉冲激励,其他时刻使用Mur的一阶吸收边界条件。对右端口,为减小反射造成的影响,使用吸收效果更好的三阶廖氏吸收边界条件。激励源及吸收边界条件公式如式(4)~(6):

(Ex)i+1/2,1n=E0exp[-(t-t0Τ)2](4)(Ex)i+1/2,1n+1=(Ex)i+1/2,2n+(cΔt-ΔlcΔt+Δl)[(Ex)i+1/2,2n+1-(Ex)i+1/2,1n](5)(Ex)i+1/2,maxn+1=98(Ex)i+1/2,maxn+94(Ex)i+1/2,max-1n-38(Ex)i+1/2,max-2n-3(Ex)i+1/2,max-1n-1-18(Ex)i+1/2,maxn-2+34(Ex)i+1/2,max-1n-2+38(Ex)i+1/2,max-2n-2(6)

这样,当一次时刻所有场域的电磁场计算完成后,使用迭代过程就可以计算出随时间变化的波导内电磁场的分布情况。在Fortran中,使用Do循环语句可以方便的模拟上述过程。

2 OpenMP并行设计

OpenMP采用的是标准的并行模式——fork/join式并行,使用编译器指导指令实现并行化。当程序开始执行时,只有1个主线程的线程存在。当遇到OpenMP的指导指令要求程序并行化时,主线程派生出很多子线程。在并行区域,主线程和这些派生的子线程协同工作。在并行区结束之后,派生的线程退出或挂起,由主线程继续执行后续部分。因而,OpenMP的并行程序线程数是动态变化的。

采用OpenMP对本文的电磁场问题进行并行设计时,可以有多种实现方法,如使用数据并行或功能并行,改变数据调度类型或改变设置的线程数目等。不同的实现方法在运行的效率上是不同的。下面根据实现方式的不同,分析不同的并行程序在性能上的差异,以此找到最为优化的设计方法。

以下测试均在基于双CPU四线程的多处理器系统上使用Intel Fortran Compiler 9.1编译实现。

2.1 并行结构的不同

OpenMP可以实现两种并行方式:数据并行和功能并行[6]。数据并行是一种普遍意义上的并行方式,在程序中,常有一些循环操作,如果在循环的内部各个计算过程之间不存在相关性,则可以转换成并行循环,即数据并行。在OpenMP中,只需要告诉编译器该循环过程可以被并行执行,则编译器会负责生成派生和会合线程以及调度并行循环的代码,并将循环的任务分配给线程。如果一个程序的某些部分之间互不影响,没有相关性,同时执行这些部分运算的结果是正确的,则可以使用功能并行的方式将这些部分同时分配到不同的线程上并行执行。在OpenMP中,可以为不同的线程分配不同部分的代码,使用parallel sections语句即可帮助编译器实现功能并行。

在电磁场的计算过程中,时间步长上的迭代过程是相互关联,相互影响的。一次迭代需要使用上一次迭代的运算结果,故迭代之间不宜实现并行化。而在一次迭代内部,电场和磁场的计算仅需要前一次时刻的计算结果,各个计算过程之间没有影响,可以使用这两种方式实现并行计算。

在原串行程序上的迭代循环内部,添加“!MYMOMP PARALLEL DO”语句实现1次循环的数据并行,添加 “!MYMOMP PARALLEL SECTIONS”语句将电场和磁场的计算过程分到各个线程上实现功能并行。将2个实现的方式与原串行方式的运行效率做比较,其结果如图3所示。从图3可以看出,并行方式明显要比串行方式效率高,花费的时间少。而数据并行与功能并行两者之间也有很大的差异,数据并行方式性能最佳。当时间迭代次数从1 000次~5 000次变化时,相比数据并行方式,功能并行所花费的时间大约是其两倍左右,而串行方式则逐渐从2倍增加到3倍。由此可见,数据并行是实现并行运算最好的方式。

2.2 调度策略的不同

在对嵌套的循环进行并行操作的过程中,执行不同的调度策略所需的时间差别是相当大的。如果要计算n次的循环过程,每次循环的计算任务需要分配到t个线程中,如果每个线程被分到(n/t)次循环,可能得到较低的并行效率。这是因为各个线程所需完成任务的时间可能是不同的,先完成的线程需要等待所有线程都完成任务后才能继续下一次循环的并行计算。如果将新任务立即交给先完成任务的线程执行,则并行计算的总效率可能是最好的。调度策略的选择,是依照任务的负载不平衡性所决定的。在OpenMP中,使用schedual子句可以指定执行循环调度的策略,即如何将循环过程在线程间分配。OpenMP的调度策略有多种,包括动态调度、静态调度、指导性自调度、运行时调度等。本文只讨论前3种调度形式,并且使用默认的数据分块大小完成调度操作。

在静态调度中,任务在执行之前就已经被分配到各个线程中了,每个线程被分配到(n/t)次连续的循环。

如果使用动态调度,则任务开始时只分配1次循环,当线程执行完这次循环操作后,再将下一次循环任务交给线程执行。

指导性自调度方式是一种启发式自调度方法,开始时每个任务会分配到较大的数据任务块,之后任务每次请求新的任务时会被分配到大小递减的数据任务块,直到所有任务均完成为止。静态调度的开销小,但有明显的负载不平衡现象,动态调度的开销相对大,但可减少负载的不平衡现象。

在效率最高的数据并行方式上,对不同的调度方式做测试,包括静态调度STATIC,动态调度DYNAMIC,指导性自调度GUIDED和不定义调度方式。测试结果如图4所示。

不同的调度策略对程序的整体性能是有影响的。从图4中可以看出,相比其他2种情况,静态的调度方式与未指定调度策略的方式效率最好,运行时间大致相等。这说明在使用FDTD进行电磁场计算的过程中,各个场量的计算所用时间大体上一致,负载的不平衡性较小,可以忽略这种现象而在执行并行计算开始前直接把任务全部分配到线程中去。而倘若使用动态调度或指导性自调度方式,增加负载的不平衡现象,消耗时间,影响效率。可见,在本例中,静态调度方式是最为合适的调度策略。

2.3 设置线程数的不同

在使用OpenMP实现共享存储并行计算的过程中,线程数目的大小并不一定和系统实际的线程数一致,可以事先指定并行区域的线程总数来完成并行操作。线程数的不同会影响到并行程序效率,OpenMP中使用OMP_SET_NUM_THREADS()函数对并行部分的线程数进行定义,线程总数的定义需要在并行操作前的串行部分,若未指定线程数,则默认使用系统最大的线程数完成并行操作。前面关于并行实现方式和调度策略2个测试就是在未指定线程数的大小下完成的。当线程数发生变化时,程序运行时间的变化情况如表1所示。

可以看到,当设置的线程数与实际系统的线程数一致时(表1中线程数为4的列),运行的时间是最短的;当线程数超过系统的最大线程时,为实现并行化,线程之间增加了同步和竞争系统资源系统所需要的时间,这种差距越大,耗时越多;当线程数小于系统的最大线程时,并行计算的过程中总会有一些CPU空闲,计算机并未工作在最佳的状态,因而用时也较多;当线程数设置为1时,此时的并行程序实际上只有一个线程执行,成为串行方式。因此,使用系统默认的最大线程数是实现电磁场并行计算的最有效途径。

3 结 语

目前,个人计算机的CPU逐渐走向多核化,单机多线程的SMP系统也逐渐普及起来,在此类系统上设计并使用并行化程序有着重要的实用价值。本文以二维波导的电磁场FDTD算法为例,研究如何使用OpenMP设计其并行程序,以及分析影响并行效率的几个因素。测试分析的结果表明,采用不同的并行方式,设定不同的调度策略以及修改并行线程数的大小均会对并行程序本身的优化效率性能产生影响。综合考虑各个因素才能设计出最为优化的并行程序。本文所使用的例子虽然简单,但代表了设计此类问题的一般方法,对在使用FDTD算法的电磁场复杂问题有着一定的指导意义。

使用OpenMP设计的基于共享存储标准的电磁场并行程序虽然在效率方面有一些提高,但只能运行在多线程的SMP系统上。如果结合消息传递机制的MPI标准对这一类问题混合编写并行代码,则能够适用于由多处理器计算机组成的多机机群,用途更广、效率也更高。这一问题,将在今后的研究中进行下去。

参考文献

[1]莫则尧,袁国兴.消息传递并行编程环境MPI[M].北京:科学出版社,2001.

[2]OpenMP Architecture Review.OpenMP Application Pro-gramInterface[EB/OL].Version 2.5.http://www.openmp.org,2005.

[3]Paul Petersen,Sanjiv Shah.OpenMP Support inthe Intel ThreadChecker[J].International Workshop on OpenMP Applicationsand Tools,WOMPAT 2003.Toronto,2003:1 12.

[4]Li Jianjiang,Shu Ji wu,Chen Yongjian,et al.Analysis of Fac-tors Affecting Execution Performance of OpenMP Programs[J].Tsinghua Science and Technology,2005,10(3):304308.

[5]王秉中.计算电磁学[M].北京:科学出版社,2002.

本文来自 99学术网(www.99xueshu.com),转载请保留网址和出处

【程序性能】相关文章:

高性能沥青混合料抗车辙性能09-12

性能改进05-04

结晶性能05-05

检索性能05-06

产品性能05-16

墙体性能05-16

工作性能05-16

干热性能05-22

剩余性能05-23

抗水性能05-25

上一篇:学科理论视野下一篇:变质过程