Sendbuffer를 Pooling 하여 사용하도록 바꿔보자
CoreTLS.h, CoreTLS.cpp
#pragma once
#include <stack>
extern thread_local uint32 LThreadId;
extern thread_local std::stack<int32> LLockStack;
extern thread_local SendBufferChunkRef LSendBufferChunk;
#include "pch.h"
#include "CoreTLS.h"
thread_local uint32 LThreadId = 0;
thread_local std::stack<int32> LLockStack;
thread_local SendBufferChunkRef LSendBufferChunk;
SendBuffer.h, SendBuffer.cpp
#pragma once
class SendBufferChunk;
/*-----------
SendBuffer
-----------*/
class SendBuffer
{
public:
SendBuffer(SendBufferChunkRef owner, BYTE* buffer, int32 allocSize);
~SendBuffer();
BYTE* Buffer() { return _buffer; }
int32 WriteSize() { return _writeSize; }
void Close(uint32 writeSize);
private:
BYTE* _buffer;
uint32 _allocSize = 0;
uint32 _writeSize = 0;
SendBufferChunkRef _owner;
};
/*---------------------
SendBufferChunk
---------------------*/
class SendBufferChunk : public enable_shared_from_this<SendBufferChunk>
{
enum
{
SEND_BUFFER_CHUNK_SIZE = 6000
};
public:
SendBufferChunk();
~SendBufferChunk();
void Reset();
SendBufferRef Open(uint32 allocSize);
void Close(uint32 writeSize);
bool IsOpen() { return _open; }
BYTE* Buffer() { return &_buffer[_usedSize]; }
uint32 FreeSize() { return static_cast<uint32>(_buffer.size()) - _usedSize; }
private:
Array<BYTE, SEND_BUFFER_CHUNK_SIZE> _buffer = {};
bool _open = false;
uint32 _usedSize = 0;
};
/*---------------------
SendBufferManager
---------------------*/
class SendBufferManager
{
public:
SendBufferRef Open(uint32 size);
private:
SendBufferChunkRef Pop();
void Push(SendBufferChunkRef buffer);
static void PushGlobal(SendBufferChunk* buffer);
private:
USE_LOCK;
Vector<SendBufferChunkRef> _sendBufferChunks;
};
#include "pch.h"
#include "SendBuffer.h"
/*-----------
SendBuffer
-----------*/
SendBuffer::SendBuffer(SendBufferChunkRef owner, BYTE* buffer, int32 allocSize)
: _owner(owner), _buffer(buffer), _allocSize(allocSize)
{
}
SendBuffer::~SendBuffer()
{
}
void SendBuffer::Close(uint32 writeSize)
{
ASSERT_CRASH(_allocSize >= writeSize);
_writeSize = writeSize;
_owner->Close(writeSize);
}
/*---------------------
SendBufferChunk
---------------------*/
SendBufferChunk::SendBufferChunk()
{
}
SendBufferChunk::~SendBufferChunk()
{
}
void SendBufferChunk::Reset()
{
_open = false;
_usedSize = 0;
}
SendBufferRef SendBufferChunk::Open(uint32 allocSize)
{
ASSERT_CRASH(allocSize <= SEND_BUFFER_CHUNK_SIZE);
ASSERT_CRASH(_open == false);
if (allocSize > FreeSize())
return nullptr;
_open = true;
return ObjectPool<SendBuffer>::MakeShared(shared_from_this(), Buffer(), allocSize);
}
void SendBufferChunk::Close(uint32 writeSize)
{
ASSERT_CRASH(_open == true);
_open = false;
// cursor 옮기기
_usedSize += writeSize;
}
/*---------------------
SendBufferManager
---------------------*/
//[ ] 많이 예약해두고 일부만 사용
SendBufferRef SendBufferManager::Open(uint32 size)
{
if (LSendBufferChunk == nullptr)
{
LSendBufferChunk = Pop(); // WRITE_LOCK
LSendBufferChunk->Reset();
}
ASSERT_CRASH(LSendBufferChunk->IsOpen() == false);
// 다 썼으면 버리고 새것으로 교체
if (LSendBufferChunk->FreeSize() < size)
{
LSendBufferChunk = Pop(); // WRITE_LOCK
LSendBufferChunk->Reset();
}
cout << "Free : " << LSendBufferChunk->FreeSize() << endl;
return LSendBufferChunk->Open(size);
}
SendBufferChunkRef SendBufferManager::Pop()
{
{
WRITE_LOCK;
if (_sendBufferChunks.empty() == false)
{
SendBufferChunkRef sendBufferChunk = _sendBufferChunks.back();
_sendBufferChunks.pop_back();
return sendBufferChunk;
}
}
return SendBufferChunkRef(xnew<SendBufferChunk>(), PushGlobal);
}
void SendBufferManager::Push(SendBufferChunkRef buffer)
{
WRITE_LOCK;
_sendBufferChunks.push_back(buffer);
}
void SendBufferManager::PushGlobal(SendBufferChunk* buffer)
{
GSendBufferManager->Push(SendBufferChunkRef(buffer, PushGlobal));
}
개선할 점? 매번마다 OnRecv에서 MakeShared ?? 근데, 메모리 풀 사용하잖아? 그럼에도 메모리 양이 커지면 pooling 안하고 new, delete 방식임.
메모리 미리 많이 잡아서 sendbuffer 잡는 게 낭비
크게 할당 (SendBufferChunk ) 잘라먹기 가 포인트
SendBufferManager에서 더 이상 사용하지 않는 메모리 관리하는 방법은?
- 메모리 풀에 해당 메모리 반납
SendBufferChunkRef를 tls에 만드는 이유는?
- lock 사용을 최소화 하기 위함.
할당 한 Chunk 메모리를 모두 사용하여 새 거로 교체되면 이전 메모리는 해제 되지 않음을 어떻게 보장하는지?
- SendBuffer는 WSASend에서 Ref 카운팅 되고 있다.
참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 대시보드 - 인프런 | 강의 (inflearn.com)
Buffer Helpers (0) | 2023.04.19 |
---|---|
Packet Session (0) | 2023.04.16 |
SendBuffer (0) | 2021.12.21 |
RecvBuffer (0) | 2021.12.20 |
네트워크 라이브러리 만들기(Session #3) (0) | 2021.12.13 |
댓글 영역