4.2)代码调优。从我的经验上来说,代码上的调优有下面这几点:

  ● 字符串操作。这是费系统性能的事了,无论是strcpy, strcat还是strlen,需要注意的是字符串子串匹配。所以,能用整型好用整型。举几个例子,第一个例子是N年前做银行的时候,我的同事喜欢把日期存成字符串(如:2012-05-29 08:30:02),我勒个去,一个select where between语句相当耗时。另一个例子是,我以前有个同事把一些状态码用字符串来处理,他的理由是,这样可以在界面上直接显示,后来性能调优的时候,我把这些状态码全改成整型,然后用位操作查状态,因为有一个每秒钟被调用了150K次的函数里面有三处需要检查状态,经过改善以后,整个系统的性能上升了30%左右。还有一个例子是,我以前从事的某个产品编程规范中有一条是要在每个函数中把函数名定义出来,如:const char fname[]=”functionName()”, 这是为了好打日志,但是为什么不声明成 static类型的呢?

  ● 多线程调优。有人说,thread is evil,这个对于系统性能在某些时候是个问题。因为多线程瓶颈在于互斥和同步的锁上,以及线程上下文切换的成本,怎么样的少用锁或不用锁是根本(比如:多版本并发控制(MVCC)在分布式系统中的应用 中说的乐观锁可以解决性能问题),此外,还有读写锁也可以解决大多数是读操作的并发的性能问题。这里多说一点在C++中,我们可能会使用线程安全的智能指针AutoPtr或是别的一些容器,只要是线程安全的,其不管三七二十一都要上锁,上锁是个成本很高的操作,使用AutoPtr会让我们的系统性能下降得很快,如果你可以保证不会有线程并发问题,那么你应该不要用AutoPtr。我记得我上次我们同事去掉智能指针的引用计数,让系统性能提升了50%以上。对于Java对象的引用计数,如果我猜的没错的话,到处都是锁,所以,Java的性能问题一直是个问题。另外,线程不是越多越好,线程间的调度和上下文切换也是很夸张的事,尽可能的在一个线程里干,尽可能的不要同步线程。这会让你有很多的性能。

  ● 内存分配。不要小看程序的内存分配。malloc/realloc/calloc这样的系统调非常耗时,尤其是当内存出现碎片的时候。我以前的公司出过这样一个问题??在用户的站点上,我们的程序有不响应了,用GDB跟进去一看,系统hang在了malloc操作上,20秒都没有返回,重启一些系统好了。这是内存碎片的问题。这是为什么很多人抱怨STL有严重的内存碎片的问题,因为太多的小内存的分配释放了。有很多人会以为用内存池可以解决这个问题,但是实际上他们只是重新发明了Runtime-C或操作系统的内存管理机制,完全于事无补。当然解决内存碎片的问题还是通过内存池,具体来说是一系列不同尺寸的内存池(这个留给大家自己去思考)。当然,少进行动态内存分配是好的。说到内存池需要说一下池化技术。比如线程池,连接池等。池化技术对于一些短作业来说(如http服务) 相当相当的有效。这项技术可以减少链接建立,线程创建的开销,从而提高性能。

  ● 异步操作。我们知道Unix下的文件操作是有block和non-block的方式的,像有些系统调用也是block式的,如:Socket下的select,Windows下的WaitforObject之类的,如果我们的程序是同步操作,那么会非常影响性能,我们可以改成异步的,但是改成异步的方式会让你的程序变复杂。异步方式一般要通过队列,要注间队列的性能问题,另外,异步下的状态通知通常是个问题,比如消息事件通知方式,有callback方式,等,这些方式同样可能会影响你的性能。但是通常来说,异步操作会让性能的吞吐率有很大提升(Throughput),但是会牺牲系统的响应时间(latency)。这需要业务上支持。

  ● 语言和代码库。我们要熟悉语言以及所使用的函数库或类库的性能。比如:STL中的很多容器分配了内存后,那怕你删除元素,内存也不会回收,其会造成内存泄露的假像,并可能造成内存碎片问题。再如,STL某些容器的size()==0 和 empty()是不一样的,因为,size()是O(n)复杂度,empty()是O(1)的复杂度,这个要小心。Java中的JVM调优需要使用的这些参数:-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold,还需要注意JVM的GC,GC的霸气大家都知道,尤其是full GC(还整理内存碎片),他像“恐龙特级克赛号”一样,他运行的时候,整个世界的时间都停止了。

  4.3)网络调优

  关于网络调优,尤其是TCP Tuning(你可以以这两个关键词在网上找到很多文章),这里面有很多很多东西可以说。看看Linux下TCP/IP的那么多参数知道了(顺便说一下,你也许不喜欢Linux,但是你不能否认Linux给我们了很多可以进行内核调优的权力)。强烈建议大家看看《TCP/IP 详解 卷1:协议》这本书。我在这里只讲一些概念上的东西。

  A) TCP调优

  我们知道TCP链接是有很多开销的,一个是会占用文件描述符,另一个是会开缓存,一般来说一个系统可以支持的TCP链接数是有限的,我们需要清楚地认识到TCP链接对系统的开销是很大的。正是因为TCP是耗资源的,所以,很多攻击都是让你系统上出现大量的TCP链接,把你的系统资源耗尽。比如的SYNC Flood攻击。

  所以,我们要注意配置KeepAlive参数,这个参数的意思是定义一个时间,如果链接上没有数据传输,系统会在这个时间发一个包,如果没有收到回应,那么TCP认为链接断了,然后会把链接关闭,这样可以回收系统资源开销。(注:HTTP层上也有KeepAlive参数)对于像HTTP这样的短链接,设置一个1-2分钟的keepalive非常重要。这可以在一定程度上防止DoS攻击。有下面几个参数(下面这些参数的值仅供参考):
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 20
net.ipv4.tcp_fin_timeout = 30
  对于TCP的TIME_WAIT这个状态,主动关闭的一方进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),默认为4分钟,TIME_WAIT状态下的资源不能回收。有大量的TIME_WAIT链接的情况一般是在HTTP服务器上。对此,有两个参数需要注意,

net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1

  前者表示重用TIME_WAIT,后者表示回收TIME_WAIT的资源。