背景
在运行操作软件的,一个操作执行太慢,需要首先分类是IO操作密集引起的问题还是CPU相关的计算密集型问题,软件的性能优化不管是从编码规范还是工程项目实践上来说,都有很多需要我们作为开发人员注意的方向点。
性能优化的目的是为了让程序执行功能变得高效,但同时也不能丧失程序的可维护性和可扩展性。
性能优化是一种实验科学,往往是通过不断迭代进行,在每次优化方案实施完毕后需要对程序的优化前后的性能进行对比来验证优化方案的可行性。
下面主要从C和C++语言入手进行一些代码性能优化上去分析,助力开发相对高性能的软件。
理论基础
影响一个软件程序性能架构的因素主要有两方面分别为:硬件和软件。
影响硬性性能方面的因素有:
影响软件性能方面的主要因素有:
软件的系统函数调用例如 open、read、fread、write、close、mmap、sbrk、time、gettimeofday等系统函数(因为需要通过系统调用来和内核进行交互)
编译器优化:在没有同步原语(包括:互斥锁操作、内存屏障、原子操作等等)的情况下,为了程序的性能编译器一般可以在当前线程的结果不变的情况下,自由调整执行顺序。
语言抽象性(表现为词汇级和词法级抽象) : C、C++语言的中间文件是obj文件,它通过在栈上分配了sizeof(obj)字节空间,它们的时间复杂度都是为0(1),相对于C语言C++面向对象中的类机制,涉及到类初始化时候的构造函数调用,类结束时的析构函数,这会给程序带来一定性能影响。
编译器的优化
软件的开发离不开编译器工具作为基础,编译工具的合理利用也可以为程序性能提升提供助推作用。
下面从编译器浅谈下优化的一点点思路。
1、在没有同步原语(互斥锁操作、内存屏障、原子操作)的情况下,编译器为了性能可以在当前线程结果不变的情况下自由调整执行顺序。
2、在编译器中,会自动将语句进行等价转换例如:x=a; y=2; 可以自动转换为 y=2; x=a;再入x=y+1; y=x+2 可等价转换为t=y; y+=3;x=t+1;
3、在编译器中,局部变量可能会被完全消除。
4、全局变量只保证在下一个同步点到来之前写回到内存里。
5、Volatie声明会禁止编译器进行相关的优化。
6、在编译器中,可以使用__attribute__((noinline))防止意外内联。
循环中的优化
程序使用循环语句,在一定情况下会大大增加计算机中CPU的运算时间和效率。因此在程序中的性能优化,循环语句是一个非常大的技术点需要重点设计考虑。
下面针对循环语句罗列几个优化的思路方案。
对象参数的优化
String接口的优化
函数和虚函数的优化
函数的调用使得处理器跳到另外一个代码地址并回来,这个过程一般需要4个时钟周期,大多数情况处理器会把函数调用、返回和其他指令一起执行以节约运行时间。函数的参数存储在栈上需要额外的时间( 包括栈帧的建立、saving and restoring registers、可能还有异常信息等)。
下面就针对函数相关的罗列一些提高性能的思路。
1、避免过多使用不必要的函数,特别在最底层的循环,应该尽量让代码在一个函数内。看起来与良好的编码习惯冲突(一个函数最好不要超过80行),我们应该知道何时去关注函数的这些优化,而不是一上来就让代码可读性和可为维护性变低。
2、可以使用一些inline函数,让函数调用的地方直接用函数体替换。Inline它对编译器来说是个建议,而且不是inline了性能就好,一般当函数比较小或者只有一个地方调用的时候,inline效果会相对比较好。
3、减少函数的间接调用,如偏向静态链接而不是动态链接,尽量少用或者不用多继承、虚拟继承等风格。
4、优先使用迭代而不是递归。
5、使用函数来替换define,从而避免多次求值。宏的其他缺点:不能overload和限制作用域。
6、减少虚函数的使用,尽可能使用模板方式进行代替虚函数的使用。
7、类的使用,同时在构造函数、析构函数尽可能简单化使用,消除不必要的反复使用构造函数和析构函数。
8、类对象使用时候,复制对象的开销是高昂的。最好选择传递引用,而不是传递值。
运算表达式优化
内存优化
程序在运行时,占用内存越少,那么它的运行效率也就更快,也说明程序的运行性能较好。那么如果对这块内存进行做优化,让程序达到更好的性能?
下面分析几种对内存优化的方案。
算法优化
在程序开发过程中,可以根据数据集的特征选择更高的数据结构和算法策略,这就要求到开发人员对数据结构和算法空间复杂度和时间复杂度有清晰的认识。
在程序中算法会大大影响程序的性能,因此选择一个合适高效率的算法很重要。
多线程的优化
总结
程序的性能优化,不仅可以从编译器、语言特性、编码习惯、算法选择、程序架构设计等等方面入手,不断的提升程序的性能,以此达到用户体验感的提升。
最后从项目上梳理几个可以优化的思路点。
1、去除项目中冗余代码。
2、字符串操作优化。
3、减少内存分配、释放操作,例如可以使用内存池。
4、减少不必要的互斥锁操作。
5、根据性能需求选择数据结构。
6、延迟工作,按需执行。
7、减少跨进程的调用。
8、使用高性能的函数库。
9、可以通过使用智能指针代替指针的使用。
10、优化动态库文件的加载,尽量避免不必要的IO操作。
最后推荐一个很不错的在线编码平台(可以将代码自动转换为汇编代码,并且支持多平台汇编代码的转换):COMPILER EXPLORER;
后面是平台链接https://godbolt.org/