반응형

이글을 먼저 보신 후 보시면 되겠습니다

 

https://3dmpengines.tistory.com/2233

 

MemoryPool

메모리 풀의 전체 구조는 아래와 같습니다 컨셉은 다양한 크기의 메모리를 메모리 풀화시켜서 사용 하는 방식입니다 주요 클래스는 다음과 같습니다 오른쪽 하단 부터 위쪽으로 화살표 방향대

3dmpengines.tistory.com

 

위 Memory Poll 에서 ms 로 변경 처리를 하면 다음 처럼 바뀝니다 (주석에 설명이 들어 있음)

왠만하면 기본제공해 주는걸 사용하는게 좋습니다

 

 

 

 

 

MeoryHeader 부분

 : 16바이트 단위로 메모리를 정렬, SLIST_ENTRY 을 상속 받으면 메모리가 SLIST_ENTRY 이 먼저 배치됨
    그래서 next 포인터가 먼저 존재하게 됨

    그리고 나머지들도 16바이트 단위로 메모리로 정렬

#pragma once

enum
{
	SLIST_ALIGNMENT = 16
};

//16바이트 단위로 메모리를 정렬, SLIST_ENTRY 을 상속 받으면 메모리가 SLIST_ENTRY 이 먼저 배치됨
//그래서 next 포인터가 먼저 존재하게 됨
DECLSPEC_ALIGN(SLIST_ALIGNMENT)
struct MemoryHeader : SLIST_ENTRY
{
	// [MemoryHeader][Data]
	MemoryHeader(int32 size) : allocSize(size) { }

	static void* AttachHeader(MemoryHeader* header, int32 size)
	{
		new(header)MemoryHeader(size); // placement new
		return reinterpret_cast<void*>(++header);
	}

	static MemoryHeader* DetachHeader(void* ptr)
	{
		MemoryHeader* header = reinterpret_cast<MemoryHeader*>(ptr) - 1;
		return header;
	}

	int32 allocSize;
	// TODO : 필요한 추가 정보
};



//16바이트 단위로 메모리를 정렬
DECLSPEC_ALIGN(SLIST_ALIGNMENT)
class MemoryPool
{
public:
	MemoryPool(int32 allocSize);
	~MemoryPool();

	void			Push(MemoryHeader* ptr);
	MemoryHeader*	Pop();

private:

	//lock 과 queue 를제거하고 SLIST_HEADER 를 추가하여 queue를 대체한다
	SLIST_HEADER _header;

	int32 _allocSize = 0;
	atomic<int32> _allocCount = 0;

	//USE_LOCK;
	//queue<MemoryHeader*> _queue;
};

 

 

cpp 부분

 

 

기존 push 와 pop 을 ms 함수로 대체한 코드입니다

동작은 동일합니다

#include "pch.h"
#include "MemoryPool.h"


MemoryPool::MemoryPool(int32 allocSize) : _allocSize(allocSize)
{
	//헤드를 초기화 해준다 
	::InitializeSListHead(&_header);
}

MemoryPool::~MemoryPool()
{
	/*
	* 이 부분은 더이상 사용하지 않고, 아래 구문으로 대체
	while (_queue.empty() == false)
	{
		MemoryHeader* header = _queue.front();
		_queue.pop();
		::free(header);
	}
	*/
	while (MemoryHeader* next = static_cast<MemoryHeader*>(::InterlockedPopEntrySList(&_header)))
	{
		//16바이트 정렬 할당을 aligned malloc 을 했으니 이에 맞춰 _aligned_free 로 해제처리 해준다
		::_aligned_free(next);
	}
	
}

void MemoryPool::Push(MemoryHeader* ptr)
{
	//lock 은 안쓰니 제거
	//WRITE_LOCK;
	ptr->allocSize = 0;

	//queue 부분 제거
	// Pool에 메모리 반납
	//_queue.push(ptr);

	//대신 ms 걸 쓴다
	::InterlockedPushEntrySList(&_header, static_cast<PSLIST_ENTRY>(ptr));


	_allocCount.fetch_sub(1);
}

MemoryHeader* MemoryPool::Pop()
{
	//pop 할때도 기존 것은 제거하고 ms 걸로 대체한다
	//static_cast<MemoryHeader*> 캐스팅 하는 이유는 _header 를 통해 꺼내온  
	//PSLIST_ENTRY 는  MemoryHeader 가 SLIST_ENTRY를 상속받고 있음으로 가능하다
	MemoryHeader* newNext =  static_cast<MemoryHeader*>(::InterlockedPopEntrySList(&_header));
	/* 기존 queue 부분 제거
	{
		WRITE_LOCK;
		// Pool에 여분이 있는지?
		if (_queue.empty() == false)
		{
			// 있으면 하나 꺼내온다
			header = _queue.front();
			_queue.pop();
		}
	}
	*/

	// 없으면 새로 만들다
	if (newNext == nullptr)
	{
		//header = reinterpret_cast<MemoryHeader*>(::malloc(_allocSize));
		//그냥 malloc 하면 안되고 MemoryHeader에 대한 메모리 할당임으로 16 바이트로 정렬된 메모리를 할당 받기 위해서 _aligned_malloc 사용한다
		newNext = reinterpret_cast<MemoryHeader*>(::_aligned_malloc(_allocSize, SLIST_ALIGNMENT));
	}
	else
	{
		ASSERT_CRASH(newNext->allocSize == 0);
	}

	_allocCount.fetch_add(1);

	return newNext;
}

 

 

 

 

 

 

이부분은 동일..

#pragma once
#include "Allocator.h"

class MemoryPool;


class Memory
{
	enum
	{
		// ~1024까지 32단위, ~2048까지 128단위, ~4096까지 256단위
		POOL_COUNT = (1024 / 32) + (1024 / 128) + (2048 / 256),
		MAX_ALLOC_SIZE = 4096
	};

public:
	Memory();
	~Memory();

	void*	Allocate(int32 size);
	void	Release(void* ptr);

private:
	vector<MemoryPool*> _pools;

	// 메모리 크기 <-> 메모리 풀
	// O(1) 빠르게 찾기 위한 테이블
	MemoryPool* _poolTable[MAX_ALLOC_SIZE + 1];
};


template<typename Type, typename... Args>
Type* xnew(Args&&... args)
{
	Type* memory = static_cast<Type*>(xalloc_(sizeof(Type)));
	new(memory)Type(forward<Args>(args)...); // placement new
	return memory;
}

template<typename Type>
void xdelete(Type* obj)
{
	obj->~Type();
	xrelease_(obj);
}

 

 

 

cpp

: 메모리 정렬 부분들이 추가 되었습니다

#include "pch.h"
#include "Memory.h"
#include "MemoryPool.h"


Memory::Memory()
{
	int32 size = 0;
	int32 tableIndex = 0;

	for (size = 32; size <= 1024; size += 32)
	{
		MemoryPool* pool = new MemoryPool(size);
		_pools.push_back(pool);

		while (tableIndex <= size)
		{
			_poolTable[tableIndex] = pool;
			tableIndex++;
		}
	}

	for (; size <= 2048; size += 128)
	{
		MemoryPool* pool = new MemoryPool(size);
		_pools.push_back(pool);

		while (tableIndex <= size)
		{
			_poolTable[tableIndex] = pool;
			tableIndex++;
		}
	}

	for (; size <= 4096; size += 256)
	{
		MemoryPool* pool = new MemoryPool(size);
		_pools.push_back(pool);

		while (tableIndex <= size)
		{
			_poolTable[tableIndex] = pool;
			tableIndex++;
		}
	}
}

Memory::~Memory()
{
	for (MemoryPool* pool : _pools)
		delete pool;

	_pools.clear();
}

void* Memory::Allocate(int32 size)
{
	MemoryHeader* header = nullptr;
	const int32 allocSize = size + sizeof(MemoryHeader);

	if (allocSize > MAX_ALLOC_SIZE)
	{
		// 메모리 풀링 최대 크기를 벗어나면 일반 할당
		header = reinterpret_cast<MemoryHeader*>(::_aligned_malloc(allocSize, SLIST_ALIGNMENT));
	}
	else
	{
		// 메모리 풀에서 꺼내온다
		header = _poolTable[allocSize]->Pop();
	}

	return MemoryHeader::AttachHeader(header, allocSize);
}

void Memory::Release(void* ptr)
{
	MemoryHeader* header = MemoryHeader::DetachHeader(ptr);

	const int32 allocSize = header->allocSize;
	ASSERT_CRASH(allocSize > 0);

	if (allocSize > MAX_ALLOC_SIZE)
	{
		// 메모리 풀링 최대 크기를 벗어나면 일반 해제
		::_aligned_free(header);
	}
	else
	{
		// 메모리 풀에 반납한다
		_poolTable[allocSize]->Push(header);
	}
}

 

반응형

+ Recent posts