#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() :1Abc() :2Abc() :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
고정크기의 작은 블럭(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 |