반응형



필요한 헤더:
<boost/pool/pool.hpp>,
<boost/pool/object_pool.hpp>,
<boost/pool/singleton_pool.hpp> 등. 사용한다template에 따라.
기능:
고정 사이즈의 고속 메모리 할당

#include <iostream>
#include <boost/pool/pool.hpp>
#include <boost/pool/object_pool.hpp>
using namespace std;


struct Abc
{
  Abc()  { static int no=0; cout << "Abc() :" << (m_no=++no) << endl; }
  ~Abc() { cout << "~Abc():" << m_no << endl; }
  int m_no;
};

int main()
{
  {
    // sizeof(int)아르바이트 전용 allocater
    boost::pool<> p( sizeof(int) );
    int* x = (int*)p.malloc();
    int* y = (int*)p.malloc();
    int* z = (int*)p.malloc();
    *x = 10;
    p.free(z); // 명시적으로free

    // free하지 않고라고도pool오브젝트의 수명과 동시에 해방된다
  }

  {
    // Abc전용 allocater
    boost::object_pool<Abc> p;
    Abc* x = p.construct();
    Abc* y = p.construct();
    Abc* z = p.construct();
    p.destroy(y);// 명시적으로 소멸자 호출

    // destroy하지 않고라고도pool오브젝트의 수명과 동시에 해방된다
  }

  return 0;
}

출력
Abc() :1
Abc() :2
Abc() :3
~Abc():2
~Abc():1
~Abc():3



http://cafe.naver.com/cafec/271959


일단 우리가 쓸 범용적인 메모리 풀은
boost/pool/object_pool.hpp 에 있습니다.
템플릿을 통해 객체를 받고 자동으로 생성자와 소멸자도 호출해줍니다.

예제1
class Something
{
    int v;
public:
    Something(){v=rand()%100+1;cout<<"생성"<<v<<endl; }
    ~Something(){cout<<"소멸"<<v<<endl;}
    void Print(void){cout<<v<<endl;}
};


int main(void)
{
    boost::object_pool<Something> memory_pool(10); //이 시점에서 Something 500개가 할당되지 않습니다.
    Something* trash = memory_pool.construct(); // 이 시점에서 할당됩니다.
    for(int i=0;i<5;i++)
    {
        Something* val = memory_pool.construct();
        val->Print();
        memory_pool.destroy(val);
    }
    return 0;
}
일단 construct멤버함수로 메모리풀에 있는 자원을 쓸 수 있습니다.
destroy로 해제 할 수 있습니다. 
object_pool 객체는 메모리풀이 생성될때 해당 갯수의 메모리를 할당하지 않고, 한번 construct할 때 할당한다고 합니다.
그래서 아무거나 한번 생성했습니다.
프로그램 돌려보시면 아시겠지만, 맨 마지막에 trash가 해제됩니다. 프로그래머가 해제를 하지 않아도 construct해준것은 destory해줍니다.

두번째 예제에서는 실행시간 비교...를 해보겠습니다.
시간측정을 위해서 #include <boost/chrono.hpp> 를 해줍시다.

    auto start_mempool = boost::chrono::system_clock::now();
    boost::object_pool<Something> memory_pool(10000); //이 시점에서 Something 500개가 할당되지 않습니다.
    Something* trash = memory_pool.construct(); // 이 시점에서 할당됩니다.
    for(int i=0;i<10000;i++)
    {
        Something* val = memory_pool.construct();
        val->Print();
        memory_pool.destroy(val);
    }
    auto end_mempool = boost::chrono::system_clock::now();
    auto duration = end_mempool - start_mempool;
    

    auto start_alloc = boost::chrono::system_clock::now();
    for(int i=0;i<10000;i++)
    {
        Something* val = new Something;
        val->Print();
        delete val;
    }
    auto end_alloc = boost::chrono::system_clock::now();
    auto duration_alloc = end_alloc - start_alloc;

    cout<<"메모리풀"<<duration<<endl;
    cout<<"무식하게 할당"<<duration_alloc<<endl;
    system("pause");
    return 0;




메모리풀 : 약 31초
무식하게 할당 : 약 35초

더 많이 돌리면 차이가 더 나겠죠.






http://blog.naver.com/sorkelf/40135434878



Pool.cpp



고정크기의 작은 블럭(chunk)들을 할당할때 고속으로 작동하는 메모리 할당 관리자

로써 메모리사용시 속도문제와 단편화 문제를 해결하기위해 사용되는 라이브러리이다.

현재 할당된 크기의 2배씩 커지는 특징이 있다


boost::pool  <- 기본 Pool 인터페이스


boost::object_pool  <- 객체 기반의 Pool 인터페이스


boost::singleton_pool <- 정적 기반의 thread safe singleton으로서의 기본 Pool 인터페이스


boost::pool_alloc     <-  singleton_pool에 기초를 둔 표준 allocator 인터페이스 


예제)








결과)





boost의 share_ptr을 사용할때 일반적으로


typedef boost::shared_ptr<Myclass> SharedPtr;

SharedPtr ptr  = SharedPtr(new Myclass); 


위와 같은 방식의 힙의 동적 메모리 할당을 사용하나


new와 delete를 자주 하는 경우에는 메모리 단편화를 몰고 오고 


단편화들로 인한 캐쉬히트들을 떨어뜨리며 그것은 즉슨 


시스템의 퍼포먼스를 떨어트리는 결과를 가져오게 된다


이럴 때 boost::pool을 사용하면 상당히 좋다 -ㅅ-


특히 릴리즈모드에서 탁월한 효과를 볼수 있다.

[출처] boost::pool|작성자 풍풍풍










http://blog.naver.com/sorkelf/40154614484


std::allocator VS boost::fast_pool_allcator (pool_allcator)






boost::pool 관련된 내용을 찾아 돌아다니던 중..( 왜 돌아다녔지..-_-)


pool_allocator - 메모리 공간을 특정영역 만큼 할당에서 특정 메모리영역 에서 데이터를

insert 및 delete 처리.. vector에 사용


fast_pool_allocator - 메모리 공간을 할당하지 않으나 이전에 메모리를 할당 하였을 경우

이전 메모리 주소의 근처에 있는 메모리 주소를 할당한다 list, set, map에 사용


내용이 있어얼마나 많이 빨라지나 테스트를 해본 결과...


list에서는 fast_pool_allocator를 쓰면 나름 속도가 빨라지지만

set은 거기서 거기였고.. vector는 처음 할당할때 느려졌다 (reserve 하면 그나마 제 속도...)


아래는 테스트 코드이다..

(AMD Cpu일 경우 시간이 잘 안맞는다는 이야기가 있어서.. ::timeGetTime()으로 대체..)


#include <iostream>
#include <Windows.h>
#include <list>
#include <vector>
#include <set>
#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered_set.hpp>
#include <MMSystem.h>
#pragma comment(lib,"winmm.lib")

using namespace std;

//#define _USE_NVIDIA //AMD일 경우 시간이 약간 안맞는다..
#define _USE_BOOST_POOL         //BOOST_POOL 사용
#define _USE_NULL_MUTEX         //Null Mutex 사용
//#define _USE_HASH_SET //boost/unordered_set 사용


#ifdef _USE_NVIDIA
    double pcFreq = 0.0;
    __int64 CounterStart = 0;
#else
    DWORD startTime;
    DWORD endTime;
#endif

#ifdef _USE_BOOST_POOL

        #ifdef _USE_NULL_MUTEX
            typedef std::list<int, boost::fast_pool_allocator <int, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex> > BOOST_POOL_LIST;

            typedef std::vector<int, boost::pool_allocator <int, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex> > BOOST_POOL_VECTOR;

            typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
                boost::fast_pool_allocator<int, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex> > BOOST_POOL_SET;
    #else
        typedef std::list<int, boost::fast_pool_allocator <int, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex> > BOOST_POOL_LIST;

        typedef std::vector<int, boost::pool_allocator <int, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex> > BOOST_POOL_VECTOR;

        typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
            boost::fast_pool_allocator<int, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex> > BOOST_POOL_SET;
    #endif
#endif

void StartCounter();
double GetCounter();

#define TEST_TIME_NUMBER 1000
#define TEST_ITERATOR_NUMBER 80000

int main()
{
    #ifdef _USE_BOOST_POOL
        cout << "------- USE_BOOST_POOL -------" <<endl;

        #ifdef _USE_NULL_MUTEX
                cout << "------- USE_NULL_MUTEX -------" <<endl;
        #else
                cout << "------- USE_DEFAULT_MUTEX-------" <<endl;
        #endif
    #else
        cout << "------- std::list -------" <<endl;
        cout << "------- std::vector -------" <<endl;
        #ifdef _USE_HASH_SET
            cout << "------- boost::unordered_set -------" <<endl;
        #else
            cout << "------- std::set-------" <<endl;
        #endif
    #endif

    cout << endl;

    //List
    {
        #ifdef _USE_BOOST_POOL
                BOOST_POOL_LIST testContainer;
        #else
                std::list<int> testContainer;
        #endif

        StartCounter();

        for(int iTestTime = 0; iTestTime< TEST_TIME_NUMBER; ++iTestTime)
        {
            for(int iIterateConut = 0; iIterateConut< TEST_ITERATOR_NUMBER; ++iIterateConut)
            {
                testContainer.push_back(iIterateConut);
            }

            for(int iIterateConut = 0; iIterateConut< TEST_ITERATOR_NUMBER; ++iIterateConut)
            {
                testContainer.pop_back();
            }
        }

        //결과 출력
        cout << "list(Push, Pop) : " << GetCounter() <<endl;
    }

    //Vector
    {
        #ifdef _USE_BOOST_POOL
                BOOST_POOL_VECTOR testContainer;
        #else
                std::vector<int> testContainer;
        #endif

        testContainer.reserve(TEST_TIME_NUMBER* TEST_ITERATOR_NUMBER);

        StartCounter();

        for(int iTestTime = 0; iTestTime< TEST_TIME_NUMBER; ++iTestTime)
        {
            for(int iIterateConut = 0; iIterateConut< TEST_ITERATOR_NUMBER; ++iIterateConut)
            {
                testContainer.push_back(iIterateConut);
            }

            for(int iIterateConut = 0; iIterateConut< TEST_ITERATOR_NUMBER; ++iIterateConut)
            {
                testContainer.pop_back();
            }
        }

        //결과 출력
        cout << "vector(Push, Pop) : " << GetCounter() <<endl;

    }

    //Set

    {
        #ifdef _USE_BOOST_POOL
                BOOST_POOL_SET testContainer;
        #else
            #ifdef _USE_HASH_SET
                boost::unordered_set<int> testContainer;
            #else
                std::set<int> testContainer;
            #endif
        #endif

        StartCounter();

        for(int iTestTime = 0; iTestTime< TEST_TIME_NUMBER; ++iTestTime)
        {
            for(int iIterateConut = 0; iIterateConut< TEST_ITERATOR_NUMBER; ++iIterateConut)
            {
                testContainer.insert(iIterateConut);
            }

            for(int iIterateConut = 0; iIterateConut< TEST_ITERATOR_NUMBER; ++iIterateConut)
            {
                testContainer.erase(iIterateConut);
            }
        }

        //결과 출력
        cout << "set(Insert, erase) : " << GetCounter() <<endl;
    }

    return 0;
}

double GetCounter()
{
    #ifdef _USE_NVIDIA
        LARGE_INTEGER li;

        QueryPerformanceCounter(&li);

        return double(li.QuadPart - CounterStart)/pcFreq;

    #else

        endTime = ::timeGetTime();

        return double(endTime - startTime)*0.001;

    #endif
}

void StartCounter()
{

    #ifdef _USE_NVIDIA
        LARGE_INTEGER li;
        if(!QueryPerformanceCounter(&li))
            cout <<"QueryPerformance Fail;" <<endl;

        pcFreq = double(li.QuadPart)/100000.0;

        QueryPerformanceCounter(&li);

        CounterStart = li.QuadPart;
    #else
        startTime = ::timeGetTime();

    #endif

}



캐시를 비우고 해야겠지만..

저정도만 봐도 충분히 속도의 비교는 되리라고 생각한다..




일반 std::container




Unordered_Set 사용



boost::pool::details::default_mutex 사용




boost::pool::details::null_mutex 사용



vector.reserve로 할당 했을때


확실히 list와 set은 (아마 map도) fast_pool_allocator를 사용하였을때의 속도 향상이 있으나

vector는 그렇지 못하다.. ( 왜 그럴까..-_-)


null_mutex 와 default_mutex 구간에도 차이가 있다


이는 singleton_pool 이 (pool_allocator, fast_pool_allocator 도 내부적으로 singleton_pool 을 사용)

Thread-Safty을 보장하는 특성이 있기 때문이다.


따라서 default_mutex 는 Thread-Safty를 보장하지만  null_mutex  는 그렇지않아

락을 걸고 푸는 함수 부분이 빠져  null_mutex  가 빠른 성능을 낸다


하지만 싱글 스레드로 돌리지 않는 이상 안전성을 보장 할 수 없으므로

만약 Thread-Safty가 요구되지 않는 상황이라면  null_mutex  로 설정하는 것이 속도 향상에 도움이 될 것이다.





참고자료:


boost 폴더에 \boost\boost_1_47\libs\pool\test

http://www.boost.org/doc/libs/1_48_0/libs/pool/doc/html/index.html

http://www.gpgstudy.com/forum/viewtopic.php?postdays=0&postorder=asc&start=40&t=20337

http://godsfly.blog.me/130125905050

http://northwind.springnote.com/pages/625623

http://jacking75.cafe24.com/Boost/Pool/conception.htm

http://www.gpgstudy.com/forum/viewtopic.php?highlight=&postdays=0&postorder=asc&start=80&t=20337

반응형

'메타프로그래밍 > Boost::' 카테고리의 다른 글

xml_parser.hpp, json_parser.hpp  (0) 2014.06.23
boost::intrusive_ptr  (0) 2013.05.13
boost::dynamic_bitset  (0) 2013.03.08
boost::boost::unordered_map, unordered_set  (0) 2013.03.02
boost::bind, boost::mem_fn, boost::ref 조합  (0) 2013.03.01

+ Recent posts