什么真正影响性能?

来源:互联网 发布:纪检监察网络舆情 编辑:程序博客网 时间:2024/04/29 15:59

话还得从我之前对Tcl的解释器做的一点小小改动说起。当时公司需要在产品中嵌入Tcl的解释引擎,但在工程中加入了Tcl的代码后,运行结束时却发现了大量的内存泄漏。因此习惯打杂的我被叫过去解决这个问题。仔细的阅读了Tcl内存管理的算法后,我发现Tcl的的内存使用方式的确是相当高效的。

Tcl内部有一个Bucket列表,每个Bucket包含一个可用内存块的链表,链表中所有内存块的大小是相同的。内存的申请与回收主要通过这种缓存完成。

我们以一次内存申请过程为例,假设要申请的内存大小为50,那么一次申请的过程如下:

1. 首先查找大小为64的Bucket中是否有可用内存块,如果有,则直接将该块取出返回。

2. 否则,从最大Bucket开始查找一块可用内存,将其切分为大小64的小块,这样大小为64的Bucket又有可用内存块了。

3. 如果在进行第二部的时候找不到更大的内存块,则向OS申请。申请的大小为最大Bucket中内存块的大小。

clip_image001

至于内存回收时,则直接将其放回大小与其对应的Bucket中。

这里大家也明白为什么我们在嵌入Tcl的时候会发现内存泄漏,因为Tcl对内存本来就是只吞不吐的,所有使用完毕的内存都被缓存了下来 – Tcl依赖Windows来做内存回收的。

不谈我们是怎么解决这个问题的,我想说的是,这种内存管理策略是相当有效的。

首先,由于所有向操作系统申请的内存都是固定大小的,因此可以大大减少内存碎片。

其次,多级的内存管理方式大大减少了堆查找所消耗的时间。

再次,对于常见的应用程序,开发人员所申请的内存的大小常常落在一定范围。因此当程序运行一段时间后,反复的内存申请,释放将会是不停的把一块内存放入,取出某个Bucket,如果内存管理是交给OS的,想想性能会差多少!

最后,在多线程的Tcl中,每个线程都会拥有自己的Cache,这可以大大降低malloc是lock全局堆所浪费的大把时间。

现在回到文章开头的观点,为什么有时候脚本语言会比C++快?让我们来这样想象一下,我们现在要开发一款坦克大战游戏(抱歉一时间想不出更好的例子了),里面会不断的创建子弹对象,敌人的坦克对象,各种效果对象,又不断的销毁,因此内存申请与回收的效率对运行速度非常重要。使用C++的程序员将直接使用操作系统提供的内存管理功能,而使用Tcl的程序员,虽然脑子里面思考的东西和使用C++的程序员差不多,却已经不知不觉中使用了经过更好优化打内存管理策略。因此使用Tcl写出来的程序很可能会跑得更快。

当然了,内存的管理只是Tcl所做的优化中的一种,因为我没有完全读过Tcl的代码,所以很难说Tcl还做了什么能大幅提高效率的优化。但我相信肯定还有不少我所未知的优化。

当然,你可以说Tcl也是用C写的。但问题是你自己用C写代码的时候会考虑这么多吗?你不会啊!

你还可以说用C也可以写出很robust的代码,但通常你嫌麻烦啊,或者也写的很累啊!

所以我认为:

1. 不必用C++写的代码,尽量不用C++,或者尽量少用。

2. 多学点各个层次的计算技巧,要比死抱着C++更能提高性能。

3. 多了解一下编译器如何优化,也要比在C++里面硬嵌汇编更能提高性能。(同样的道理,汇编不见得比C++快,有时候我看反汇编的代码时,发现编译器的优化能力令我发指!)

 

PS: 其实Tcl的内存管理策略中也有一点对对象池模式的使用。在C++代码中使用对象池是可以提高代码的性能的,但如果有人推而广之,在Tcl中也使用对象池,一则Tcl对内存管理所做的优化被浪费了,其次由于有两重的对象池,事实上代码的效率反而降低了。也有人质疑在有垃圾回收的系统中使用对象池并能否提高性能。总之,能否写出高性能的代码,关键在于对系统的理解。