工作中碰到一个内存分配导致的多线程server性能问题,网上翻到一个文档讨论多线程场景下的malloc性能——<malloc() Performance in a Multithreaded Linux Environment>,2000年的,有点旧,提到了多thread的时候,由于竞争单个堆分配器,导致malloc时间增长的问题。

写了小程序,使用同步模型,预分配固定个数的work thread,使用一种自定义的内存池(slab方式管理的内存池,当单次申请的内存大于4MB的时候,直接调用系统级的malloc)。循环1kw次的malloc模拟了下,统计每个线程的1kw总共耗时,结合文档中的Benchmark 1 Test的Result,有以下的几点可以供在平常开发多线程server时借鉴。

  1. work thread中尽可能的避免调用系统级的malloc,陷入内核态后等待时间很长。特别是在多线程场景下,由于竞争单个堆分配器,导致多线程下malloc巨慢无比。 (场景3):
  2. slab内存池使用要很小心,由于大于了预定义max_size后会调用系统级别的malloc,需要明确自己的使用场景会最大单次申请多大内存。如果某个场景下会经常超过max_size时,就特别要注意slab。测试场景2随机有1/1000的概率会超过4M。(场景2)。从结果看,线程越多,整个处理时间增长也很快,特别是当线程数超过Cpu的核数12后,增长迅速(猜测这个与malloc系统调用陷入内核态后,无法被随时抢占进行线程切换,导致处理时间变长,2.6后支持内核态抢占CONFIG_PREEMPT,但也不是任意时刻,线上机器编译参数好像也没开)。
  3. 场景1中1kw次malloc中,每个都小于4M,能够保证从slab内存池中分配,在线程增加的时候,性能没有明显变化,即使线程数超过了cpu核数,增加幅度也相对较小。结合server工作模型的特点(检索),一次query算一个task,完成后可以清理所有检索中allocate的object/memory,通过提供全局的nofree的一次性clear mempool来管理内存,有整体性能收益,也降低基础组件的设计实现难度。可以考虑在设计基础组件时,都是通过宿主程序统一传递内存池的方式。

结合测试结果,多线程server性能突增的场景原因的推测:绝大多数情况下,虽然我们开了N个work threads,但其实大部分都是在闲置cond_wait状态;当压力增大时,同时处在工作状态的线程上涨,多个线程竞争系统级malloc的概率增大,导致性能突增。

场景1 :  统计每个线程申请1kw次的时间,每次申请的大小都 < 4M。

image001

场景2 :  统计每个线程申请1kw次的时间,1/1000的概率申请的大小> 4M(调用系统malloc)。

image002

场景3:统计每个线程申请1kw次的时间,每次的申请大小> 4M(调用系统malloc)。

image003

转载请注明来源:Leoncom-《多线程模型下的malloc性能》
,
Trackback

no comment untill now

Add your comment now