数据结构——程序性能分析

程序 = 数据结构 + 算法  数据结构是数据的组织方式,编程时用的每一个数据都是根据需求按照一定的结构组织起来的;算法是解决问题的方法、思想,讲怎么操作的问题。一个优秀的程序就是两者的结合,密不可分。数据的组织形式可以影响算法,好的数据结构或者说适用于此问题的数据结构可以简化算法的设计,同样算法也可以影响数据结构的设计。

 

一个程序,首先要保证算法的正确性,在算法正确的前提下,要求程序有优良的性能。程序性能指程序运行所需要的内存和时间,程序的性能通常用两种方式确定,一种是理论分析方法,一种是性能测试方法。

 

1.理论分析

程序的复杂度常和问题的规模相关,例如对n个元素排序,通常n越大,程序所需要的空间就越大,时间就越长,所以复杂度是n的函数,称n为实例特征。实例特征不但与空间复杂度有关,而且和时间复杂度紧密相关。

1)空间复杂度

空间复杂度指此程序运行时所需要的内存大小。通过空间复杂度可以知道指定机器是否有足够内存运行程序和运行此程序时所占的内存大小,这对于嵌入式开发在硬件设计中很重要。空间复杂度由以下部分构成。

①指令空间:源代码编译后产生的程序指令需要的存储空间。指令空间的大小取决于编译器和运行程序的目标计算机。

②数据空间:程序编译确定的常量和变量以及程序运行时动态开辟的内存空间(如new操作)。数据空间的大小也取决于编译器对不同数据类型分配的空间大小,如int类型变量,有的编译器分配4个字节,有的分配2个字节。

③环境堆栈空间:保存当前暂停的函数的状态(暂停时变量的值,寄存器状态等)及返回函数的入口地址等,如函数调用和中断响应。

通常也将指令空间和数据空间称为固定部分,环境堆栈空间称为可变部分。

 

2)时间复杂度

程序可以一次编译成功后多次运行,所以这里时间复杂度主要指运行所需时间。当不用限定词地使用“复杂度”时,通常都是指时间复杂度。时间复杂度也是程序性能的一个重要指标,因为如果程序能够运行,但需要的时间很长,这是不能接受的。例如交互式程序没有令人满意的交互响应,程序执行时不能满足实时性需求等。

通过理论分析得出程序的运行时间是很复杂的,因为计算机是一个复杂的系统,在计算机上运行的程序受到多方面因素的影响,如CPU、内存、缓存、数据的储存形式和位置等,所以理论分析只是一种大致的估计方法,只有通过上机测试才能得出程序的性能。因此理论分析通常是找出一个或多个关键操作,确定它们的执行时间或者确定程序总的步数。

操作计数:在程序中找出一种或多种关键操作,确定每一种操作在程序执行过程中的执行次数。所有操作的执行次数相加就是总的操作计数。程序的执行次数有时候不但和实例特征有关,而且与测试实例有关,程序在不同的实例上运行,执行次数不一定相同。因此在同一实例特征下,就存在最好和最坏的操作计数。最好、最坏操作计数一般和具体实例相关,而平均操作计数没有特定的实例,所以一般不易确定。

步数:操作计数方法只是统计了关键的操作,而忽略了其他操作,如自加操作、函数调用和返回操作等。步数的方法是统计程序执行过程中所有的操作,可以定义一个统计变量,将此变量嵌入到程序的每一程序步中进行自加操作,这样通过执行程序就可以知道程序进行了多少步。程序性能分析中定义的程序步没有明确的规定,而是分析人员自己定义的。如一次赋值操作可以作为一步,十次加法操作也可以作为一步等,但实例特征n的函数不能定义一步,例如n次加法作为一步。

对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,可以相互置换。追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。


关于时间复杂度的计算,这里引一篇时间复杂度计算的文章:

http://univasity.iteye.com/blog/1164707

 

2.性能测试

无论从操作计数或步数来评价程序的性能都是理论推理得到的,理论分析是一种大致估计的方法,在估计过程中忽略了一些无关紧要的因素,保留了主要因素,而要确定程序的实际运行时间,必须通过上机实验测试。性能测试关注程序的实际运行时间,通过在程序中嵌入C++提供的函数clock()来测量时间。

先看看下面这个例子。

#include<iostream>

#include<ctime>

using namespace std;

int main()                                                          //测试数组赋值并输出数组元素的时间

{

       floata[50][50];

       double elapsetime;

       clock_tstartime = clock();                      //记下开始计时的时候的滴答数

       for (int i = 0;i < 50;i++)

              for (int j = 0;j < 50; j++)

              {

                     a[i][j]= i*50+j;

                     cout<< a[i][j]<<endl;            

              }

       elapsetime =(clock() - startime);         //两次滴答次数之差,每滴答一次是一毫秒

       cout <<elapsetime<<"milliseconds"<<endl;

       return 0;

}

上面简单的例子是通过两次的滴答数之差得到程序运行时间。通过改变数组的大小,即实例特征,程序的执行时间就会改变。通常程序的时间复杂度是实例特征的函数,例如,当时间复杂度为Θ(n^2),理论上做三个不同的实例特征测试就可以确定时间复杂度函数(三点确定一条抛物线),但实际上不是这样的。因为①在我们推理时间复杂度渐进曲线的过程中,是建立在n非常大的情况下的,当n比较小的时候,得到的结果可能与渐进曲线不一致。②在推理时间复杂度渐进曲线过程中,忽略了一些非关键操作,而性能测试包含所有的操作,这也可能使结果与渐进曲线不一致。所以通常会取多个实例特征值测试。实例特征值不是越大越好,当实例特征太大,机器的性能不能满足这样的操作,而且测试也费时。

 

小结:

理论分析是一种大致估计的方法,理论分析不仅与编译器和计算机性能有关(CPU、内存,缓存等)还有数据的存储形式有关,数据存储模式不同,访问数据的时间就不同,程序的执行时间就不同,而且同一程序在同一机器上运行,每次的运行时间也是有一点差别的,不是完全相同的。

 

 

参考文献:

http://blog.csdn.net/booirror/article/details/7707551

 

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值