技术开发 频道

认识软件的性能问题

  1.1.5 软件人员眼里的软件性能

  用户恨不能让软件有无限的性能,但作为软件技术人员,我们需清楚地认识到,那种理想化的要求是不可能的。在软件性能方案中,没有什么功能较多钥匙,软件性能方案充满了辩证的各种矛盾。每种方案和方法几乎都有利有弊。只有把握设计系统的具体环境,明确设计目标,具体问题具体分析,合理平衡各种矛盾,牢牢抓住主要矛盾,才能产生出优化的软件系统性能方案。

  在上面的分析中,我们得知软件性能是软件运行空间和时间综合考虑的解决方案。那么其实满足用户的性能需求,只有以下几种方案:

  1.消除软件对空间和时间不必要的浪费

  一个最明显的例子就是内存泄漏问题,它被开发人员看做是大忌。

  严格地说,内存泄漏应该属于软件程序设计的一种缺陷,该缺陷直接导致了程序在运行过程中无法释放不再需要的内存空间,从而造成内存资源浪费,严重的会造成无可用内存,导致系统崩溃。具体来说,当用户程序在运行过程中需要动态获得内存时,操作系统总是从堆(heap)上分配相应的空间给应用,分配的结果是将该堆内存的起始地址通过指针返回给应用。正常情况下,使用完这块内存后,应通过系统调用主动通知操作系统回收这些堆内存以便重用。但是,如果由于设计缺陷导致在某些情况下程序没有主动地通知到操作系统,而后应用又失去了对这块内存的引用时,则该堆内存块将成为既不受程序控制,又不能被系统回收重用的“孤儿”内存,这便是我们所指的内存泄漏。

  案例1

  void foo( )

  {

  char *str;

  str = (char*)malloc(32*sizeof(char));

  strcpy(str, "hello world");

  return;

  /* str所指向的32个字节的内存没有被释放,当foo()返回时造成内存泄漏 */

  }

  解决:C语言中malloc和free函数要配对使用。

  案例2

  void foo()

  {

  //定义string1指针,其指向一个堆上的100个字节的内存空间

  char *string1 = (char*)malloc(100*sizeof(char));

  //定义string2指针,其指向一个堆上的200个字节的内存空间

  char *string2 = (char*)malloc(200*sizeof(char));

  scanf("%s", string2);

  string1=string2;/*string1原先指向的100个字节的内存没有被释放*/

  /*而后又被指向string2所指的内存块,造成前面100个字节的内存泄漏*/

  free(string2);

  free(string1); /* 这个free()调用会失败,因为string1指向

  的内存地址与string2的相同,而那块内存已经被释放了 */

  return 0;

  }

  解决:在程序堆上分配内存后,要在使用完后及时释放,同时避免野指针的产生,比如string1。

  原理:内存是软件运行的重要的空间资源,内存泄漏实际上是浪费了软件的空间资源。因此,内存泄漏对软件的性能影响十分重要。

  另外,对于程序在时间上的浪费,我们通常是采用优化算法和数据结构的解决策略。

  案例3

  最近几年,很多知名软件公司在招聘软件测试人员,考察代码能力的时候,内存泄露和算法优化是经常的试题之一。这说明了用户对软件性能的要求越来越严格,已经传递到了软件公司。

  2.以空间换时间

  软件的高性能并不是凭空产生的,在解决了空间和时间浪费的问题之后,如果用户还有更高的性能要求,我们软件人员只好“偷梁换柱”,做一下调整,而这种调整往往是很灵活的。

  空间换时间是软件人员解决性能问题最常见的方法。是在系统功能正常的前提下增大软件空间开销的方法来缩减运行的时间。一般的方法有算法调整、并行计算方法、体系结构方法和一些不是“办法”的办法。

  通常的解决方案有Cache缓存、数据库的index等。

  案例4

  一个动态网站服务器总发生CPU耗尽的问题,因此造成给用户的响应缓慢或者长时间没有响应,进而引起Server的宕机。经调查分析,网站首页是个PHP程序,每次用户访问都要多次查询数据库,也没有Cache机制,数据库查询负荷过高,耗尽CPU。

  解决:改写网站首页以及部分频繁访问的程序,增加Cache机制,减少数据库访问。

  原理:将常用数据放在服务器的内存中,虽然增加了内存的开销,但带来了时间上的优化,对用户而言,提高了处理速度。

  3.以时间换空间

  时间换空间的方案解决性能问题的情形比较少。有时会出现在对内存要求十分苛刻的地方,比如嵌入式操作系统中。

  案例5

  程序设计的要求是不设中间变量,交换两个变量的值。

  我们通常的中间变量的解决方案是:

  Void swapOne(int *a, int *b)

  {

  Int temp;

  Temp = *a;

  *a = *b;

  *b= temp;

  }

  但这里需要在程序中为temp变量在栈上分配一个空间。可不可以不用这个temp变量呢?

  解决:

  修改程序如下:

  void swapTwo(int *a,int *b)

  {

  *a=*a+*b;

  *b=*a-*b;

  *a=*a-*b;

  }

  原理:修改之后,多了运算复杂度,但没有使用第三方变量,减少了空间的占用。

  以上是我们从简单的程序例子来理解性能解决方案,但现实要远远复杂得多,因为随着软件系统功能的复杂强大,软件的规模也在不断扩大,我们不可能完全自己开发程序,很多时候是利用已有的平台和中间件资源。在这种场景下,我们应该怎样考虑性能问题呢?

  第一,软件系统设计的架构及技术平台

  软件在设计阶段一旦决定采用哪种架构和技术,其性能也就注定只能在一定的范围内变动了。这就是“先天”因素。比如在上节讲到的一个删除/增加数据的业务操作,如果用户对时间非常苛刻,密集型计算、在线的大数据量统计和分析等应用,这些场景通常J2EE不能够很好地解决,使用C++或者其他平台搭建会更合理些。如果在这些场景下硬要采用J2EE架构,那么开发和设计人员如何绞尽脑汁,优化设计和程序,也不会满足用户的性能要求。

  第二,中间件的设置和优化

  这里的中间件是广义的中间件,是应用程序调用的第三方软件,包括操作系统、数据库、Web服务器、消息服务器等。我们不能改变中间件的程序,只能通过调优手段来提高它所支持的软件系统的性能。

  第三,硬件的配置

  这里包括服务器硬件配置和网络环境。服务器硬件包括内存、CPU等,网络环境有交换机、路由器等。

0
相关文章