您的位置:首页 > 其它

从语句、函数这一级提高软件性能

2010-07-28 15:40 239 查看
从语句、函数这一级提高软件性能

作者: slowaction

我们能做的有限,不代表性能提升就有限,再不改变整体结构的情况下,我们也能做出惊人的成绩。下面分别从影响软件性能的各个角度分析一下提升性能的方法,
有的作用微乎其微,有的则效果显著,我的格言“蚂蚱也是肉“

1 ,减少机器指令数---当前主频下更有效的运算

cpu 主频是我们控制不了的,不过相关的我们能控制我们的语句复杂度,完成同样的功能,使用的机器指令可是有天壤之别。比如我们要一个hash
结构,hash 桶的大小是65535, 我们的key 是int 的,简单的做法就是int%65635 决定冲突链,殊不知这个%
可是慢得不得了,如果我们用int&0xffff, 效果是一样的,不过性能好很多。

hash key 对一个素数取模数的运算是很常见的,好多开源的程序也这么干,个人以为向2 的幂取整,然后按位运算要好。

2 ,cpu 字长

一般说来,32 位机做int32_t 或者更少 的运算最舒服,所以除非特别必要,不要再32 位机器作64 的运算,不用特别声明16 和8
长度的数据,16 位数据和32 位数据对总线的压力几乎一样的,64 位慢一倍

3 ,能不用系统调用就不用。

比如时间,尽量少用time之类的函数.用户态到内核态是由开销的,并且可能导致进程切换

4 ,能用指针的不要copy数据

Copy 数据是要申请总线的

5 ,用带缓冲的函数,不要动辄非阻塞

相信操作系统,他们能做得更好

6 ,需要多少空间申请多少。

不要总是 char buf[65536];

memset(buf,0x00,65535);

7, 不要破坏 流水线

志强的cpu 流水线是7 级,p4 是三级,从这个角度来说,志强确实比p4 彪悍。不过我们的程序是不是针对流水线作了优化呢?
流水线比较忌讳条件转移,尽量的发挥流水线的优势就要少一些if , 或者说把if
和运算分开,比如在函数入口一次判断完了所有的条件,比如空指针,长度不对等等。然后开始运算。不要随时使用随时判断。

likely 和unlikely 宏定义就是针对流水线的优化语句,编译器碰到likely 宏应该预读likely
分支的语句。这是我们给编译器的一点提示,至于人家领情不领情就不知道了。(只是希望如此,呵呵)。

8 ,const inline register

能const 的都给const 掉,你知道的一定要提醒编译器,立即数要比任何寻址要好很多。

Inline: 短小的函数inline. 注意inline 和register 是c
语言的建议性关键字,编译器自己决定怎么处理,所以不要再inline 中间写复杂的逻辑和循环,否则编译器不给你展开的。

Register: 建议性变量。用不用寄存器不是我们能决定的。我们把我们常用的变量声明成register 提醒一下就仁至义尽了。

9 ,进程通讯shm最快

Shm 的读写和读写自己的内存一样,远非文件之流的可比。Msg是可消耗资源,肯定有锁保护,效率就要差一点了。

10 ,大负荷的文件读取mmap

使用mmap 操作比传统的read 操作好处是减少了一次内核态到用户态的拷贝.

11, 不要分配4096的内存。

Malloc 和内核之间还有一个glibc,他要偷一点空间,如果malloc(4096)实际需要的空间比4096大,很可能申请两个页。

12 ,sched_setaffinity指定进程的cpu的亲密关系,减少cache miss

13 ,空间换时间

一般来说,上面带来的性能提升是很有限的,甚至根本看不出来,写一个likely
宏多半用来吓唬人。时间和空间不同了,这个可能带来成倍的提高。最近有个模块,要过滤不关心的端口,关心的端口不会超过100
个,并且是动态的添加删除的。 开始要用排序数组,二分发查找,我个人感觉插入删除略闲麻烦,更重要的是我感觉不够酷。

平衡二叉树如何,一堆指针,程序还不core 来core 去的。最后我用的方法是,声明一个65656
的数组,端口号作数组下标,关心的是0, 不关心的是-1,ok, 简单明了,想core 都困难,并且速度无出其右,当然我用了256k
内存,为什么是256k 不是64k 或者8k, 32 位机32 位最快么。这个是凑巧了,端口号在16 位以内,如果我们要过滤ip
地址怎么处理?考虑子网的情况,ip 地址的数量要远远超过端口,并且32 位的展开不太可能了。

其实考虑考虑实际环境,很少有0 位的掩码,16 位掩码已经很夸张了,一般是24 位,所以我们有了空间换时间的方案。按照我们关心的ip
地址的后16 位展开成65535 数组,子网展开成host 地址,判断ip
地址是不是我们关心的范围只要简单ip_array[ip&0xffff] 就ok
了。这比采用任何复杂的数据结构都要简单并且效率最好,就算有一些漏网之鱼也不足为患

如果用户非要设置 0.0.0.0 的掩码,那我们只能自认倒霉了。不过这就是小概率事件了。

14 ,深入理解malloc和free

注意保持正确的malloc和free的顺序,不要让内核建立很多的vma,不要让glibc私藏很多的内存,这个话题比较大,有机会我单独写一点资料。

按照栈的规则来申请释放内存一般比较合理。

共享内存,mmap之类的不要和malloc交叉

如果可能,请使用内存池。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: