'memory pool'에 해당되는 글 2건

  1. 2009.06.02 stack memory allocator
  2. 2008.08.25 메모리 풀링
2009. 6. 2. 12:01

stack memory allocator

// 이 코드는 구현의 예만을 보여주기 위한 것으로
// 실제 사용하기 위해서는 예외 처리나, 메모리 할당 등의 
// 추가적인 구현이 필요.
#include 
#include 

#define SAFE_FREE(buf) if( buf ){ free(buf); buf = 0; }
#define MEM_PTR		unsigned char*

class stack_allocator
{	
	void	_alloc_buffer(size_t stacksize)
	{
		_free_buffer();
		buffer_ = (MEM_PTR)malloc(stacksize);
		top_ = buffer_;
		bottom_ = top_+stacksize;
	}

	void	_free_buffer()
	{
		SAFE_FREE(buffer_);
	}
public:
	stack_allocator(size_t stacksize = 1024):buffer_(0), top_(0), bottom_(0)
	{
		_alloc_buffer(stacksize);
	}

	void*	alloc(size_t size)
	{
		MEM_PTR ret_ptr = top_;
		top_ = ret_ptr + size;
		if( top_ > bottom_ )
		{	// check overflow
			return 0;	//throw exception;
		}
		return ret_ptr;
	}
	
	friend	void* operator new( size_t size, stack_allocator& mem, int count);
	friend	class scoped_mem_stack;

	MEM_PTR	get_top()
	{
		return top_;
	}
protected:
	void	restore(MEM_PTR ptr)
	{
		top_ = ptr;
	}
private:

	MEM_PTR	buffer_;
	MEM_PTR	top_;
	MEM_PTR	bottom_;
};

void* operator new( size_t size, stack_allocator& allocator, int count = 1)
{
	return allocator.alloc(size*count);
};

class scoped_mem_stack
{
public:
	scoped_mem_stack(stack_allocator& allocator):allocator_(allocator)
	{
		backup_top_ = allocator_.get_top();
	}

	~scoped_mem_stack()
	{
		restore();
	}

	void	restore()
	{
		allocator_.restore(backup_top_);
	}
private:
	stack_allocator& allocator_;
	MEM_PTR			 backup_top_;
};

stack_allocator	g_stack_allocator;

struct test_struct
{
	char	buffer[100];
};

void main()
{
	scoped_mem_stack guard(g_stack_allocator);

	MEM_PTR back_ptr = g_stack_allocator.get_top();
	MEM_PTR back_ptr2 = back_ptr;
	assert(back_ptr == g_stack_allocator.get_top());

	void* test_ptr = new(g_stack_allocator) void*;
	back_ptr +=sizeof(void*);
	assert( back_ptr == g_stack_allocator.get_top());

	test_struct*	test_struct_ptr = new(g_stack_allocator) test_struct;
	back_ptr += sizeof(test_struct);
	assert( back_ptr == g_stack_allocator.get_top());

	test_struct**	test_struct_ptr_ptr = new(g_stack_allocator, 5) test_struct*;	
	back_ptr += sizeof(test_struct*)*5;
	assert( back_ptr == g_stack_allocator.get_top());
	guard.restore();

	assert(back_ptr2 == g_stack_allocator.get_top());
}


scoped_lock idiom 과 구현의 방법이 비슷해서 임의로 scoped_mem_stack 이라는 클래스를 사용해 보았다. 
stack을 사용하는 메모리 풀은 흔하게 볼수 있는 예는 아닐것이다. 

stack의 특성상 반드시 할당  받은 순서대로 해제가 되어야 하는 것은 번거로움은 제외 하고라도, 다중 쓰레딩 환경에서도 문제가  될수 있다.

그러나, 위의 구현 예제와 같이 local functin 내에서 일정크기 이상의 메모리 자원이 필요할때 아주 유용하게 사용될수 있을것으로 기대 되며, 매우 빠르다!!!

특히나, stack의 크기(stack allocator가 아닌)를 넘어가는 메모리 자원이 필요할때(게다가 특정 function내에서만 임시로 사용될때) 임시 메모리 버퍼로서 아주 톡톡한 역할을 해 줄수 있을것으로 기대 된다.
allocator의 크기만 유동적으로 조정이 가능하게 구현해 두면, 그냥 막 불러서 사용하면, 해당 function의 scope가 끝나는 시점에서 자동으로(빠르게!!!) 해제가 되니, 이 얼마나 편리하지 않을수가 있을까?

다중 쓰레드 환경에서는 TSS와 같이 사용하면 된다.
2008. 8. 25. 18:00

메모리 풀링

요즘 서버 모듈을 다시 만들어 볼려고 이것 저것 손대 보고 있는데, 오늘은 동적인 message buffer 생성을 위한 메모리 풀을 만들어 보고 테스트를 해 보았다.

결과는 아래와 같다.

테스트 환경:
컴파일러 : vc2008 express edition
PC : Pentium(R)4 CPU 3.00GHz, Ram 2G
운영체제 : XP sp2
테스트 내용 :
  1032BYTE 짜리 buffer, 4byte int 각각 5만개씩 생성
결과 :  
** message_buffer로 테스트 했을 때
미리 잡아 놓고 dequeue를 이용한 풀[message_buffer] : 0
기본 new/delete[message_buffer] : 0.156
boost::object_pool[message_buffer] : 142.844
boost::pool_allocator[message_buffer] : 141.203
boost::fast_pool_allocator[message_buffer] : 0.047
 
** int로 테스트 했을 때
미리 잡아 놓고 dequeue를 이용한 풀[int] : 0.016
기본 new/delete[int] : 0.032
boost::object_pool[int] : 3.875
boost::pool_allocator[int] : 3.89
boost::fast_pool_allocator[int] : 0

생각보다 object_pool 이나 pool_allocator가 성능이 신통치가 않다.(사실 사용법을 잘 몰라서 그런지도 모른다. 대충썼다 -_-)

미리 잡아 놓고 dequeue를 사용한 풀을 써볼려고 테스트를 만든건데, 위의 테스트는 버퍼를 미리 잡는 시간은 제외된 시간이다. 의외로 쓸만한거 같은데, 동기화 오브젝트를 넣고 하면, 얼마나 성능이 나올지는 좀더 테스트를 해 봐야 될거 같다.

아래는 소스 코드