ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • boost pool_allocator 분석
    프로그래밍/c++ 2016. 1. 14. 23:30
    반응형

    부스트 메모리 할당자


    헤더 


    #include <boost/pool/pool_alloc.hpp>


    특징


    스레드에 안전함

    rebind 지원

    가변 크기 지원

     

    pool_allocator

     

    vector 같은 연속적인 메모리를 가지는 자료형 할당에 적합한 할당자. 내부적으로 singleton_pool을 사용하고, singleton_pool은 pool을 전역적으로 사용할 수 있도록 만든 클래스


    pool_allocator::allocate 호출 시 다음과 같은 호출 과정을 거침




    pool::ordered_malloc 내부


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    template <typename UserAllocator>
    void * pool<UserAllocator>::ordered_malloc(const size_type n)
    //! Gets address of a chunk n, allocating new memory if not already available.
      //! \returns Address of chunk n if allocated ok.
      //! \returns 0 if not enough memory for n chunks.
     
      const size_type partition_size = alloc_size();
      const size_type total_req_size = n * requested_size;
      const size_type num_chunks = total_req_size / partition_size +
          ((total_req_size % partition_size) ? true : false);
     
      void * ret = store().malloc_n(num_chunks, partition_size); // sss에서 메모리를 할당을 시도.
     
    //… 이 아래는 ret이 0(0 or nullptr)일 때 처리하는 루틴
    }

    cs


    simple segregated storage malloc_n


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    template <typename SizeType>
    void * simple_segregated_storage<SizeType>::malloc_n(const size_type n,
        const size_type partition_size)
    {
       BOOST_POOL_VALIDATE_INTERNALS
      if(n == 0)
        return 0;
      void * start = &first; // 메모리 검색을 시작할 위치 설정(sss 노드의 첫번째)
      void * iter;
      do
      {
        if (nextof(start) == 0// start의 다음 노드가 없다면(nextof(start) == nullptr) 0 반환
          return 0;
     
       // start에서 할당을 시도함
        iter = try_malloc_n(start, n, partition_size);
      // try_malloc_n을 빠져나오면 start는 다음으로 검사할 노드가 된다.
      } while (iter == 0); // iter가 0이면 계속
      void * const ret = nextof(start);
      nextof(start) = nextof(iter);
      BOOST_POOL_VALIDATE_INTERNALS
      return ret;
    }
    cs
    \
    simple segregated storage try_malloc_n
    sada
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    template <typename SizeType>
    void * simple_segregated_storage<SizeType>::try_malloc_n(
        void * & start, size_type n, const size_type partition_size)
    {
      // iter = 검색할 시작 노드의 다음 노드
      void * iter = nextof(start); 
     
      // @begin
      // partition_size * n만큼 연속적인지 검사합니다.
      // 
      while (--!= 0)
      {
        void * next = nextof(iter); 
      
       //  필요한 만큼(n*partition_size) 연속적인지 검사
       // next == (char*)iter + partition_size라면 다음 청크가 이어져있다는 것
        if (next != static_cast<char *>(iter) + partition_size) 
        {// next가 없거나(nullptr, end of list) partition_size만큼 연속적이지 않다면
        // start를 다음에 검사할 지점으로 설정하고 반환
          start = iter; 
          return 0;
        }
        iter = next;
      }
      // @end
     
     // 위 테스트를 통과했다면 값을 담아서 반환
      return iter;
    }

    cs



    인상 깊었던 부분은 next != static_cast<char*>(iter) + partition_size

    내가 예전에 메모리 풀을 만들었을 땐 청크 노드의 앞 부분에 size를 써놨었는데, boost pool_allocator는 위의 저 연산을 통해 연속적인 메모리 여부를 검사한다. 


    다음 메모리 청크가 현재 메모리청크 + 파티션사이즈(바이트 단위로 이동한 주소!) 이면 직렬화된 메모리


    결론은 이런 구조의 메모리 리스트에서 적합한 메모리를 찾는다는 것



    적합한 메모리가 없으면 새롭게 할당한다.  여튼 메모리 할당 방식을 버디 메모리 할당이라고 함.



    pool에서 할당한 메모리를 해제할 때에도 저 메모리 리스트에서 삽입할 적절한 위치를 찾는 과정이 있는데,


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        // Same preconditions as 'segregate'
        // Post: !empty()
        void add_ordered_block(void * const block,
            const size_type nsz, const size_type npartition_sz)
        { //! add block (ordered into list)
          //! This (slower) version of add_block segregates the
          //!  block and merges its free list into our free list
          //!  in the proper order.
           BOOST_POOL_VALIDATE_INTERNALS
          // Find where "block" would go in the free list
          void * const loc = find_prev(block); // 해제할 메모리 블럭의 이전 위치를 찾음
     
          // Place either at beginning or in middle/end
          if (loc == 0)
            add_block(block, nsz, npartition_sz);
          else
            nextof(loc) = segregate(block, nsz, npartition_sz, nextof(loc));
          BOOST_POOL_VALIDATE_INTERNALS
        }
    cs


    이는 위 코드에서 11번 라인에서 확인할 수 있다.


    boost의 pool은 다른 메모리 풀 라이브러리와는 다르게 가변 크기를 지원한다길래 어떤식으로 되어있을까 찾아봤다.


    free list에서 연속적인 메모리가 없으면 새로 할당, 있으면 재사용.


    -끝-


    반응형
Designed by Tistory.