一些操作和C++语言本身的特性可能成为应用程序的瓶颈

来源:互联网 发布:java框架面试ssh ssm 编辑:程序博客网 时间:2024/04/27 19:24

=====一般公认的有如下因素:

 

(1)缺页:缺页往往意味着需要访问外部存储。因为外部存储访问相对于访问内存或者代码执行,有数量级的差别。因此只要有可能,应该尽量想办法减少缺页。

(2)从堆中动态申请和释放内存:如C语言中的malloc/free和C++语言中的new/delete操作非常耗时,因此要尽可能优先考虑从线程栈中获得内存。优先考虑栈而减少从动态堆中申请内存,不仅仅是因为在堆中开辟内存比在栈中要慢很多,而且还与"尽量减少缺页"这一宗旨有关。当执行程序时,当前栈帧空间所在的内存页肯定在物理内存中,因此程序代码对其中变量的存取不会引起缺页;相反,从堆中生成的对象,只有指向它的指针在栈上,对象

本身却是在堆中。堆一般来说不可能都在物理内存中,而且因为堆分配内存的特性,即使两个相邻生成的对象,也很有可能在堆内存位置上相隔很远。

因此当访问这两个对象时,虽然分别指向它们指针都在栈上,但是通过这两个指针引用它们时,很有可能会引起两次"缺页"。

(3)复杂对象的创建和销毁:这往往是一个层次相当深的递归调用,因为一个对象的创建往往只需要一条语句,看似很简单。另外,编译器生成的临时对象因为在程序的源代码中看不到,更是不容易察觉,因此尤其值得警惕和关注。

(4)函数调用:因为函数调用有固定的额外开销,因此当函数体的代码量相对较少,且该函数被非常频繁地调用时,函数调用时的固定额外开销容易成为不必要的开销。C语言的宏和C++语言的内联函数都是为了在保持函数调用的模块化特征基础上消除函数调用的固定额外开销而引入的,因为宏在提供性能优势的同时也给开发和调试带来了不便。在C++中更多提倡的是使用内联函数。

 

 

 

 

 

构造函数和析构函数性能分析

 创建一个对象,首先需取得所需的内存,然后在该内存上执行构造函数。

 创建对象时,如果从线程运行栈中创建(局部对象,语句类似ClassName obj),则在程序进入其作用域时,该对象的内存已经分配好了(一般都是通过移动栈指针),ClassName obj:一句只需调用构造函数,且这种创建方式不需要程序显式地调用析构函数,在程序运行到该对象作用域的终点时会自动调用析构函数,将空间返回给线程栈。

 如果从全局堆中创建对象,即通过new、malloc创建,对象的指针是一个局部对象,需要从线程中分配,而对象本身的内存是从全局堆中分配的,需要显式调用delete或free来销毁。用delete或free来销毁对象时,会调用其析构函数,并将所占的全局堆内存空间返回。但从销毁对象到程序退出该作用域,对象的指针还存在于栈中,并指向对象本来的位置。显然,这种情况下调用指针是非常危险的。Win32平台下访问这种指针,结果有三种可能情况:访问违例、取得无意义值、取得其他对象。第一种会导致进程崩溃,后两种虽然不会立即崩溃,但是可能会有不可预测的行为操作或造成对象不必要的变化,需要谨慎避免。

 

 

构造函数构造对象时分两个步骤:初始化执行构造函数的函数体。需要注意几点:

--构造函数是一个递归操作,即先父类后子类,父类还有父类就先构造最上面的父类。每一个在构造时都有严格的顺序,即按成员变量在类中的声明顺序进行初始化。故在初始化列表中成员的声明前后实际是无意义的。尽量养成使初始化列表与成员声明顺序一致的习惯,而且还要避免用某个成员变量的值初始化另外一个成员变量。

--千万不要以为只有初始化列表里列出来的成员变量才在执行构造函数体之前进行初始化的。事实上,即使没有在初始化列表中出现,所有成员变量仍然是在初始化这一步骤(也就是在执行构造函数的函数体前)完成初始化的。所以,我们常常在构造函数的函数体内对变量进行初始化,实际是非常浪费和降低效率的。应该养成用初始化列表进行赋初值的习惯。有个程序,就是通过多次循环来检验在构造函数的函数体内部进行初始化对性能的影响,在循环足够大的情况下,这种方法耗时是非常显著的。

--父类对象和一些成员变量没有出现在初始化列表中时,执行默认构造函数(也就是没有参数的那个构造函数)。如果没有定义构造函数,编译器会为其生成默认的构造函数。但是!!如果定义了其他类型的构造函数而没定义默认的构造函数,则会阻止编译器生成默认构造函数,就会编译出错.

--常量和引用型成员变量必须要在初始化列表中进行初始化。以前学C++也学过这点,但是知其然不知其所以然。现在明白了,因为这两类一旦被赋值,整个生命周期内都不能修改,而在执行构造函数的函数体前,所有成员变量已经被初始化了,所以不能在构造函数体内对其赋值。

--大型复杂系统中,如果需要生成的对象属于一个复杂继承体系的末端类,则其构造会引起一长串递归构造,会成为消耗CPU操作的主要部分。

 

避免临时变量

C/C++语言的函数调用都是“值传递”的,也就是说,在调用函数时,实际生成了一个参数的副本,在函数体内部所有修改都是针对这个副本的,因此这些修改在函数结束后会全部丢失。因此,最好使用引用传递参数(如果不需要修改,可传入常量引用参数),避免不必要的构造和析构操作降低效率。

原创粉丝点击