brpc源码学习- 内存池ObjectPool

Posted KIDGINBROOK

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了brpc源码学习- 内存池ObjectPool相关的知识,希望对你有一定的参考价值。

brpc为了优化在高并发场景下malloc和free的性能,引入了ObjectPool和ResourcePool,这两个类基本一样,只不过返回的一个是T*,一个是ID,所以接下来以ObjectPool为例分析一下

ObjectPool使用非常简单,假如有个自定义的无参数构造的类TestClass,那么创建一个TestClass实例的方法为TestClass* a = get_object<TestClass>(),归还则只需要return_object(a)

先介绍下ObjectPool中的Block和BlockGroup的概念,如上图,每个Block是一个连续的内存,大小为sizeof(T) * 256,即256个TestClass,BlockGroup里有65536个Block。

ObjectPool的整体逻辑如上图所示,思想类似tcmalloc,通过引入了tls以降低glibc malloc的全局竞争,因为malloc有锁

对于TestClass的ObjectPool,全局有个block_groups,即多个block_group,还有一个free_chunks;每个线程有个tls的LocalPool,LocalPool中有cur_blcok和cur_free;其中free_chunks和cur_free存的都是归还回来的TestClass

当申请一个TestClass的时候,首先看LocalPool的cur_free是否有空闲的,如果没有的话就去全局的free_chunks中尝试pop一个free_chunk,如果还没有的话就看LocalPool中的cur_block中是否有空闲,如果还没有的话就去全局的block_groups中申请一个block到LocalPool中,然后返回TestClass

当释放一个TestClass的时候,首先看Local_pool中的cur_free中空闲数是否超过256,如果没有超过则直接归还给cur_free,否则需要先将cur_free中全部还回全局的free_chunks,然后再归还cur_free

然后看下细节,Block如上所述即一块连续内存,可以存256个T的实例,BlockGroup即65536个Block*

然后是FreeChunk,其实就是存了NITEM个T*,对NITEM=0的柔性数组就叫DynamicFreeChunk

然后看下申请,接口为get_object,其中singleton()是TestClass类型的ObjectPool的单例,创建过程如下

首先会判断_singleton是否创建,没创建的话则创建一个,否则直接返回,这里用到了release-consume保证其他线程看到_singleton后,ObjectPool已经初始化好

然后调用ObjectPool的get_object

_local_pool是个tls,初始化为null,首先看_local_pool是否创建,若创建则直接返回,否则创建一个,并注册线程退出时析构_local_pool的函数

其中LocalPool的成员变量如下

然后看下LocalPool的get,其实就是下面这个函数

首先看cur_free中空闲数量,因为初始化是0,所以拿不到,再接着往下执行,去全局尝试拿一个free_chunk,

free_chunks初始化时是空,所以pop_free_chunk也会失败,当不为空时候,会拿到一个free_chunk,然后拷贝到LocalPool的cur_free中

接着往下看,然后就去cur_block中看有没有空闲,如果有则直接placement new即可,因为初始化的时候cur_block是null,因此也拿不到

此时就会去全局新建一个Block赋给cur_block,然后从cur_block里创建TestClass返回

首先创建一个Block,然后看最后的BlockGroup g(即_block_groups[_ngroup]),如果g中容量未满,则将该Block插入g中返回新建的Block,否则创建一个新的BlockGroup并插入到_block_groups中

值得注意的是这里的并发问题,_block_group_mutex这个锁是为了解决多个线程并发执行add_block_group;347行的release是为了和describe_objects的consume配对,保证看到blocks[block_index]时block已经初始化完毕;340行的acquire是为了和377行release配对,保证add_block_group在ngroup位置写入的bg能被add_block的342行读到;而376行的release和342的consume配对,保证读到的BlockGroup g已经初始化完成

最后看下return_object,如果LocalPool的cur_free没有满,那么直接写入cur_free即可,否则需要先调用push_free_chunk将cur_free返回到全局,然后再写入cur_free

以上是关于brpc源码学习- 内存池ObjectPool的主要内容,如果未能解决你的问题,请参考以下文章

brpc源码学习- 内存池ObjectPool

brpc源码学习- RDMA通信

brpc源码学习- RDMA通信

ObjectPool 对象池设计模式

对象池ObjectPool的简单实现

从ObjectPool到CAS指令