优化程序性能

2018-04-12  本文已影响0人  索马里的海带

    个人觉得虽然没必要过度优化,现代编译器在优化方面已经做得比较好了,但是在有些方面,还是需要程序员主动去做的。

    不过度优化≠不主动优化

    深度学习跑模型的时候,有时候可能一跑就是几天,哪怕只对程序优化10%,效果也是非常可观的。在进行优化的时候,程序可能因此可读性和可维护性都变差,这就需要程序员自己去权衡了,在速度非常重要的时候,做一些牺牲也是可以接受的。

1.高级设计。为遇到的问题选择适当的算法和数据结构。要特别警觉,避免使用那些会渐进产生糟糕性能的算法和编码技术。

2.基本编码原则。避免限制优化的因素,这样编译器就能产生高效的代码。

    这点是非常重要的,因为其他优化编译器都可以代替你做,但是以下两点,编译器是没办法做优化的,至于为什么这两点会导致编译器没办法优化,这里不再赘述。

1)消除连续的函数调用。在可能时,将计算移到循环外。考虑有选择性的妥协程序的模块性已获得更大的效率。 

    除此之外,在可能的情况下,使用内联函数也是一个很好的选择。

    举个例子:

    在C++11以前,容器成员函数size()都是O(n),如果采用以下写法,原本O(n)复杂度的代码就变成了O(n^2)。

    for (int i=0;i<str.size();i++)

    正确的做法是尽量将函数调用提到循环外。

2)消除不必要的存储器引用。引入临时变量来保存中间结果。只有在最后的值计算出来时,才将结果放到数组或全局变量中。

    这个应该很好理解,学过计算机组成原理的都知道存储器速度远低于寄存器速度。

3.低级优化 

1)展开循环,降低开销,并且使得进一步的优化成为可能。 

    现代编译器一般都会进行循环展开优化,但是碰到严重限制程序速度的代码,还是可以尝试用循环展开改进速度。

2)通过使用例如累积变量和重新结合等技术,找到方法提高指令级并行。 

    ①累计变量

    例如a1*a2*a3*a4

    可以改成求(a1*a2)*(a3*a4)

    如果用分治法,可以把原本O(n)复杂度减少到O(logn)

    ②重新结合

    那么重新结合是意思呢,看以下代码:

    int    a=0,b=1,c=3;

    第一种写法:    a=a+b+c;

    第二种写法:    a=a+(b+c);

    乍一看这两种写法没什么区别,但是实际上两种代码的关键路径是不一样的。在汇编代码中,关键路径限制代码执行代码执行速度。

    第一种写法,应该先将a+b保存在寄存器c中,即存在寄存器写后读相关,指令不能并行执行。但是第二种写法,b+c保存在不同寄存器中,两条乘法指令互相不影响。

    以前学计算机组成原理的时候,这种冲突可以通过记分牌算法和Tomasulo算法解决,但是让硬件去优化,还是会增加开销。我用clang试了一下,编译器也是会做此类优化的。所以一般情况下,这类优化并没有太大必要。

3)使用功能的风格重写条件操作,使得编译采用条件数据传送。

    C++中在可能的情况下,用三目运算符 ?:代替条件语句。(条件传送指令更快)

上一篇下一篇

猜你喜欢

热点阅读