마이크로 소프트에서 제공하는 lock free stack을 활용하여 #2 와 같은 기능을 하는 memory pool을 만들 수 있다.
먼저 간단한 활용을 살펴보자.
DECLSPEC_ALIGN(16)
class Data
{
public:
SLIST_ENTRY _entry;
int64 _rand = rand() % 1000;
};
SLIST_HEADER* GHeader;
int main()
{
GHeader = new SLIST_HEADER();
ASSERT_CRASH(((uint64)GHeader % 16) == 0);
::InitializeSListHead(GHeader);
for (int32 i = 0; i < 3; i++)
{
GThreadManager->Launch([]()
{
while (true)
{
Data* data = new Data();
ASSERT_CRASH(((uint64)data % 16) == 0);
::InterlockedPushEntrySList(GHeader, (SLIST_ENTRY*)data);
this_thread::sleep_for(10ms);
}
});
}
for (int32 i = 0; i < 2; i++)
{
GThreadManager->Launch([]()
{
while (true)
{
Data* pop = nullptr;
pop = (Data*)::InterlockedPopEntrySList(GHeader);
if (pop)
{
cout << pop->_rand << endl;
delete pop;
}
else
{
cout << "NONE" << endl;
}
}
});
}
GThreadManager->Join();
}
SLIST_HEADER, SLIST_ENTRY를 이용하여 memory pool을 만들어 보자.
MemoryPool.h
#pragma once
enum
{
SLIST_ALIGNMENT = 16
};
/*---------------------
MemoryHeader
---------------------*/
DECLSPEC_ALIGN(SLIST_ALIGNMENT)
struct MemoryHeader : public SLIST_ENTRY
{
// [MemoryHeader][Data] : 표준 new, delete도 같은 방식
MemoryHeader(int32 size) : allocSize(size) {}
static void* AttchHeader(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;
};
/*---------------------
MemoryPool
---------------------*/
DECLSPEC_ALIGN(SLIST_ALIGNMENT)
class MemoryPool
{
public:
MemoryPool(int32 allocSize);
~MemoryPool();
void Push(MemoryHeader* ptr);
MemoryHeader* Pop();
private:
SLIST_HEADER _header;
int32 _allocSize = 0;
atomic<int32> _allocCount = 0;
};
MemoryPool.cpp
#include "pch.h"
#include "MemoryPool.h"
/*---------------------
MemoryPool
---------------------*/
MemoryPool::MemoryPool(int32 allocSize) : _allocSize(allocSize)
{
::InitializeSListHead(&_header);
}
MemoryPool::~MemoryPool()
{
while (MemoryHeader* memory = static_cast<MemoryHeader*>(::InterlockedPopEntrySList(&_header)))
{
::_aligned_free(memory);
}
}
void MemoryPool::Push(MemoryHeader* ptr)
{
ptr->allocSize = 0;
// Pool에 메모리 반납
::InterlockedPushEntrySList(&_header, static_cast<PSLIST_ENTRY>(ptr));
_allocCount.fetch_sub(1);
}
MemoryHeader* MemoryPool::Pop()
{
MemoryHeader* memory = static_cast<MemoryHeader*>(::InterlockedPopEntrySList(&_header));
// 없으면 새로 만든다
if (memory == nullptr)
{
memory = reinterpret_cast<MemoryHeader*>(::_aligned_malloc(_allocSize, SLIST_ALIGNMENT));
}
else
{
ASSERT_CRASH(memory->allocSize == 0);
}
_allocCount.fetch_add(1);
return memory;
}
Memory.cpp
...
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::AttchHeader(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);
}
}
아래처럼 사용할 수 있다.
class Knight
{
public:
int32 _hp = rand() % 1000;
};
int main()
{
for (int32 i = 0; i < 5; i++)
{
GThreadManager->Launch([]()
{
while (true)
{
Knight* knight = xnew<Knight>();
cout << knight->_hp << endl;
this_thread::sleep_for(10ms);
xdelete(knight);
}
});
}
GThreadManager->Join();
}
1. SLIST_ENTRY를 모든 힙 할당에 사용하는 방법은?
- SLIST_ENTRY를 상속하여 MemoryHeader를 정의한다.
- LockFree stack을 사용하기 위해, MemoryPool에 SLIST_HEADER를 멤버변수로 선언한다.
2. _alined_malloc을 사용하는 이유는?
- malloc은 aligned 16 byte를 보장하지 않음.
참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의 (inflearn.com)
TypeCast (0) | 2021.10.29 |
---|---|
Object pool (0) | 2021.10.01 |
Memory pool #2 (0) | 2021.09.23 |
Memory pool (0) | 2021.09.15 |
STL allocator (0) | 2021.09.12 |
댓글 영역