eXile 的专栏

高性能服务器的多线程策略


(一)线程数量与线程池模型
  参见:high-performance server design. http://pl.atyp.us/content/tech/servers.html

  频繁的上下文切换,会导致系统性能严重下降,而产生过多的切换大致有两个原因:
  1)过多的线程数量。这会使系统性能呈指数级的下降。对于每连接一个线程的系统而言,这也是为什么性能会变差的原因。一个具有可伸缩性的系统,它的唯一可行的选择,就是限制运行线程的数量。一般而言,这个值应该小于或等于处理器的数目。(说明,在boost网络库asio提供的example中,有一个关于这种实现的很好的例子)
  2)所使用的线程池及事件模型。最简单的一种模型通常是这个样子:一个侦听线程异步地接收请求,并在队列中进行缓冲。另外一组工作者线程则负责处理这些请求。这是一个不错的模型,缺点就是每处理一个请求一般要经过两次线程切换(加上对请求的回复)。为了避免这种缺点,一个线程就必须具备侦听者和工作者两种角色,可以采用称之谓“领导者/跟随者”的模型。一个线程作为领导者,来进行监听,当收到请求时,它选出一个跟随者线程作为新的领导者进行侦听,自己则对请求进行处理,结束后,到跟随者队列中进行等待。

(二)多线程的内存池优化

  普通的内存池一旦应用到多线程中,都面临着锁竞争的问题,在stlport所做的对于字符串性能的测试中,当使用两个线程时,它所使用的内存池node_allocator性能已经出现明显的下降。所以对于多线程而言,一个线程一个内存池是一个很好的选择。要实现这种设计,面临的第一个问题,是内存块的跨线程使用问题,即一个内存块,可能在A线程中申请,但可能在B线程中释放。
  在GCC的STL实现libstdc++中,有一个多线程内存池的实现(mt_allocator)。它是node_allocator(现在叫pool_allocator) 的多线程版本。它还有一个优点就是所有参数都是可配置的。
  它的设计思路如下:每个线程一个内存池,同时还有一个全局的内存池。每个线程可以访问自己的内存池,同时可在锁保护下访问全局内存池。申请的每一个内存块中,都有一个线程ID,以标明是从哪个线程中申请的。
  申请过程:首先向所在线程申请,若本线程没有空闲块,则向全局内存池申请。
  释放过程:直接归还到本线程的空闲链中。还有一个问题,为了防止线程内存池之间的不均衡,或者某一个线程中的空闲链过长,可以设置一个水位标,当超过这个水位标时,把释放的内存直接归还到全局内存池中。

posted on 2008-03-06 00:09 eXile 阅读(6958) 评论(9)  编辑 收藏 引用 所属分类: 网络开发

评论

# re: 高性能服务器的多线程策略[未登录] 2008-03-06 08:30 cppexplore

顶 不错
内存申请针对线程内还是跨线程 决定是否采用加锁策略,为了差异化这种处理,在内存池上进行进一步的封装 不错!  回复  更多评论   

# re: 高性能服务器的多线程策略[未登录] 2008-03-06 14:42 cppexplore

第一部分(2)的结论不太对。
主线程侦听,预派生的线程处理业务,这个称为模型A吧.
leader/follow这里称为B.
我服务器的4核的SMP,linux 2.6内核,测试工具ab,小压力的测试不说了,都能达到17000左右,测试项如下:
ab -c 4000 -n 40000 http://172.24.252.248:5000/
预派生线程数量都是4。
压力测试的结果是:
业务逻辑简单的时候(仅仅是读数据,然后resonse200OK):
B模型多次测试平均的结果大约是每秒8500。
而A模型的性能和缓存队列的大小有关,当缓存大小取500时,和B模型性能相当。取1000,测试的平均结果大约是每秒9500。取100则降低为6500左右


猜测原因:linux的pthread_mutex_t既然是非暂停点的实现,那么它的性能一定很好,远好于条件锁、信号灯等。pthread_cond_t则是暂停点的实现,可能把线程推向睡眠。增大缓存大小,可以有效减少对pthread_cond_t的系统调用。

另根据对各种模型的测试,accept的处理速度非常非常的块,简单的业务处理时间也要比accept的处理低一个数量级。因此单一线程处理accept,可以尽快建立三次握手,进入缓存队列等待。

因此猜测 如果业务为复杂逻辑(实际测试加了4个循环相加,一句打印的log)的话,模型B的性能将进一步下降。增大业务处理复杂度后的结果果然如此,模型B的处理降低为6500左右,而模型A在1000的缓存下,每秒的处理能力还是9500左右。  回复  更多评论   

# re: 高性能服务器的多线程策略[未登录] 2008-03-06 15:14 cppexplore

说错了 呵呵 三次握手在accept前就完成了 从完成队列里取而已  回复  更多评论   

# re: 高性能服务器的多线程策略 2008-03-06 20:54 true

leader/followers模式不适合处理爆发  回复  更多评论   

# re: 高性能服务器的多线程策略 2008-03-06 22:36 eXile

@cppexplore
谢谢指正,不过cppexplore这么快就把测试模型搞出来,厉害,应该用的是什么库吧,Apache?
  回复  更多评论   

# re: 高性能服务器的多线程策略[未登录] 2008-03-07 08:16 cppexplore

@eXile
呵呵,还没用库。
是我最近正在梳理网络模型。这几天正在写各种模型的网络程序并测试性能。
等搞完这个再看各种库是如何实现这些模型以及其性能如何。  回复  更多评论   

# re: 高性能服务器的多线程策略[未登录] 2008-03-08 10:55 小白

领导者这个处理accept比较好。

服务器只要不用弱智算法,以及处理好内存和线程问题,就是高性能的服务器。

服务器虽然把网络底层作为心脏,把架构作为骨架,但是还有一个很大的部分,就是血肉,也就是服务器的逻辑处理。

很多人谈高性能服务器的时候,忽略了逻辑处理,只是在谈网络底层的问题,这是非常错误的。

网络底层,只要符合我上面说的那个高性能服务器的标准,就可以在真正的服务器硬件上跑得很好了。

接下来,我们需要面对的就是逻辑处理给我们的挑战。无论在哪个行业,都应该仔细谨慎的去对逻辑处理进行设计。

  回复  更多评论   

# re: 高性能服务器的多线程策略 2008-03-12 12:41 eXile

在现在gcc的mt_allocator实现中,有一个bug, 就是对一个计数器(_M_use)并没有采用原子计数,这个计数器主要用来控制空闲链的长度,所以它并不会导致程序异常,但是可能会影响到程序性能,不过如果采用原子计数,则有另一个性能上的担忧。这个问题直到最新的gcc4.3中才得到解决。  回复  更多评论   

# re: 高性能服务器的多线程策略 2011-08-29 19:40 cingoli

小白 说得非常好啊,我做网游服务器开发,着眼点也在这里。

不过,非本质复杂性 和 本质复杂性 都要处理好才好吧。无论性能还是复杂度。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

常用链接

留言簿(18)

随笔分类

随笔档案

服务器编程

搜索

最新评论

阅读排行榜

评论排行榜