zoukankan      html  css  js  c++  java
  • 池式对象分配

    有了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;//·ÖÅäÏ̵߳Äid
            char   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);
            }
            else
                lfstack_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);            
            }else
                return __alloc(_allo,pth);
            if(unlikely(dlist_empty(&pth->free_blocks)))
                __expand(_allo,pth);
            return __alloc(_allo,pth);
        }

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


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

     

  • 相关阅读:
    Spring中@Autowired注解、@Resource注解的区别 (zz)
    ECLIPSE使用HG插件去上载 GOOGLE.CODE下的代码
    jrebel
    myeclipse 上安装 Maven3
    web项目目录结构
    笔记 利用python进行数据分析
    算法浅入浅出之Textrank
    算法浅入浅出之TF-IDF
    python库之jieba小试牛刀 3
    python库之jieba小试牛刀 2
  • 原文地址:https://www.cnblogs.com/sniperHW/p/3575816.html
Copyright © 2011-2022 走看看