本文以C/C程序为例讲述了程序运行效率的10个简单方法分享给大家供大家参考之用。具体分析如下对于每一个程序员来说程序的运行效率都是一个值得重视并为之付出努力的问题。但是程序性能的优化也是一门复杂的学问需要很多的知识然而并不是每个程序员都具备这样的知识而且论述如何优化程序提高程序运行效率的书籍也很少。但是这并不等于我们可以忽略程序的运行效率下面就介绍一下本人积累的一些简单实用的提高程序运行效率的方法希望对大家有所帮助。一、尽量减少值传递多用引用来传递参数。至于其中的原因相信大家也很清楚如果参数是int等语言自定义的类型可能能性能的影响还不是很大但是如果参数是一个类的对象那么其效率问题就不言而喻了。例如一个判断两个字符串是否相等的函数其声明如下1234boolComparestring s1, string s2)boolCompare(string *s1, string *s2)boolCompare(string s1, string s2)boolCompare(conststring s1,conststring s2)其中若使用第一个函数值传递则在参数传递和函数返回时需要调用string的构造函数和析构函数两次即共多调用了四个函数而其他的三个函数指针传递和引用传递则不需要调用这四个函数。因为指针和引用都不会创建新的对象。如果一个构造一个对象和析构一个对象的开销是庞大的这就是会效率造成一定的影响。然而在很多人的眼中指针是一个恶梦使用指针就意味着错误那么就使用引用吧它与使用普通值传递一样方便直观同时具有指针传递的高效和能力。因为引用是一个变量的别名对其操作等同于对实际对象操作所以当你确定在你的函数是不会或不需要变量参数的值时就大胆地在声明的前面加上一个const吧就如最后的一个函数声明一样。同时加上一个const还有一个好处就是可以对常量进行引用若不加上const修饰符引用是不能引用常量的。二、i和i引申出的效率问题看了上面的第一点你可能觉得那不就是多调用了四个函数而已你可能对此不屑一顾。那么来看看下面的例子应该会让你大吃一惊。至于整型变量的前加和后加的区别相信大家也是很清楚的。然而在这里我想跟大家谈的却是C类的运算符重载为了与整形变量的用法一致在C中重载运算符时一般都会把前加和后加都重载。你可能会说你在代码中不会重载运算符但是你敢说你没有使用过类的运算符重载吗迭代器类你总使用过吧可能到现在你还不是很懂我在说什么那么就先看看下面的例子吧是本人为链表写的一个内部迭代器。1234567891011_SingleList::Iterator _SingleList::Iterator::operator()//前加{pNote pNote-pNext;return*this;}_SingleList::Iterator _SingleList::Iterator::operator(int)//后加{Iterator tmp(*this);pNote pNote-pNext;returntmp;}从后加的实现方式可以知道对象利用自己创建一个临时对象自己在函数调用的一个复制然后改变自己的状态并返回这个临时对象而前加的实现方式时直接改变自己的内部状态并返回自己的引用。从第一点的论述可以知道后加实现时会调用复制构造函数在函数返回时还要调用析构函数而由于前加实现方式直接改变对象的内部状态并返回自己的引用至始至终也没有创建新的对象所以也就不会调用构造函数和析构函数。然而更加糟糕的是迭代器通常是用来遍历容器的它大多应用在循环中试想你的链表有100个元素用下面的两种方式遍历123456789for(_SingleList::Iterator it list.begin(); it ! list.end(); it){//do something}for(_SingleList::Iterator it list.begin(); it ! list.end(); it){//do something}如果你的习惯不好写了第二种形式那么很不幸做同样的事情就是因为一个前加和一个后加的区别你就要调用多200个函数其对效率的影响可就不可忽视了。三、循环引发的讨论1循环内定义还是循环外定义对象请看下面的两段代码代码1123456ClassTest CTfor(inti 0; i 100; i){CT a;//do something}代码212345for(inti 0; i 100; i){ClassTest CT a;//do something}你会觉得哪段代码的运行效率较高呢代码1科学家是代码2其实这种情况下哪段代码的效率更高是不确定的或者说是由这个类ClassTest本向决定的分析如下对于代码1需要调用ClassTest的构造函数1次赋值操作函数operator100次对于代码2需要高用复制构造函数100次析构函数100次。如果调用赋值操作函数的开销比调用构造函数和析构函数的总开销小则第一种效率高否则第二种的效率高。四、循环引发的讨论2避免过大的循环现在请看下面的两段代码代码112345for(inti 0; i n; i){fun1();fun2();}代码212345678for(inti 0; i n; i){fun1();}for(inti 0; i n; i){fun2();}复制讲解注这里的fun1()和fun2()是没有关联的即两段代码所产生的结果是一样的。以代码的层面上来看似乎是代码1的效率更高因为毕竟代码1少了n次的自加运算和判断毕竟自加运算和判断也是需要时间的。但是现实真的是这样吗这就要看fun1和fun2这两个函数的规模或复杂性了如果这多个函数的代码语句很少则代码1的运行效率高一些但是若fun1和fun2的语句有很多规模较大则代码2的运行效率会比代码1显著高得多。可能你不明白这是为什么要说是为什么这要由计算机的硬件说起。由于CPU只能从内存在读取数据而CPU的运算速度远远大于内存所以为了提高程序的运行速度有效地利用CPU的能力在内存与CPU之间有一个叫Cache的存储器它的速度接近CPU。而Cache中的数据是从内存中加载而来的这个过程需要访问内存速度较慢。这里先说说Cache的设计原理就是时间局部性和空间局部性。时间局部性是指如果一个存储单元被访问则可能该单元会很快被再次访问这是因为程序存在着循环。空间局部性是指如果一个储存单元被访问则该单元邻近的单元也可能很快被访问这是因为程序中大部分指令是顺序存储、顺序执行的数据也一般也是以向量、数组、树、表等形式簇聚在一起的。
提高C++程序运行效率的10个简单方法
本文以C/C程序为例讲述了程序运行效率的10个简单方法分享给大家供大家参考之用。具体分析如下对于每一个程序员来说程序的运行效率都是一个值得重视并为之付出努力的问题。但是程序性能的优化也是一门复杂的学问需要很多的知识然而并不是每个程序员都具备这样的知识而且论述如何优化程序提高程序运行效率的书籍也很少。但是这并不等于我们可以忽略程序的运行效率下面就介绍一下本人积累的一些简单实用的提高程序运行效率的方法希望对大家有所帮助。一、尽量减少值传递多用引用来传递参数。至于其中的原因相信大家也很清楚如果参数是int等语言自定义的类型可能能性能的影响还不是很大但是如果参数是一个类的对象那么其效率问题就不言而喻了。例如一个判断两个字符串是否相等的函数其声明如下1234boolComparestring s1, string s2)boolCompare(string *s1, string *s2)boolCompare(string s1, string s2)boolCompare(conststring s1,conststring s2)其中若使用第一个函数值传递则在参数传递和函数返回时需要调用string的构造函数和析构函数两次即共多调用了四个函数而其他的三个函数指针传递和引用传递则不需要调用这四个函数。因为指针和引用都不会创建新的对象。如果一个构造一个对象和析构一个对象的开销是庞大的这就是会效率造成一定的影响。然而在很多人的眼中指针是一个恶梦使用指针就意味着错误那么就使用引用吧它与使用普通值传递一样方便直观同时具有指针传递的高效和能力。因为引用是一个变量的别名对其操作等同于对实际对象操作所以当你确定在你的函数是不会或不需要变量参数的值时就大胆地在声明的前面加上一个const吧就如最后的一个函数声明一样。同时加上一个const还有一个好处就是可以对常量进行引用若不加上const修饰符引用是不能引用常量的。二、i和i引申出的效率问题看了上面的第一点你可能觉得那不就是多调用了四个函数而已你可能对此不屑一顾。那么来看看下面的例子应该会让你大吃一惊。至于整型变量的前加和后加的区别相信大家也是很清楚的。然而在这里我想跟大家谈的却是C类的运算符重载为了与整形变量的用法一致在C中重载运算符时一般都会把前加和后加都重载。你可能会说你在代码中不会重载运算符但是你敢说你没有使用过类的运算符重载吗迭代器类你总使用过吧可能到现在你还不是很懂我在说什么那么就先看看下面的例子吧是本人为链表写的一个内部迭代器。1234567891011_SingleList::Iterator _SingleList::Iterator::operator()//前加{pNote pNote-pNext;return*this;}_SingleList::Iterator _SingleList::Iterator::operator(int)//后加{Iterator tmp(*this);pNote pNote-pNext;returntmp;}从后加的实现方式可以知道对象利用自己创建一个临时对象自己在函数调用的一个复制然后改变自己的状态并返回这个临时对象而前加的实现方式时直接改变自己的内部状态并返回自己的引用。从第一点的论述可以知道后加实现时会调用复制构造函数在函数返回时还要调用析构函数而由于前加实现方式直接改变对象的内部状态并返回自己的引用至始至终也没有创建新的对象所以也就不会调用构造函数和析构函数。然而更加糟糕的是迭代器通常是用来遍历容器的它大多应用在循环中试想你的链表有100个元素用下面的两种方式遍历123456789for(_SingleList::Iterator it list.begin(); it ! list.end(); it){//do something}for(_SingleList::Iterator it list.begin(); it ! list.end(); it){//do something}如果你的习惯不好写了第二种形式那么很不幸做同样的事情就是因为一个前加和一个后加的区别你就要调用多200个函数其对效率的影响可就不可忽视了。三、循环引发的讨论1循环内定义还是循环外定义对象请看下面的两段代码代码1123456ClassTest CTfor(inti 0; i 100; i){CT a;//do something}代码212345for(inti 0; i 100; i){ClassTest CT a;//do something}你会觉得哪段代码的运行效率较高呢代码1科学家是代码2其实这种情况下哪段代码的效率更高是不确定的或者说是由这个类ClassTest本向决定的分析如下对于代码1需要调用ClassTest的构造函数1次赋值操作函数operator100次对于代码2需要高用复制构造函数100次析构函数100次。如果调用赋值操作函数的开销比调用构造函数和析构函数的总开销小则第一种效率高否则第二种的效率高。四、循环引发的讨论2避免过大的循环现在请看下面的两段代码代码112345for(inti 0; i n; i){fun1();fun2();}代码212345678for(inti 0; i n; i){fun1();}for(inti 0; i n; i){fun2();}复制讲解注这里的fun1()和fun2()是没有关联的即两段代码所产生的结果是一样的。以代码的层面上来看似乎是代码1的效率更高因为毕竟代码1少了n次的自加运算和判断毕竟自加运算和判断也是需要时间的。但是现实真的是这样吗这就要看fun1和fun2这两个函数的规模或复杂性了如果这多个函数的代码语句很少则代码1的运行效率高一些但是若fun1和fun2的语句有很多规模较大则代码2的运行效率会比代码1显著高得多。可能你不明白这是为什么要说是为什么这要由计算机的硬件说起。由于CPU只能从内存在读取数据而CPU的运算速度远远大于内存所以为了提高程序的运行速度有效地利用CPU的能力在内存与CPU之间有一个叫Cache的存储器它的速度接近CPU。而Cache中的数据是从内存中加载而来的这个过程需要访问内存速度较慢。这里先说说Cache的设计原理就是时间局部性和空间局部性。时间局部性是指如果一个存储单元被访问则可能该单元会很快被再次访问这是因为程序存在着循环。空间局部性是指如果一个储存单元被访问则该单元邻近的单元也可能很快被访问这是因为程序中大部分指令是顺序存储、顺序执行的数据也一般也是以向量、数组、树、表等形式簇聚在一起的。