• 注册
当前位置:1313e > 默认分类 >正文

池式对象分配

有了tcmalloc和jemalloc,在大多数情况下我们都没有必要再自己写通用的内存分配器(应该说对于极大多数的程序员,都不可能写出比这

个两个更好的通用内存分配器)。但是,如果对性能有极致的要求,写一个比通用内存分配器效率更高的池式对象分配器是可能的。

一个最简单也高效的实现就是freelist,每次分配的时候从freelist中get一个,释放的时候put回去就好了。其实现在单线程下是相当简单的,

也就几十行代码。但是在多线程的环境下,问题稍微复杂一点,因为可能有多个线程需要操作freelist,那么就要用锁去保护这个freelist,每次

get和put的时候都要加锁显然会导致freelist的操作效率低下.

我在 block_obj_allocator 中利使用了线程本地存储来减

少锁的使用,其核心思想是,每个线程都有一个本地的freelist,get和put操作的都是本地的freelist,当本地freelist为空时,向一个全局的freelist

,请求一大块的内存,这个时候需要锁操作。在执行put的时候,如果收集了一定数量的对象,就一次性将一定数量的对象返还给全局freelist,

这个时候也需要锁操作。下面提出另外一种实现方式,这个实现要更简单,其核心思想就是对象由谁分配就由谁负责释放。

    struct obj_block{struct dnode node;struct llist freelist;lockfree_stack_t que;pthread_t       thdid;//·ÖÅäÏ̵߳Äidchar   buf[0];};struct obj_slot{struct lnode node;struct obj_block *block;char buf[0];};

obj_block是一个内存块管理器,当对象池中没有对象时,就分配一个obj_block,然后将buf中的内存分成一个个单独的obj_slot

添加到obj_block的freelist中.obj_block中记录了分配线程的线程id,保存在thdid变量中.

    struct pth_allocator{lockfree_stack que;uint32_t free_block_size;struct dlist free_blocks;struct dlist recy_blocks;};struct obj_allocator{struct allocator base;uint32_t alloc_size;uint32_t objsize;uint16_t tls_type;pthread_key_t pkey;};

pth_allocator是一个每线程的管理结构,free_block_size记录了当前管理结构可供分配的obj_block的数量,所有可供分配的obj_block

被添加到free_blocks中,而recy_blocks中保存了已经没有可分配对象的obj_block.分配过程是从free_blocks的表头中获得一个obj_block,

然后从obj_block的freelist中弹出一个空闲的对象,分配出去. 如果分配之后obj_block的freelist为空,则将obj_block从free_blocks中弹

出,添加到recy_blocks中.

这个设计的关键在que,它是一个无锁算法实现的栈,这里之所以使用栈,首先,我们不需要关注释放的顺序,其次无锁栈的实现最简单,

开销最小.

如果释放线程与分配线程不是同一个线程,则将要释放的对象push到que中,由分配线程在以后的某个时间里从que中取出,然后

放回到free_list中,释放部分的代码如下:

 

    void obj_dealloc(struct allocator *allo ,void *ptr){obj_allocator_t _allo = (obj_allocator_t)allo;struct obj_slot *obj = (struct obj_slot*)((char*)ptr - sizeof(struct obj_slot));    if(obj->block->thdid == pthread_self()){struct pth_allocator *pth = (struct pth_allocator*)pthread_getspecific(_allo->pkey);;if(unlikely(!pth))abort();__dealloc(_allo,pth,obj);}elselfstack_push(obj->block->que,(struct lnode*)obj);}

再来看下分配的实现:

    void* obj_alloc(struct allocator *allo,int32_t size){obj_allocator_t _allo = (obj_allocator_t)allo;struct pth_allocator *pth = (struct pth_allocator*)pthread_getspecific(_allo->pkey);if(unlikely(!pth)){pth = new_pth(_allo);pthread_setspecific(_allo->pkey,pth);}if(unlikely(dlist_empty(&pth->free_blocks))){struct lnode *n;while((n = lfstack_pop(&pth->que)) != NULL)__dealloc(_allo,pth,(struct obj_slot*)n);            }elsereturn __alloc(_allo,pth);if(unlikely(dlist_empty(&pth->free_blocks)))__expand(_allo,pth);return __alloc(_allo,pth);}

首先看下本地是否有可供分配的对象,如果有直接调用__alloc分配,如果本地没有则看下que里有没有其线程释放的对,如果有将这些对象回收到本地
的free_list中,接着再次查看本地是否有可供分配的对象,如果还是没有调用__expand分配一块内存,创建新的对象,然后再调用__alloc分配.


分配器的完整代码, 使用方式参看asynserver

 

 

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 162202241@qq.com 举报,一经查实,本站将立刻删除。

最新评论

欢迎您发表评论:

请登录之后再进行评论

登录
相关推荐