代码优化指南(一)绪论 语言与优化

1.是否需要做优化

        我们应当忘记小的性能改善,百分之九十七的情况下,过早优化都是万恶之源。
                         ——高德纳   Structured Programming with go to Statements ACM Computing
Surveys 3 6(4), December 1974, p268. CiteSeerX 4 : 10.1.1.103.6084
        以(没有必要达成的)效率之名犯下的计算之罪比其他任何一个原因(包括盲目
愚蠢)都多。
—— A Case Against the GOTO ,第 25 届美国 ACM 会议论文集( 1972 ): 796
        但是很多程序员对这两句话是有一个误解,即不是说优化不重要,而是应当怎么做优化,在什么时候做优化
        我们举两个例子说明一下:
适当的编程时优化
        1.对100000000(我也没数几个0)个随机数据进行排序,有些程序员使用选择排序,有些程序员使用快速排序,那么我们会发现实际上对于熟练掌握快速排序的程序员来说,写一个快排不比写一个选择排序慢,但是最后得出很好的运行速度,如果在这里的程序员还以上面的两句话作为挡箭牌觉得用选择排序是适当的,那么这就是给自己的懒惰与无知找借口。
不适当的编程时优化

        2.在多线程中会设计到cache的同步和内存间的同步,如果两个线程同时操作数据,A操作1~100中的奇数,B操作1~100中的偶数,我们实际会发现有这么一个问题,即1和2大概率会在同一个cache行,一旦有一个线程对这个cache行进行修改,将会导致硬件停止所有的cache存取行为,对cache进行同步,在数据量小的时候这里的优化的时间可能只有微秒级,但是想要做适当的优化需要对特定的平台的硬件架构和指令集有比较好的认识,也就是说对对应平台不熟悉的程序员要做这么一点点的优化需要付出极大的时间代价产生不可名状的代码,对于整个工程来说是得不偿失的。

2.优化的不可能三角

        当然这里说的都是极致的优化的情况下,如果不是极致的优化这三角是可以达到完美平衡的,如果发现代码完美的实现了不可能三角的同时成功优化说明还有可优化的空间,只是是否有必要而已(针对C/C++,Java的这些丑陋的细节由JVM以及JIT隐藏)。

不可能三角的三个角

        效率:

        很多时候代码的效率是用空间换来的,C++里有一种全局运行最快的方式,即内存池加鸵鸟算法。

        这里有一个笑话,有个程序员小组为导弹编写导航代码,代码要求超高的实时性和极低的延时,一个程序员突然在某天对总工说他修了个内存泄露的bug,但是发现代码的速度突然达不到要求了,总工问:内存泄露把内存池占满要多久?答:一天。总工:别修了,在漏完之前导弹已经炸了。

        当然虽然这是个笑话,但是鸵鸟算法确实是一种应对少量内存泄露的有效的办法,同时泄露的内存很多时候会被置换到磁盘且永不使用,也不会对内存产生什么大的影响。

        关于空间换时间的方式,内存池就是一种,一次性申请大量的内存池,每次从内存池申请空间只要数十条汇编指令,但是如果向操作系统申请需要进入内核态,需要数千条指令才能完成。但是内存池的问题就是占用了过多的未使用的内存,造成资源的浪费。

        速度

        C++完全可以不要内存池,程序员自己精打细算内存的使用,最简单的一件事就是实现程序的自动内存分配。当然效率一言难尽毕竟要大量的和内核交互。

        可读性:

        充满满满的恶意建议大家去读一下C++STL库,各种各样不可名状的宏定义,一层一层宏定义的嵌套(因为代码运行流最快的方式是内联展开,只有用宏定义才能强制展开)。

3.澄清一个误解,C++和Java到底谁快

        由于Java的发展以及JIT技术的实现,现代的JAVA实际上不比C++慢(很多时候)甚至有的时候会更快,因为做为静态编译语言的C++很难获取到一些运行时才能得到的信息,因此很多C++编译器不会做很激进的优化,因为有可能会导致一些不可知的问题,我们举一个例子说明一下Java会做的优化但是gcc不会做的

int main()
{
    int j=0
    for(int i=0;i<100000000000;i++)
    {
        j+=1;
    }
}
public class MAIN
{
    public static void main(String args[])
    {
        int j=0;
        for(int i=0;i<10000000000;i++)
        {
            j+=1;
        }
    }
}

        同样的一段代码,在C++禁止编译期计算的情况下很多时候不会对这段代码进行优化(gcc/MSVC/clang -o2)我没有intelC++这个不知道,但是Java会把这段代码展开并编译成机器语言执行(JIT)展开成如下形式

public class MAIN
{
    public static void main(String args[])          
	{
		int j=0;
        for(int i=0;i<10000000000;i=i+8)   //当然这也不是最优的优化方法,但是相比C++很多时候编译器不优化以及好很多了(这个优化涉及到了汇编层面的指令数量)
        {
            j+=1;
            j+=1;
            j+=1;
            j+=1;
            j+=1;
            j+=1;
            j+=1;
            j+=1;
	}

        那么C++的优势到底在哪,很简单,极致的可控制性,可以最好的在性能和内存之间做到平衡,当然这一点很多时候对程序员的要求特别高,作为一门静态编译无gc语言实际上C++的极限运行效率要高于Java以及C++的运行平稳性要好于Java,比如有个笑话,用C++写一个JVM然后在这个jvm上运行Java。Java的速度来自于什么地方,也不复杂,Java的速度来自于大量的信息与预先分配的内存,这也是为什么我们会发现Java对内存的占用堪称恐怖,实际上有大量的内存是未经使用的,Java预先从系统申请出来,作为自身的内存池使用,同时Java的运行与gc需要大量额外的空间支持,如果一旦出现空间不足的情况,Java会出现严重的停滞想象(JVM对内存进行重新分配和全局gc释放内存),同时对这个空间的优化是困难的,Java没有给程序员操作内存空间的自由,只能等待gc来选择性释放。如果一味的说内存条是不值钱的很多时候是对自己代码的不负责,一个对内存精细优化的代码和放任内存使用的代码可能对内存的占用会到十倍以上(尤其在微机),不是所有时候都可以加内存条,有些芯片总共成本就是一两元,甚至几分(一些一次性编程芯片),内存条的成本不是这些产品可以接受的。

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值