풀 객체 할당

7298 단어 대상
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는 모든 라인의 관리 구조,freeblock_size는 현재 관리 구조가 분배할 수 있는obj 를 기록합니다block의 수량, 분배 가능한 모든 objblock
free에 추가됨Blocks 중,recyBlocks에 분배 대상이 없는 obj 저장block.분배 과정은freeBlocks의 시계 머리 중 하나obj 획득block,
그리고 Obj 에서block의freelist에서 빈 대상을 꺼내서 분배합니다.하면, 만약, 만약...block의freelist가 비어있으면objblock은freeBlocks 총에 맞았어요.
내보내기, recy 에 추가Blocks 중.
이 디자인의 관건은que에 있다. 이것은 자물쇠가 없는 알고리즘으로 이루어진 창고이다. 여기서 창고를 사용하는 이유는 첫째, 우리는 방출의 순서에 주목할 필요가 없고 둘째, 자물쇠가 없는 창고의 실현이 가장 간단하다.
지출이 가장 적다.
방출 루트와 분배 루트가 같은 루트가 아니라면, 방출할 대상push를que에 넣고, 분배 루트가 다음 어느 시간에que에서 꺼낸 다음
다시 freelist에서 해제된 부분의 코드는 다음과 같습니다.
 
    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에 라인이 풀렸는지, 이 대상을 로컬로 회수하는freelist에서 로컬에 할당할 수 있는 대상이 있는지 다시 봅니다. 만약 호출되지 않았다면expand에서 메모리를 분배하고 새 대상을 만들고 를 호출합니다alloc 할당.
분배기의 전체 코드, 사용방식 참조asynserver
 

좋은 웹페이지 즐겨찾기