系统性能调优经验

在开发对外提供服务的模块的时候,系统的性能常常会是令咱们头疼的问题,具体系统性能的定义与瓶颈的定位方法,能够参考陈皓的这篇文章:html

性能调优攻略http://coolshell.cn/articles/7490.html 大牛的这篇文章仍是很全面的。linux

下面我会以咱们的一些工程经验和曾经遇到过的问题,来实例化一些系统性能调优的经验。算法

 

Ø  代码级别shell

1.       减小数据copy缓存

1)使用指针,代替静态数据copy服务器

如下面的数据结构为例:网络

struct goods_info_for_rank_t数据结构

{多线程

    good_info_ptrp_goods_info;//静态数据,只可读不可写!!!架构

    ….//省略若干

   //策略相关的字段

   float doc_weight;

   float goods_weight;

   };

之前这个goods_info_for_rank_t 进行内容赋值的时候要从p_goods_info 拷贝所有的数据到goods_info_for_rank结构,其实不少静态数据只是可读的而且数据量很大,当时优化的一个思路是去掉这个结构体,只使用good_info_ptr这个结构,可是忽略了这个结构体是静态只读,而且多线程并发使用的,从而使得多线程状况下出现core dump(修改后须要写,致使多线程同时写一份good_info_ptr数据),后来才用这个方案优化了这段程序性能;

 

2)使用引用代替栈上数据;

这个是常识性的知识,即当咱们传入一个大结构体或者类的时候,尽可能使用引用或者经过指针访问,不然栈上变量进行大内存申请将是很大的性能损耗。

 

3)返回指针,减小数据的COPY,对比下面的两个函数:

void read(uint32 id, void *brief)

{

   if(id >= _num)

       return;

   memcpy(brief, _array_mem + id * _size, _size);

}

 

void read_no_copy(uint32 id)

{

    if(id>= _num)

       return NULL;

   return _array_mem + id * _size;

}

若是数据只读,后面的函数减小一次内存copy效率更高;

2.       内存申请

1)减小没必要要的malloc

堆是多线程共享的,频繁的malloc会致使系统申请内存速度的降低,因此肯定的大块内存,咱们的建议都是在线程空间预先申请好,而不是每次都申请内存。

对多线程频繁malloc 内存消耗的解释:

堆是进程内共享的, mallocfree的原理,大概是维护一个空闲内存块的hash表:

1.     malloc时直接从这里去,若是没合适块,还要作一些分裂;

2.     free时,视时机作一些内存块合并,并挂接到hash表中;

在多线程状况下,若是mallocfree太频繁,线程间确定须要同步,并且分裂和合并等操做也会更多,也是开销。

使用tcmalloc库,能够优化malloc内存效率,一些测试的结果是在多线程下能够优化内存申请效率30%,tcmalloc源码

http://gperftools.googlecode.com/svn/trunk/

也可使用本身实现的内存池技术来优化内存申请。

2)当心的memset

仍是继续说前面的问题,当咱们在线程里面预先申请大块内存后,当一个请求处理完成再进入另一次请求时,咱们天然的想到要用memset清理内存,可是这个内存空间若是很大,memset这个内存就会很是的慢,曾经的一个项目里面因为memset 一个10M的内存,使得系统性能降低一半。

避免memset的一个方案是经过一个长度变量控制内存的读取长度。

3)谨慎使用局部变量

局部变量真”好”,用的时候随便申请一个,等做用域失效后自动释放。但方便的同时也极可能引入问题,这个问题和1.2比较相似,linux系统的线程栈空间有限能够经过ulimit –s查询,查看咱们系统的限制为10K(我写了一个程序,发现超过2M的时候会core dump),因此当声明一个较大的局部变量的时候,就会出现系统运行速度减慢,甚至会出现core dump。

3.       减小系统调用

对于程序的时间开销咱们应该有这样的认识:

 

   因此当进行频繁的系统调用的时候对系统性能会有影响,在跟进检索请求超时的问题中,发现每召回一个obj都要进行次过滤并调用time()函数看是否过时,致使请求时间超长。后面修改成一次请求只获取time一次,则修复超时问题。在咱们调试性能问题的时候也常常在系统加入不少获取时间的系统调用,自己这些系统调用也会影响到性能,从而使性能测试结果不许。

 

Ø  网络级别

1)使用NODELAY参数

对于咱们的网络服务器,咱们都是但愿发出的包马上可以发送给对方,因此通常设置NODEAL参数,以防止nagle算法对数据报进行缓存。

有关nagle算法:

http://en.wikipedia.org/wiki/Nagle's_algorithm

2)长链接与短链接

对于内部服务器间的通讯,若是能够咱们都建议使用长链接,以提升响应的速度;

3)异常数据包的处理

通常当下游服务器接受到一个异常的数据包的时候,咱们可能会简单的丢弃它,这样可能引起一些问题。好比:一个同窗在升级下游服务的时候将异常的报丢弃掉,而且没有关闭链接,致使上游服务器一直等下游回应,而hang住整个系统,由于你没法知道上游服务器的处理逻辑,若是它设计的很挫,可能对等待这个响应报好久才超时;另外当异常报比较多的状况下,咱们若是reset链接,也会影响到整个系统的响应时间,由于上游服务器要从新创建链接。(上游最好不要把错误的包传到下游,由于那可能引起下游的报警或者异常)

Ø  架构级别

1)并发访问

当咱们有些请求要发送给多个下游服务器,而这些请求又是独立的时候,能够采用并发访问。好比一个检索系统里面咱们同时可能要获取几个检索分类的结果,这个时候咱们就可能用到并发访问(网络模型、select、epoll奔腾而过)。

2)Cache使用

Cache是提高系统性能的经常使用方法,若是检索系统中有些query就是比较慢,一个直观的方法就是使用Cache,咱们可使用结果缓存,甚至能够采用预充的手段,将长响应query预充到缓存区;好吧,咱们又必须面临缓存区脏了的问题。

3)空间换时间&线下计算

若是有些数据能够在线下计算,那尽可能在线上完成,但通常会增长些系统的复杂度和空间的占用。咱们的一个项目之前要实时计算每一个站点的结果数(召回站点结果真后计数),后面把每一个站点的结果数建到索引里面,性能提高30倍。

Ø  编译优化

 

    -O2是推荐的优化等级,也是咱们程序使用的优化等级。

相关文章
相关标签/搜索