SlideShare a Scribd company logo
1 of 62
Pool SystemCGCIICho sanghyun’s Game Classes II
멀티테스크 환경하에서 제한된 메모리를 효율적 사용을 위한 메모리 시스템 .
필요할 경우 필요할 만큼 할당요구하고 다 사용하면 반납하는 시스템 .
풀 (Pool) 시스템
CGCIICho sanghyun’s Game Classes II
풀시스템
성능에 초점을 둔 풀 시스템의 필요성 엄청 대두 !!
힙 (Heap) 즉 “동적 할당기”=
하지만 , 메모리 용량이 커지고 빈번한 사용에 따라 성능 문제도 이슈화 !
우리가 인식하지 못하는 사이 수도 없이 많은 동적 할당과 해제를 사용
하고 있다 !
(std::list 나 std::map 에 push/pop 을 할 때도 매번 동적 할당과 해제가 일어난
다 .)
풀의 기본적 개념
 은 jemalloc 라는 힙을 사용해 속도 향상을 얻었다고 한다 .
동적 할당 성능의 중요성
 의 tcmalloc 를 개발해 내놓았다 !!!
( 어떤 곳에서 단지 tcmalloc 의 사용만으로 20% 의 성능향상을 얻었다고 한
다 .)
 완전 답게 !! 기본 힙 (heap) 은 그렇게 좋은 성능을 가지고 있지 못했
다 !
 그래서 는 성능을 업그레이드 시킨 힙 시스템인
LFH(Low Fragmentation Heap) 을 내놓았다 .
(Windows XP 이후는 기본 적용된다 .)
CGCIICho sanghyun’s Game Classes II
풀시스템
CGCIICho sanghyun’s Game Classes II
풀시스템 객체 풀
 객체 풀이 단순한 메모리만을 할당하는 풀에 비해 매우 유리한 장점이 존재한다 .
1. 메모리 할당 자체에 대한 성능 향상을 기대할 수 있다 . ( 이것은 공통 )
2. 객체의 생성자와 소멸자의 호출 및 초기화 에 드는 비용을 최소화할
수 있다 .
→ 최소한의 재생 (Recycle) 과정만을 거쳐 할당할 수 있다 .
→ 복합객체나 초기화 비용이 큰 객체일수록 엄청난 비용 차이가 난다 .
→ 이 효과는 단순한 메모리 할당자인 tcmalloc 나 jemalloc 로는 기대할 수 없다 .
3. 객체 풀을 기반으로 메모리 풀을 만든다 .
CGCII 에서 사용한 객체 Pool 의 기본 원리
CGCIICho sanghyun’s Game Classes II
풀시스템 객체풀 시스템의 기본 원리
Pool
AllocAlloc
FreeFree
Garbage
Stack
1. 할당해줘 !
2.[ 스택 ] 에 저장된 거 있냐 ? 엄슴 !
3. 새로 만들어 주자
1. 잘썼다 ! 할당받았던 거 돌려줄께 !
3. [ 스택 ] 에서 꺼내주자 !
2. 오케 !! Stack 에 쌓아놓자 !
1. 또 할당해내 !!!
2. [ 스택 ] 에 저장된 거 있냐 ? 어 ! 있슴 !
delete 절약delete 절약
new 절약new 절약
이렇게 new 와 delete 할 것을 계속 절약하는 것이 쌓이면
성능이 향상될 것이다 !!!!!!!!!!
다만… [ 스택 ] 에 쌓고 [ 스택 ] 에서 꺼내오는 것이
new/delete 보다 빠르다는 전제하에서 ~~
CGCIICho sanghyun’s Game Classes II
풀시스템 객체풀 시스템의 기본 원리
 일단은 성능 좋은 [ 스택 ] 을 구현하는 것이 핵심이닷 !!!
단 , 조건이 있다 !
첫째 , new/delete 혹은 malloc()/free() 보다 넘사벽으로 빨라야 한다 !
둘째 , 쓰레드에 대해 안전 (Thread-safe) 해야 한다 !!!
 [ 스택 ] 이라면 뭐 .. 간단히 ~~ 간단히 !!!
STL 에 std::list<T> 나 std::vector<T> 를 쓰면 간단히 해결되겠네 !!!!!!!!
역시 결론은 STL!!
여기에다 쓰레드 안전을 위해 크리티컬 섹션 걸어서 쓰면 되겠네 !!!!
이건 뭐 엄청 쉽다 !!!! 초딩도 할 수 있을 것 같아 ~
라고 생각하다 보통 멘붕을 맞게 됨 ...
초간단데스네 !!
셋째 , 안정성이 확보되어야 한다 !!!
CGCIICho sanghyun’s Game Classes II
풀시스템 객체풀 시스템의 기본 원리
 일단은 std::list 는 push 등을 수행할 때 node 생성을 위해 동적 할당 (new) 을
사용한다 ! pop 을 할 때 delete 를 사용한다 !!!
그것도 allocator 를 통해 ..
앗 ! 그럼 push/pop 때 동적 할당 제거를 위해 pool allocator 를 사용하면 되지 않을까 ???
라는 생각은 pool 을 만들기 위해 다른 pool 을 쓴다는… 무한반복
 그럼 std::vector<T> 가 등장하면 어떨까 ?
list 와 같은 동적 할당은 없지만 미리 할당된 크기만큼 reserve 해둬야 하고
만에 하나 이 예측이 틀리면 엄청난 피를 봐야 한다 ~
 그래서 !!!!
[ Lock-free Singly Linked-List ]
가 등장함 !!!
이미 실패
!
이미 실패
!
역시 실패
!
역시 실패
!
CGCIICho sanghyun’s Game Classes II
풀시스템 객체풀 시스템의 기본 원리
 Lock-free 의 구현을 위해서는 Stack 되는 객체 자체가 다음 객체를 가리키는 포
인터 변수가 필요하다 !!
 그래서 풀의 대상객체들은 ICGPoolable<T> 를 상속받게 만들었다 .
- ICGPoolable<T> 에는 [Single Linked List] 를 위한 변수가 존재함 .
template<typename TTYPE>
struct SCGLIST_ENTRY
{
public:
TTYPE* Next;
};
template <typename TOBJECT>
class ICGPoolable :
public SCGLIST_ENTRY<TOBJECT>,
virtual public ICGReferenceCount
{
protected:
ICGPoolable() {}
};
template<typename TTYPE>
struct SCGLIST_ENTRY
{
public:
TTYPE* Next;
};
template <typename TOBJECT>
class ICGPoolable :
public SCGLIST_ENTRY<TOBJECT>,
virtual public ICGReferenceCount
{
protected:
ICGPoolable() {}
};
다음 객체를 가리키기 위한 변수 !!!
CGCIICho sanghyun’s Game Classes II
풀시스템 객체풀의 구현
 Pool 은 쉽게 말해서 Lock-free Singly Linked List 다 .
template<typename TOBJECT>
class CGPool::CObject
{
public:
TOBJECT* Alloc();
void Free(TOBJECT* p_pObject);
private:
lockfree_self_stack<TCREATE*> m_stackObject;
};
template<typename TOBJECT>
class CGPool::CObject
{
public:
TOBJECT* Alloc();
void Free(TOBJECT* p_pObject);
private:
lockfree_self_stack<TCREATE*> m_stackObject;
};
1. Lock-free singly linked list stack
2. Alloc()/Free()
CGCIICho sanghyun’s Game Classes II
풀시스템 객체풀의 구현
template<typename TOBJECT>
TOBJECT* CGPool:: CObject<TOBJECT>::Alloc()
{
TCREATE* pObject;
pObject = m_stackObject.pop();
if(pObject ==nullptr)
{
pObject = new TOBJECT;
}
return pObject;
}
template<typename TOBJECT>
TOBJECT* CGPool:: CObject<TOBJECT>::Alloc()
{
TCREATE* pObject;
pObject = m_stackObject.pop();
if(pObject ==nullptr)
{
pObject = new TOBJECT;
}
return pObject;
}
template<typename TOBJECT>
void CGPool:: CObject<TOBJECT>::Free(TOBJECT* p_pObject)
{
m_stackObject.push(p_pObject);
}
template<typename TOBJECT>
void CGPool:: CObject<TOBJECT>::Free(TOBJECT* p_pObject)
{
m_stackObject.push(p_pObject);
}
1. 먼저 [ 스택 ] 에서 Pop 을 해본다 .
2. [ 스택 ] 에 없으면 new 로 새로 생성한다 .
3. 객체 리턴 ~
1. [ 스택 ] 에 Push 한다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 TLS 를 이용한 풀
 아무리 Lock-free 를 사용한다 해도 Thread Safe 를 위해 Interlocked 함수만 해
도 성능을 많이 떨어뜨릴 수 있다 .
 또 Lock-free 알고리즘 상 Thread 가 많아지거나 부하가 많이 걸리면 쓰레드 간
경쟁으로 성능이 더 떨어진다 .
 TLS (Thread Local Storage) 를 사용하면 이 역시도 최소화할 수 있다 .
 TLS 는 tcmalloc 나 jemalloc 등에서 성능 향상의 핵심 역할을 하고 있다 !
 TLS (Thread Local Storage) 는 특성상 다중 쓰레드 환경 하에 많은 부하가 걸리
는 상태에서 더 엄청난 성능 향상을 준다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 TLS 를 이용한 풀
 각 Thread 마다 Thread cache 를 두고 중앙은 [Lock-free 로 구현된 스택 ] 버퍼
를 둔다 !
[ 중앙 스
택 ]
Thread A
Thread B
Thread C
[TLS 캐시 스택 ]
1. 할당해줘 !
2. 일단 TLS Cache stack 을 확인한다 . 어 없
네 !!
3. [ 중앙 스택 ] 에서 Chunk 를 가져와 TLS
Cache stack 을 채운다 !! (Lock 처리해서 가져
온다 .)
Chunk
(Bundle)
4. 그리고 TLS Cache Stack 에서 할당해준다 .
5. 또 할당해줘 !!
6. TLS Cache stack 에 있으니 TLS Cache 에서
꺼내온다 !!! ( 이때는 Lock 처리가 전혀 없다 !)
Chunk 크기가 커지면 커질 수록 Lock 의 빈도가
낮아져 성능이 그만큼 향상된다 .
하지만 Chunk 크기가 커지면 커질 수록 잔여 객
체수가 많아져 그만큼 메모리 낭비가 커지게 된다
.
Lock 처리 전혀 없음Lock 처리 전혀 없음
Lock-free 스택Lock-free 스택
 Pool 의 대상이 되려면 ICGPoolable<T> 을 상속받아야 한다 .
class foo: virtual public ICGPoolable<foo>
{
…
public
virtual void OnFinalRelease() { /*delete this 하면 클난다 !*/}
};
class foo: virtual public ICGPoolable<foo>
{
…
public
virtual void OnFinalRelease() { /*delete this 하면 클난다 !*/}
};
CGOBJ<CGPool::CObject<foo>> g_pool;
void main()
{
foo* pObject = g_pool.Alloc();
…
g_pool = g_pool.Free(pObject);
}
CGOBJ<CGPool::CObject<foo>> g_pool;
void main()
{
foo* pObject = g_pool.Alloc();
…
g_pool = g_pool.Free(pObject);
}
CGCIICho sanghyun’s Game Classes II
풀시스템 객체 풀 사용하기 (1)
1. ICGPoolable<T> 를 상속받는다 .
 Pool 은 풀의 대상이 되는 클래스를 템플릿 인자로만 넣으면 된다 .
CGPool::CObject<foo> poolTEST;
대상클래스 풀객체 변수명
2. 풀 객체 선언
3. 풀에서 객체 할당하기 ~
4. 다 사용하고 풀에 객체 반환하기 ~
 매번 Pool 을 정의하고 또 객체의 OnFinalRelease() 도 정의하려면 귀찮다 !!!
또 할당 해제할 때 생성된 Pool 을 찾아 Free() 를 해줘야 한다 !!!
template <typename TOBJECT>
class NCGPoolable : virtual public ICGPoolable<TOBJECT>
{
protected:
NCGPoolable();
public:
staticTOBJECT* Alloc() { return m_pPoolObject ->Alloc();}
void Free(TOBJECT* p_pItem) { m_pPool ->Free(p_pItem);}
private:
virtual void OnFinalRelease() { Free((TOBJECT*)this);}
private:
staticCGPTR<CGPool::CObject<TOBJECT>> m_pPoolObject;
CGPTR<CGPool::CObject<TOBJECT>> m_pPool;
};
template <typename TOBJECT>
class NCGPoolable : virtual public ICGPoolable<TOBJECT>
{
protected:
NCGPoolable();
public:
staticTOBJECT* Alloc() { return m_pPoolObject ->Alloc();}
void Free(TOBJECT* p_pItem) { m_pPool ->Free(p_pItem);}
private:
virtual void OnFinalRelease() { Free((TOBJECT*)this);}
private:
staticCGPTR<CGPool::CObject<TOBJECT>> m_pPoolObject;
CGPTR<CGPool::CObject<TOBJECT>> m_pPool;
};
 그래서 NCGPoolable<T> 클래스가 있다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 객체 풀 사용하기 (2)
class foo: public NCGPoolable<foo>
{
…
};
class foo: public NCGPoolable<foo>
{
…
};
void main()
{
CGPTR<foo> pObject = foo ::Alloc();
…
}
void main()
{
CGPTR<foo> pObject = foo ::Alloc();
…
}
 ICGPoolable<T> 대신 NCGPoolable<T> 을 상속받으면 아주 간단해진다 !!!
OnFinalRelease() 를 정의할 필요도 없다 !!
 따로 전역으로 풀 객체 (CGPool::CObject<T>) 를 선언할 필요가 없다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 객체 풀 사용하기 (3)NCGPoolable<T>
자동 참조계수 처리된다 .
template <class TOBJECT>
CGPTR<TOBJECT> NEW()
{
__if_exists(TOBJECT::NCGPoolable)
{
return TOBJECT::Alloc();
}
__if_not_exists(TOBJECT::NCGPoolable)
{
class OBJECT_CREATE : public TOBJECT, public CGReleaser::NDelete
{
};
return CGPTR<TOBJECT>(new OBJECT_CREATE());
}
};
template <class TOBJECT>
CGPTR<TOBJECT> NEW()
{
__if_exists(TOBJECT::NCGPoolable)
{
return TOBJECT::Alloc();
}
__if_not_exists(TOBJECT::NCGPoolable)
{
class OBJECT_CREATE : public TOBJECT, public CGReleaser::NDelete
{
};
return CGPTR<TOBJECT>(new OBJECT_CREATE());
}
};
 매번 Pool 을 정의하고 OnFinalRelease() 하려면 귀찮다 !!!
그래서 NEW<T>() 함수에 기능 추가 !
CGCIICho sanghyun’s Game Classes II
풀시스템
 NCGPoolable<T> 을 상속받았으면 T::Alloc() 함수를 호출하여 [Pool 에서 할당 ] 받는다 .
 NCGPoolable<T> 을 상속받지 않았으면 [new 로 생성 ] 한다 .
객체 풀 사용하기 (4) NCGPoolable<T> & NEW<T>
class foo: virtual public ICGReferenceCount
{
…
};
class gee: virtual public NCGPoolable<gee>
{
…
};
void main()
{
CGPTR<foo> p1 = NEW<foo>(); // new 를 사용해 동적 할당
CGPTR<gee> p2 = NEW<gee>(); // pool 에서 할당
…
}
class foo: virtual public ICGReferenceCount
{
…
};
class gee: virtual public NCGPoolable<gee>
{
…
};
void main()
{
CGPTR<foo> p1 = NEW<foo>(); // new 를 사용해 동적 할당
CGPTR<gee> p2 = NEW<gee>(); // pool 에서 할당
…
}
 NEW<T>() 함수를 사용하면
• NCGPoolable<T> 를 상속받으면 Pool 에서 할당된다 .
• ICGReferenceCoount 만 상속받았다면 new 를 사용하여 동적 할당한다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 객체 풀 사용하기 (5) NCGPoolable<T> & NEW<T>
1. 그냥 일반 참조계수
2. 풀을 사용한 객체
CGCIICho sanghyun’s Game Classes II
풀시스템 메모리 블록 풀 (1)
 메모리를 할당 받아야 할 경우에는 임의의 크기를 할당 받아야 하기 때문에
CGPool::CObject<T> 로 힘들다 .
 이를 위해 여러 개의 [ 메모리 블록 풀 ] 을 연결하여 사용한다 .
1k Memory Pool1k Memory Pool
2k Memory Pool2k Memory Pool
3k Memory Pool3k Memory Pool
64k Memory Pool64k Memory Pool
.
.
.
1~1K Byte 할당 요구 시
1K~2K Byte 할당 요구 시
2K~3K Byte 할당 요구 시
63K~64K Byte 할당 요구 시
.
.
.
여러 크기의
메모리 블록 풀을
마련함 .
(Buckets)
64K Byte 이상 할당 요구 시 일반적인 할당 사용
버킷들 (Buckets)
CGCIICho sanghyun’s Game Classes II
풀시스템 메모리 블록 풀 (2)
 메모리 블록 풀은 처리는 간단히 해당 메모리 풀을 선택하는 처리가 전부 !
class CGPool::CMemoryBlock
{
…
vector<CGPTR<CGPool::CObjectTLS>> m_vectorMemoryBlockPool;
…
};
class CGPool::CMemoryBlock
{
…
vector<CGPTR<CGPool::CObjectTLS>> m_vectorMemoryBlockPool;
…
};
#define MEM_POOL_ALLOC(size) CGPool::CMemoryBlock::GetInstance().Alloc(SELECT_BLOCK(size))#define MEM_POOL_ALLOC(size) CGPool::CMemoryBlock::GetInstance().Alloc(SELECT_BLOCK(size))
#define SELECT_BLOCK(size) ((size<=8192) ? ((size<=1024) ? ((size-1)>>7) : (((size-1)>>10)+7)) :
((size<=65536) ? (((size-1)>>12)+13) : (((size-1)>>14)+25)))
#define SELECT_BLOCK(size) ((size<=8192) ? ((size<=1024) ? ((size-1)>>7) : (((size-1)>>10)+7)) :
((size<=65536) ? (((size-1)>>12)+13) : (((size-1)>>14)+25)))
ICGMemory* CGPool::CMemoryBlock::Alloc(int p_iBlock)
{
return m_vectorMemoryBlockPool[p_iBlock]->Alloc();
}
ICGMemory* CGPool::CMemoryBlock::Alloc(int p_iBlock)
{
return m_vectorMemoryBlockPool[p_iBlock]->Alloc();
}
 할당을 요구하는 메모리의 크기에 따라 어떤 풀을 선택할 것인가는 매크로로 정의
CGCIICho sanghyun’s Game Classes II
풀시스템 버퍼 시스템 (1)
 할당된 메모리는 메모리 포인터가 아니라 객체를 리턴한다 .
class ICGMemory :
public CGBUF,
virtual public ICGReferenceCount // 참조 계수 ..
{
…
};
class ICGMemory :
public CGBUF,
virtual public ICGReferenceCount // 참조 계수 ..
{
…
};
1. WSABUF(CGBUF==WSABUF) 를 상속받는다 .
2. 참조계수
ICGMemory* MEM_POOL_ALLOC(int SIZE);
void* MEM_POOL_ALLOC(int SIZE);
 ICGMemory 는 참조계수로 동작하기 위해 정의 되었다 .
 풀에서 할당을 요구할 때는 MEM_POOL_ALLOC 를 호출하면 된다 .
MEM_POOL_ALLOC(30);
MEM_POOL_ALLOC(1000);
30 Byte 를 할당요구하려면…
1000 Byte 를 할당요구하려면…
CGCIICho sanghyun’s Game Classes II
풀시스템 버퍼 시스템 (2)
 CGCII 에서는 풀에서 할당된 메모리를 사용하기 위해 CCGBuffer 를 제공한다 .
CCGBuffer = WSABUF
• buf → 메모리 버퍼 포인터
• len → 메모리 버퍼 크기
• 참조계수로 할당해제하기 위해
• 버퍼로 사용을 위해 각종 편리한 함수들을 포
함한다 .
• 버퍼에 값 쓰기 (Append, <<, …)
• 버퍼에서 값 읽기 (Head, Extract, >>, …)
+ 스마트포인터
+ 편리한 버퍼 사용
• 실시간 바운드 체크+ 편리한 디버깅
CGCIICho sanghyun’s Game Classes II
풀시스템 버퍼 시스템 (3)
 CGCII 에서는 풀에서 할당된 메모리를 사용하기 위해 CCGBuffer 를 제공한다 .
class CCGBuffer :
public CGBUF
{
public:
CCGBuffer(){}
CCGBuffer(ICGMemory* p_ref) : CGBUF(p_ref->buf, 0), m_reference(p_ref) {}
CCGBuffer(ICGMemory* p_ref, ULONG p_iP) : CGBUF(p_ref->buf, p_iP), m_reference(p_ref){}
…
public:
Buffer 의 편한 사용을 위한 각종 함수들…
private:
CGPTR<ICGMemory> m_reference;
};
class CCGBuffer :
public CGBUF
{
public:
CCGBuffer(){}
CCGBuffer(ICGMemory* p_ref) : CGBUF(p_ref->buf, 0), m_reference(p_ref) {}
CCGBuffer(ICGMemory* p_ref, ULONG p_iP) : CGBUF(p_ref->buf, p_iP), m_reference(p_ref){}
…
public:
Buffer 의 편한 사용을 위한 각종 함수들…
private:
CGPTR<ICGMemory> m_reference;
};
1. WSABUF(CGBUF==WSABUF) 를 상속받는다 .
2. ICGMemory 를 멤버로 가진다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 버퍼 시스템 (4)
 메모리 풀에서 할당 받아 사용하고 참조계수로 자동 할당 해제된다 .
{
CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000);
…
};
{
CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000);
…
};
1. Pool 에서 Memory 를 할당 받는다 .
3. 블록이 끝나면 bufferTEMP 가 소멸되며 자동으로 할당 해제한다 .
CCGBuffer bufferCOPY = bufferTEMP;CCGBuffer bufferCOPY = bufferTEMP; 4. 대입하면 참조계수 1 이 증가한다 .
2. 할당 받은 Memory 를 bufferTEMP 에 들어가며
참조계수가 1 이 된다 .
 CCGBuffer 는 ‘스마트 포인터’역할도 수행함 .
 CCGBuffer 는 편리한 버퍼 사용을 위한 매우 다양한 기능을 제공해 준다 .( 추후 설명 )
CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000);
bufferTEMP.Append<int>(1);
bufferTEMP.Append<DWORD>(30);
…
bufferTEMP.Head<int>(4);
CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000);
bufferTEMP.Append<int>(1);
bufferTEMP.Append<DWORD>(30);
…
bufferTEMP.Head<int>(4);
CGCIICho sanghyun’s Game Classes II
풀시스템 메모리 블록 풀 (2)
 이런 형태의 메모리 블록 풀은 LFH, tcmalloc, jemalloc 모두 사용하고 있음 .
LFH jemalloc
16K Byte
tcmalloc
32K Byte 선택적
CGCII
CGPool
8K/64K/256K
/1M Byte
블록 크기
(Granularity)
최대 적용 크기
(Max Range)
8/16/32/64
/128/258/512
총 128 개 Buckets
선택적 (*)
선택적 (**)
최대 53 개 풀
Google 뿐 아
니라 게임 클라
이언트 / 서버
등 널리 사용 .
특징 듣보잡
할당 오버헤드
(Overhead)
3~7% 수준4% 수준 4%+alpha 수준
8/16/32/~
/256 Byte
총 170 Buckets
TLS 적용 No Yes Yes Yes
Slow-start Aver-Convergence- ?Garbage Collection
Algorithm
Windows XP
이상 기본 동작
CGCIICho sanghyun’s Game Classes II
풀시스템 풀 관리 – 재고 관리 (1)
 풀 시스템에서 재고 관리를 제대로 하지 않으면 시스템의 불안정을 야기할 수
있다 ! 그래서 매우 중요하다 !
재고가 너무 많이 저장해 놓으면 !재고가 너무 많이 저장해 놓으면 !
재고가 너무 적게 저장해 놓으면 !!재고가 너무 적게 저장해 놓으면 !!
메모리 많이 잡아 먹어 시스템에 문제 야기 !
풀을 하나 마나 !!! 오히려 풀 처리로 성능만 떨어뜨릴 뿐 !
 풀 시스템의 핵심은 적절한 재고를 유지하는 것이 풀 시스템 안정화의 핵심 !
 적절한 재고처리 시스템이 없다면 사실상 서버에서 풀 시스템을 운용하기에는 위
험부담이 너무 크다 !
CGCIICho sanghyun’s Game Classes II
풀시스템 풀 관리 – 재고 관리 (2)
 tcmalloc 의 경우 TLS cache 크기를 결정 짓는 데 TCP 의 “슬로우 스타트 (Slow-
Start)” 알고리즘을 사용한다 한다 .
TCP 의 슬로우 스타트 (Slow Start)
 총 TLS cache 크기가 2M 가 넘게 되면 재고 처리를 시작한다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 풀 관리 – 재고 관리 (3)
 CGPool 은 “평균 수렴 시스템”을 사용한다 .
 평균과 표준편차를 구해 최대 저장 재고량을 결정하고 그 이상이 넘으면 재고를
처분하는 알고리즘이다 .
사용량
시간
CGCIICho sanghyun’s Game Classes II
풀시스템 풀 관리 – 재고 관리 (3)
 CGPool 은 기본적 재고관리뿐만 아니라 4 단계의 위기 관리 시스템이 있다 .
Level 1
Level 2
Level 3
Level 4
→ 일반적인 상황 앞에서 설명한 대로 동작한다 .
→ 한계 재고 처리량으로 보다 빨리 수렴하도록 한다 .
→ 재고를 전혀 쌓지 않는다 .
(Pool 을 사용하지 않는 것과 동일하다 .)
→ 즉시 한계 재고량으로 수렴시킨다 .
CGCIICho sanghyun’s Game Classes II
풀시스템 풀 관리 – 통계 처리
 CGPool 은 사용량 , 할당량 , 해제량 , 생성량 , 소멸량 등 다양한 통계정보를 실
시간으로 제공한다 .
 그 외에 수동 Prepare 기능 , Shrink 기능 등 다양한 관리 기능을 제공해 준다 .
저장된 객체 수
할당 중인 객체 수
현재 존재하는 총 객체 수
총 할당 요구 횟수
최대 객체 수
새로 생성된 객체 수
지워버린 객체 수
재사용률
생성 / 소멸 비율
객체 명
CGCIICho sanghyun’s Game Classes II
풀시스템 Performance Time
 CGPool 과 tcmalloc 그리고 기존의 LFH 의 성능을 비교한다 .
특정 크기의 메모리를 [1000] 번 할당 후 [1000] 번 할당 해제하는 것을 [1000] 번 반
복하는 테스트를 수행한다 .
All Right Reserved ©CGLabs
Socket PerformanceCGCIICho sanghyun’s Game Classes II
 Windows Socket 에서 성능의 핵심은 뭐니 뭐니해도…
• 송수신 중 복사의 최소화 !
[ 무복사 송수신 ]
• Gathering!
• 쓰레드 풀
• 효율적인 다중 쓰레드 처리
[ProAct 패턴 지원 ]
 Windows Socket 에서 바로 이 기능들을 최대로 지원하고 최적화를 하는 것
이 고성능의 서버 엔진의 위한 관건 !
Overlapped I/O IOCP와
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 의 성능
 링 버퍼 ( 혹은 Circular) 사용
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 수신 (Receive) 방법
 선형 버퍼 사용 (Head/Tail Ptr 만 사용 )
 CGCII 의 방법
 선형 버퍼 사용 (Tail Ptr 만 사용 )
일반적이고 전통적인 방법
대규모 메모리 사용하는 선형 버퍼
가장 간단한 방법
메모리 풀을 사용하여 한번 사용한 메모리를 버리고 새로
운 버퍼를 할당 받는 방법
Overlapped I/O 를 사용하여 수신 처리하는 방법은 다양할 수 있다 .
 전형적인 Ring Buffer 를 사용한 방법 .
Receive
Message Buffer
CGCIICho sanghyun’s Game Classes II
소켓성능
Head PtrTail Ptr
Receive Receive
 처리가 매우 복잡다 .
 메시지의 처리를 위해 일반적으로 다른 버
퍼를 생성해 복사를 수행해야 한다 .
 송수신량이 적을 때는 오히려 필요 이상의
부하와 메모리를 사용하게 된다 .
1. Head 포인터 앞부분과 Tail 포인터 뒷부분을 [Receive] 건다 .
 Overlapped I/O 걸 메모리 효율이 좋다 .
 수신과 메시지 처리를 각각 다른 쓰레드에서
처리가 가능하다 .
2. 수신이 완료 되면 메시지 버퍼에 받은 데이터를 복사한다 .
3. 메시지버퍼는 Work Thread 에 걸어주고 다시 1 번부터 무한반복 ~
장점 단점
TCP 수신 (Receive) 처리 방법 (1)
Head/Tail Ptr
Receive Buffer
(Ring Buffer)
Overlapped I/O 를 걸 버퍼
메시지 처리
 아주 간단하게 Receive 버퍼를 만들 수도 있다 .
Receive
1. Tail 포인터 ( 혹은 len) 뒤쪽 메모리를 모두 [Receive] 건다 .
3. 다시 1 번부터 무한반복 .
CGCIICho sanghyun’s Game Classes II
소켓성능
Tail Ptr
2. 수신이 완료되면 메시지를 처리하고 남은 것은 앞으로 복사해서 땡긴다 .
 처리 구조가 매우 간단하다 .
 이렇게 되면 메시지가 끊겨서 복사해야 하
는 일은 역시 없다 !!
 I/O 와 메시지 처리를 각각 다른 쓰레드에서
처리할 수는 역시 없다 .
 매번 메시지를 처리할 때마다 메모리 복사를
할 수도 있고 부하가 늘어나면 그 빈도가 증
가할 수 있다 .
장점 단점
TCP 수신 (Receive) 처리 방법 (2)
Receive Buffer
Receive
 매번 받을 때마다 복사가 될 수 있는 위험이 있기 때문에 크게 버퍼를 잡고 Head
포인터와 Tail 포인터를 사용하여 수신처리를 한다 !
Receive
 처리 구조는 매우 간단한 편이다 .
 이렇게 되면 메시지가 끊겨서 복사해야 하
는 일은 없다 !!
CGCIICho sanghyun’s Game Classes II
소켓성능
Head PtrTail Ptr
 I/O 와 메시지 처리를 각각 다른 쓰레드에서
처리할 수는 없다 .
 잦은 메모리 복사를 막을 수 있다 .
 효율대비 고정적 메모리 사용이 많다 .
1. Tail 포인터 뒷부분에 대해서 [Receive] 를 건다 .
2. 수신이 완료되면 Head 포인터부터 메시지를 처리하고 Head 포인터를 옮긴다 .
3. Head 포인터 ( 혹은 Tail 포인터 ) 가 특정 위치를 넘어서면 앞으로 땡겨서 복사한
후 1 번부터 무한반복
장점 단점
TCP 수신 (Receive) 처리 방법 (3)
Head/Tail Ptr
Receive
Receive Buffer
Receive
 앞의 방법들과 유사하나 한번 사용한 메모리는 버리고 항상 새로 할당 받는다는
것이 다르다 .
Receive
2. 수신이 완료되면 Head 포인터부터 메시지를 처리하고 Head 포인터를 옮긴다 .
1. Tail 포인터 뒷부분에 대해서 [Receive] 를 건다 .
CGCIICho sanghyun’s Game Classes II
소켓성능
3. Head 포인터 ( 혹은 Tail 포인터 ) 가 특정 위치를 넘어서면 새로 버퍼를 할당 받아
남은 데이터를 복사한 후 1 번부터 무한반복
 처리 구조는 비교적 간단하다 .
 이렇게 되면 불연속적 메시지 버퍼로 인해 복사를 해야 하는 일은 역시 없다 !!
 I/O 와 메시지 처리를 각각 다른 쓰레드에서 처리하는 것도 문제없다 .
 받은 메모리를 저장 후 사용하거나 재송신에 사용해 추가적으로 메모리 복사량
을 줄일 수 있다 .
 버퍼 크기를 수신 상황에 따라 바꾼다면 더 금상첨화 .
 ?
장점 단점
CGCII TCP 수신 (Receive) 처리 방법
Receive Buffer
Head/Tail PtrHead PtrTail Ptr
참조계수
00
11
 수신 받는 량에 따라 Buffer 의 크기를 변화시키는 것을 말한다 .
너무 크게 잡으면
CGCIICho sanghyun’s Game Classes II
소켓성능 CGCII 가변 수신 버퍼
 고정된 크기로 버퍼를 잡을 경우 .
너무 작게 잡으면
더 많은 메모리가 소모된다 .
성능이 떨어지거나 큰 메시지를 받지 못할 수 있다 .
 WSAIoctl() 함수 혹은 ioctlsocket() 함수와 FIONREAD 플래스를 사용하여 수신
한 데이터량을 확인하여 Receive Buffer 의 크기를 변경시킨다 .
→ 큰 크기의 버퍼를 새로 할당 받아야 하므로 반드시 풀 시스템이 기반이 되어야
한다 !
 가변 수신 버퍼를 사용하면 성능과 메모리 사용량 모두를 해결할 수 있
다 .
 CGCII 에서는 당연히 적용되어 있다 . ( 옵션으로 MIN SIZE 와 MAX SIZE 를 설
정할 수 있다 .)
CGCIICho sanghyun’s Game Classes II
소켓성능 메시지 처리 (1)
 데이터 수신과 메시지 처리의 방법적 문제에서도 성능의 차이가 있을 수 있다 .
 Overlapped I/O 로 데이터 수신 후 핵심 처리 과정은 대략 아래와 같은 3 가지 단계이다 .
• 받은 데이터를 메시지별로 분리하는 작업이 필요하다 .
• 일반적으로 메시지 헤드 (Head) 에서 메시지 크기를 확인하고 메
시지 별로 분리작업을 한다 .
• 분리된 각 메시지가 어떤 메시지인지를 확인하여 메시지에 맞는
처리를 하는 과정이다 .
• 가장 부하가 많이 걸릴 수 있는 부분이다 .
• 일반적으로 switch-case 문을 사용하여 처리한다 .
• 데이터를 받기 위해 데이터를 받을 버퍼를 준비하고 정리한 후 다
시 Recv() 를 걸어주는 과정이다 .
 수신 받은 데이터를 메시지 별로 분리해 처리하는 방법은 대략 아래 3 가지 정도로 정리
CGCIICho sanghyun’s Game Classes II
소켓성능 메시지 처리 (2)
• 수신 받는 즉시 메시지 분리와 처리 그리고 Recv() 걸기까지 모두 처리한다 .
• 수신 받은 후 메시지를 분리하여 큐잉하고 바로 Recv() 를 걸어준다 .
• 메시지의 처리는 다른 쓰레드에서 큐잉된 메시지를 꺼내 처리한다 .
• 수신 받은 후 데이터를 즉시 큐잉만 하고 Recv() 를 건다 .
• 메시지 분리와 처리는 다른 Thread 에서 한다 .
메시지 분리 메시지 처리 Recv() 걸기+ +
메시지 분리 큐잉 Recv() 걸기+ +
큐잉 Recv() 걸기+
메시지 처리디큐잉 +
메시지 처리디큐잉 + 메시지 분리 +
I/O Thread
I/O Thread
I/O Thread
Work Thread
Work Thread
 데이터를 받은 후 메시지 분리 , 메시지 처리를 모두 수행하고 Recv() 를 거는 방식이다 .
CGCIICho sanghyun’s Game Classes II
소켓성능 메시지 처리 (2) – 방법 1
Recv 다시 걸기Recv 다시 걸기
메시지 수신메시지 수신
종료종료
메시지 처리메시지 처리
메시지 분리메시지 분리
 처리 구조는 매우 간단한 편이다 .
 데이터 수신이 많지 않을 경우 효율적이다
.
 메시지분리와 처리를 쓰레드에 분산해서 처
리할 수 없다 .
 단일 소켓으로 많은 데이터를 수신할 경우
다중 코어에 분산처리할 수 없어 비효율적 .
 따라서 모든 데이터를 처리한 후 Recv() 를
걸어야 하기 때문에 데이터를 빨리 비울 수
없어 그만큼 수신 성능이 떨어진다 .
장점 단점
I/O Thread
 데이터를 받은 후 메시지 분리하여 분리된 메시지 정보를 큐잉한 후 Recv() 를 거는 방식이다 .
 링버퍼를 사용할 경우 이런 방법을 많이 사용한다 .
CGCIICho sanghyun’s Game Classes II
소켓성능 메시지 처리 (2) – 방법 2
Recv 다시 걸기Recv 다시 걸기
메시지 수신메시지 수신
종료종료
메시지 큐잉
메시지 분리메시지 분리
메시지 처리메시지 처리
메시지 수신메시지 수신
종료종료
메시지 디큐잉
I/O Thread Work Thread
 처리 과정이 좀 복잡하다
 I/O 처리와 메시지처리를 분리해서 처리할
수 있다 .
 빠르게 Recv() 를 다시 걸어 데이터 수신
성능을 향상시킬 수 있다 .
 메시지 수신량이 적을 때는 오히려 추가적인
부담만 늘어날 수 있다 .
 추가적인 메모리 부담이 있을 수 있다 .
장점 단점
 데이터를 받은 후 즉시 큐잉만 하고 Recv() 를 거는 방식이다 .
CGCIICho sanghyun’s Game Classes II
소켓성능 메시지 처리 (2) – 방법 3
Recv 다시 걸기Recv 다시 걸기
메시지 수신메시지 수신
종료종료
데이터 큐잉
메시지 처리메시지 처리
메시지 수신메시지 수신
종료종료
데이터 디큐잉
I/O Thread Work Thread
 처리 과정이 좀 복잡하다
 I/O 처리와 메시지처리를 분리해서 처리할
수 있다 .
 가장 빠르게 Recv() 를 다시 걸어 가장 뛰
어난 데이터 수신 성능을 제공한다 .
 메시지 수신량이 적을 때는 오히려 추가적인
부담만 늘어날 수 있다 .
장점 단점
메시지 분리메시지 분리
 Overlapped I/O 를 사용하여 송신 (Send) 처리를 하는 방법은 크게 아래와 같이 방법이 있다 .
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 송신 (Send) 처리 방법
Gathering (A)
Gathering (B)
• 전송 요청 즉시 Overlapped I/O 를 걸어 Send 를 수행함
• 전송 빈도가 적을 때 효율적인 반면 많으면 전송 효율 급하락 .
Gathering 버퍼를 두어 데이터를 이곳에 복사하여 전송하는 방법
Gathering 버퍼를 사용하지 않고 WSASend 의 Gathering 기능을 사용하
여 전송하는 방법
CGCII 에서 사용하는 방법
• 하나의 소켓당 한번에 하나의 Overlapped I/O 만 걸어 Send 를 수행하는 방법 .
• Send 중이면 Queuing 하였다가 Send 가 완료 후 한꺼번에 전송한다 .
• 전송 빈도가 많으면 효율 좋음 .
단순 Gathering
전송을 요청하면 무조건 Queuing 하고 일정 시간 마다 Queuing 된 데이터
를 전송하는 방법
( 반응속도 느림 , 전송용 쓰레드 추가적으로 필요 )
Overlapped
Send
Overlapped
Send
전송 요청전송 요청
전송 끝전송 끝
전송 완료전송 완료
완료 처리 끝완료 처리 끝
Buffer
AddRef()
Buffer
AddRef()
Buffer
Release()
Buffer
Release()
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 송신 (Send) 처리 방법 (1) – 단순
 가장 일반적이고 간단한 Overlapped I/O 를 사용한 송신 처리 방법
 한번의 Send 요청에 한번의 Overlapped I/O 를 거는 방법
 처리 구조가 매우 간단한다 .
 크기가 큰 메시지를 띄엄띄엄 전송한다면
효과가 매우 좋다 .
 작은 메시지를 매우 빈번히 전송할 경우
CPU 를 매우 많이 사용하고 성능이 급격히
떨어진다 .
( 그냥 Send() 보다 더 떨어진다 .)
장점 단점
전송 요청전송 요청
자체 송신 버퍼에
memcpy()
자체 송신 버퍼에
memcpy()
전송 끝전송 끝
시작시작
전송할 것이
있나 ?
전송할 것이
있나 ?
완료 처리 끝완료 처리 끝
No
Yes
Overlapped SendOverlapped Send
LOCK
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 송신 (Send) 처리 방법 (2) – 단순 Gathering
 전송을 요청하면 무조건 Queuing 하고 Send 용 Thread 가 일정 시간 마다
Queuing 된 데이터를 확인하여 전송하는 방법
 반응속도 느리고 전송용 쓰레드가 추가적으로 필요함 .
 구시대의 유물로 비추 방법 !!!
Send 용 Thread
전송 완료전송 완료
완료 처리 끝완료 처리 끝
완료처리
( 버퍼 정리 )
완료처리
( 버퍼 정리 )
일정시간 Sleep일정시간 Sleep
LOCK
전송 요청전송 요청
자체 송신 버퍼에
memcpy()
자체 송신 버퍼에
memcpy()
bSending?bSending?
false
true
전송 끝전송 끝
bSending=truebSending=true
전송 완료전송 완료
완료 처리완료 처리
전송할 것이
있나 ?
전송할 것이
있나 ?
완료 처리 끝완료 처리 끝
버퍼 내용
전송
버퍼 내용
전송
No
Yes
bSending=falsebSending=false
LOCK
LOCK
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 송신 (Send) 처리 방법 (3) – Gathering (A)
 내부 송신 버퍼에 송신 내용을 복사한 후 모아서 Overlapped I/O 를 거는 방법
 하나의 소켓에서 동시에 한번의 Overlapped Send 만 걸리도록 한다 .
Overlapped SendOverlapped Send
1:N 전송은 왜 ?
• 같은 버퍼를 여러 곳에 전송하면 각 소켓의 버퍼에 복사를 해 넣어야 한다 !!!
• 특히 Server 에서 이런 전송은 매우 빈번하다 !
SocketSocket
SocketSocket
SocketSocket
SocketSocket
.
.
.
Message Buffer
Send BufferSend Buffer
Send BufferSend Buffer
Send BufferSend Buffer
Send BufferSend Buffer
복
사
복
사
복
사
복
사
CGCIICho sanghyun’s Game Classes II
소켓성능
 이렇게 되면 결론적으로 Overlapped I/O 의 가장 큰 장점이 무력화된다 !!!!!
TCP 송신 (Send) 처리 방법 (4)
 처리 구조가 좀 복잡다 .
 한꺼번에 모아 전송하므로 Overlapped I/O
량을 줄일 수 있다 .
 작은 메시지를 빈번히 전송할 경우 성능이
우수하다 .
 전송 때 반드시 ! memcpy() 를 동반한다 .
 따라서 큰 메시지 전송이나 1:N 전송을 수
행하면 잦은 메모리 복사로 인해 성능이 급
격히 떨어질 수 있다 .
장점 단점
int WSASend (
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
);
int WSASend (
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
);
 어떻게 IOCP 의 장점을 가진체 I/O 를 최소화할 수 있는 방법은 없을까 ?
→ 하지만 !! WSASend 에 Gathering 기능을 사용하면 어떨까 ?
Gathering 전송을 지원한다 !
→ 일반적으로 한꺼번에 모으려면 복사를 해야 한다 !!!
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 송신 (Send) 처리 방법 (5)
포인터만 큐잉포인터만 큐잉
bSending?bSending?
Overlapped
Send
Overlapped
Send
false
true
전송 요청전송 요청
전송 끝전송 끝
완료완료
Buffers Release()Buffers Release()
큐잉된 것이
있나 ?
큐잉된 것이
있나 ?
완료 처리 끝완료 처리 끝
큐잉된 것
모두 꺼내서 ~
큐잉된 것
모두 꺼내서 ~
No
Yes
bSending=truebSending=true bSending=falsebSending=false
LOCK
LOCK
Buffer
AddRef()
Buffer
AddRef()
Overlapped
Send
Overlapped
Send
Gathering Send
CGCIICho sanghyun’s Game Classes II
소켓성능 TCP 송신 (Send) 처리 방법 (6) - Gathering (B)
CGCIICho sanghyun’s Game Classes II
소켓성능
Message Buffer
Head Ptr Tail Ptr
Send Buffer
 자 그렇다면 에코 송수신을 처리할 때 기존의 링 - 버퍼를 사용하고 그냥 모아 전송을
사용하면 어떻게 될까 ?
복
사
복
사
복
사 복
사 복
사
Receive
Send
메시지 생성기
복
사
메아리 (Echo) 송수신 (1)
CGCIICho sanghyun’s Game Classes II
소켓성능
Message Buffer
Head Ptr Tail Ptr
Send Buffer
 CGCII 의 송수신 방법을 사용하여 Echo 전송을 수행한다고 하면…
Receive
Send
메아리 (Echo) 송수신 (2)
 아무런 복사를 수행하지 않고 단지 참조계수만으로 처리됨 !
112233445566
CGCIICho sanghyun’s Game Classes II
소켓성능 Performance Time
 Socket 의 Echo 송수신 테스트를 수행한다 .
 CGCII 의 Socket 의 특성에 따라 다른 튜닝을 할 수 있도록 해준다 .
CGCIICho sanghyun’s Game Classes II
소켓성능 CGCII Socket Tunning
일반 Overlapped 전송
Gathering 전송을 지원하는 전송
받는 즉시 메시지 처리
받은 데이터 큐잉 후 다른 쓰레드가 처리
(I/O 와 Work 의 분리 )
TCP Socket Send 버퍼 크기
Gather 큐 최대 깊이
TCP Socket Receive 버퍼 크기
수신 버퍼 크기 ( 최소 / 최대 )
수신 큐 최대 깊이
 메신저나 채팅을 위한 소켓
CGCIICho sanghyun’s Game Classes II
소켓성능 CGCII Socket Tunning
Send/Receive 가 비교적 적다
대규모 접속이 필요하다 .
 다운로드 서버를 위한 소켓
Send 가 엄청나게 많다 .
같은 데이터를 여러 군데 전송한다 .
 서버간 연결을 위한 소켓
단일 연결로 많은 Send/Receive 수행
접속 안정성 최대화 필요
 일반 게임 서버에 접속하는 유저용 소켓
Send/Receive 가 중간 정도 ~
 다운로드 클라이언트를 위한 소켓
Receive 량이 엄청나게 많다 .
Receive 후 처리 과정이 매우 길다 .
Send-Simple, Receive-Simple
모든 버퍼 최소 크기 설정하여 Socket 당 사용 메모리 최소화
Send-Gather 혹은 전용
모든 버퍼 최소 크기 설정하여 Socket 당 사용 메모리 최소화
Send-Simple, Receive-Queued
수신버퍼 크기 최대화하여 수신효율 극대화 .
Send-Gather, Receive-Queued
송수신버퍼 크기 최대화하여 송수신효율 모두 극대화 .
Send-Gather, Receive-Simple
송수신 버퍼 크기 중간 정도 .
Socket 의 특성에 따라 최적의 성능을 내기 위해서 다양한 튜닝이 있을 수 있다 .
CGCIICho sanghyun’s Game Classes II
소켓성능
 매번 전송 때마다 메시지를 작성하는 것이 아니라 한번 작성해 놓고 지속적으로 사용
하는 메시지를 의미한다 .
 메시지의 많은 부분이 정적 메시지일 수 있다 .
정적 메시지 버퍼 (1)
 한번 작성하면 끝날 때까지 메시지를 계속 쓰는 것이 아니라 일정 시간마다 갱신해야
하는 경우 한번 작성한 메시지를 여러 번 사용할 수도 있다 .
 어떠한 메시지는 대부분의 값은 동일하고 일부 값만 살짝 틀린 메시지들이 있을 수
있다 . 그럴 경우 나머지 설정된 값들은 그대로 사용하고 수정된 부분만 바꾸어 전송
하는 것을 의미한다 .
이것을 위한 ‘메시지 버퍼 풀 (CGPool::CBuffer<I>)’ 을 제공한다 .
 메시지 버퍼 풀
 정적 메시지 버퍼 (2)
 정적 메시지 버퍼 (1)
CGCIICho sanghyun’s Game Classes II
소켓성능 접속종료와 소켓 재사용
 접속종료 처리시 DisconnectEx 를 사용하여 소켓 재사용 할 수 있다 .
따라서 매번 socket 을 생성 / 소멸 시킬 필요가 없다 !
 하지만 DisconnectEx 가 간혹 완료가 되지 않는 경우를 대비해야 한다 .
( 보통 0.000001% 정도의 낮은 확률로 DisconnectEx 가 완료되지 않는 경우가 있
고 CGCII 는 이를 대비해 DisconnectEx 를 건 Socket 의 List 를 보관 후 일정 시간
동안 접속 종료처리가 되지 않으면 강제 완료 처리한다 .)
 AcceptEx 는 접속 받기 성능을 높여준다 .
 AcceptEx 와 ConnectEx 를 사용하면 접속 처리를 비동기함수를 사용하여 처리가 가
능하다 .
 AcceptEx 와 ConnectEx 는 접속받기와 접속하기를 IOCP 에 통합할 수 있도록 해준
다 .
 DisconnectEx 와 Socket Reuse 의 사용
 AcceptEx 와 ConnectEx 의 사용
CGCIICho sanghyun’s Game Classes II
소켓성능 Lock-free 의 광범위한 적용
 Lock-free Stack 이나 Queue 말고도 많은 곳에 Lock-free 처리를 적용하여 Critical-
section 의 사용을 최소화하였다 .
bool foo::Start()
{
long preState;
preState = InterlockedExchange(&m_bStart, 1);
RETURN_IF(preState==0, false);
…
bool foo::Start()
{
long preState;
preState = InterlockedExchange(&m_bStart, 1);
RETURN_IF(preState==0, false);
…
bool foo::Start()
{
long preState;
preState = InterlockedCompareExchange(&m_bState, STATE_SYN, STATE_CLOSE);
RETURN_IF(preState==STATE_CLOSE, false);
…
bool foo::Start()
{
long preState;
preState = InterlockedCompareExchange(&m_bState, STATE_SYN, STATE_CLOSE);
RETURN_IF(preState==STATE_CLOSE, false);
…
CGCIICho sanghyun’s Game Classes II
소켓성능 최적 자료 구조 사용
 빈번한 삽입이나 삭제되는 큐 같은 것의 처리를 위해 CGD::circular_list<T> 등을 사
용한다 . ( 대표적으로 각종 Manager 들… )
std::list
CGD::circular_list
Head Tail
Head
Tail
CGCIICho sanghyun’s Game Classes II
소켓성능 반복 처리 최소화
 최대한 검색하거나 찾는 과정에서 Loop 처리 과정을 제거했다 .
void Manager::Remove(foo* p_pobject)
{
list<CGPTR<foo>>::iterator iter = m_objects.begin();
list<CGPTR<foo>>::iterator iterEnd = m_objects.end();
for(; iter!=iterEnd; ++iter)
{
if(*iter==p_pObject)
{
m_objects.erase(iter);
break;
}
}
}
void Manager::Remove(foo* p_pobject)
{
list<CGPTR<foo>>::iterator iter = m_objects.begin();
list<CGPTR<foo>>::iterator iterEnd = m_objects.end();
for(; iter!=iterEnd; ++iter)
{
if(*iter==p_pObject)
{
m_objects.erase(iter);
break;
}
}
}
제거를 위해 Iterator 을 돌아야 한다 !
CGCIICho sanghyun’s Game Classes II
소켓성능 반복 처리 최소화
 최대한 검색하거나 찾는 과정에서 Loop 처리 과정을 제거했다 .
// Error & Exception 처리 생략 !!
void Manager::Add(foo* p_pobject)
{
m_objects.push_front(p_pobject);
p_pobject->m_iter = m_objects.begin();
}
// Error & Exception 처리 생략 !!
void Manager::Add(foo* p_pobject)
{
m_objects.push_front(p_pobject);
p_pobject->m_iter = m_objects.begin();
}
1. 생성 할 때 iterator 을 저장해 놓는다 .
void Manager::Remove(foo* p_pobject)
{
m_objects.erase(m_objects->m_iter);
}
void Manager::Remove(foo* p_pobject)
{
m_objects.erase(m_objects->m_iter);
}
2. 한 번에 지운다 !
 당연히 std::map 보다도 훨씬 빠르다 !!
(std::map 도 O(log n) 의 부하가 필요하고 한 스텝 자체의 크기와 Balancing 을 위한
부하도 무시할 수 없다 .)
CGCIICho sanghyun’s Game Classes II
질문 ?
중간 질문 ?
소켓성능

More Related Content

What's hot

Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)Esun Kim
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012devCAT Studio, NEXON
 
Multiplayer Game Sync Techniques through CAP theorem
Multiplayer Game Sync Techniques through CAP theoremMultiplayer Game Sync Techniques through CAP theorem
Multiplayer Game Sync Techniques through CAP theoremSeungmo Koo
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템QooJuice
 
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)Seungmo Koo
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019devCAT Studio, NEXON
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...Amazon Web Services Korea
 
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지강 민우
 
임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012devCAT Studio, NEXON
 
송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010devCAT Studio, NEXON
 
중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직Hoyoung Choi
 
실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략YEONG-CHEON YOU
 
마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건강 민우
 
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리YEONG-CHEON YOU
 
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception상현 조
 
게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가Seungmo Koo
 
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?내훈 정
 
게임 분산 서버 구조
게임 분산 서버 구조게임 분산 서버 구조
게임 분산 서버 구조Hyunjik Bae
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기Seungjae Lee
 
게임서버프로그래밍 #1 - IOCP
게임서버프로그래밍 #1 - IOCP게임서버프로그래밍 #1 - IOCP
게임서버프로그래밍 #1 - IOCPSeungmo Koo
 

What's hot (20)

Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
 
Multiplayer Game Sync Techniques through CAP theorem
Multiplayer Game Sync Techniques through CAP theoremMultiplayer Game Sync Techniques through CAP theorem
Multiplayer Game Sync Techniques through CAP theorem
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템
 
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
 
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
 
임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
 
송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010
 
중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직
 
실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략
 
마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건
 
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
 
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
 
게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가
 
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
 
게임 분산 서버 구조
게임 분산 서버 구조게임 분산 서버 구조
게임 분산 서버 구조
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기
 
게임서버프로그래밍 #1 - IOCP
게임서버프로그래밍 #1 - IOCP게임서버프로그래밍 #1 - IOCP
게임서버프로그래밍 #1 - IOCP
 

Viewers also liked

GCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (3) - ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception상현 조
 
GCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group System
GCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group SystemGCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group System
GCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group System상현 조
 
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server SampleGCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample상현 조
 
GCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing System
GCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing SystemGCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing System
GCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing System상현 조
 
GCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple Inhertance
GCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple InhertanceGCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple Inhertance
GCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple Inhertance상현 조
 
[KOSSA] C++ Programming - 14th Study - template
[KOSSA] C++ Programming - 14th Study - template[KOSSA] C++ Programming - 14th Study - template
[KOSSA] C++ Programming - 14th Study - templateSeok-joon Yun
 
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화Jaeseung Ha
 
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍흥배 최
 
오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발
오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발
오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발주항 박
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11OnGameServer
 
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이  왜 이리 힘드나요?  (Lock-free에서 Transactional Memory까지)Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이  왜 이리 힘드나요?  (Lock-free에서 Transactional Memory까지)
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)내훈 정
 
NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉
NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉
NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉iFunFactory Inc.
 
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기흥배 최
 
모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트
모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트
모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트Dae Kim
 
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git민태 김
 

Viewers also liked (16)

GCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (3) - ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
 
GCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group System
GCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group SystemGCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group System
GCGC- CGCII 서버 엔진에 적용된 기술 (8) - Group System
 
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server SampleGCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
 
GCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing System
GCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing SystemGCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing System
GCGC- CGCII 서버 엔진에 적용된 기술 (4) - Executing System
 
GCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple Inhertance
GCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple InhertanceGCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple Inhertance
GCGC- CGCII 서버 엔진에 적용된 기술 (7) - Multiple Inhertance
 
Iocp advanced
Iocp advancedIocp advanced
Iocp advanced
 
[KOSSA] C++ Programming - 14th Study - template
[KOSSA] C++ Programming - 14th Study - template[KOSSA] C++ Programming - 14th Study - template
[KOSSA] C++ Programming - 14th Study - template
 
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
 
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
 
오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발
오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발
오픈 소스를 활용한 캐쥬얼 게임 서버 프레임워크 개발
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
 
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이  왜 이리 힘드나요?  (Lock-free에서 Transactional Memory까지)Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이  왜 이리 힘드나요?  (Lock-free에서 Transactional Memory까지)
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
 
NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉
NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉
NDC14 범용 게임 서버 프레임워크 디자인 및 테크닉
 
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
 
모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트
모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트
모바일 게임과 앱을 위한 오픈소스 게임서버 엔진 프로젝트 CloudBread 프로젝트
 
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
 

Similar to GCGC- CGCII 서버 엔진에 적용된 기술 (2) - Perfornance

[2D4]Python에서의 동시성_병렬성
[2D4]Python에서의 동시성_병렬성[2D4]Python에서의 동시성_병렬성
[2D4]Python에서의 동시성_병렬성NAVER D2
 
HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3suitzero
 
Multi-thread : producer - consumer
Multi-thread : producer - consumerMulti-thread : producer - consumer
Multi-thread : producer - consumerChang Yoon Oh
 
모어 이펙티브 c++ 5장 스터디
모어 이펙티브 c++ 5장 스터디모어 이펙티브 c++ 5장 스터디
모어 이펙티브 c++ 5장 스터디quxn6
 
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화sung ki choi
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinDong Chan Shin
 
Api design for c++ pattern
Api design for c++ patternApi design for c++ pattern
Api design for c++ patternjinho park
 
Api design for c++ ch3 pattern
Api design for c++ ch3 patternApi design for c++ ch3 pattern
Api design for c++ ch3 patternjinho park
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택JinTaek Seo
 
Kth개발자 세미나 1회
Kth개발자 세미나 1회Kth개발자 세미나 1회
Kth개발자 세미나 1회Byeongsu Kang
 
[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리MinGeun Park
 
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)Dong Chan Shin
 
[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...
[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...
[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...Seok-joon Yun
 
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기Wonha Ryu
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성HyeonSeok Choi
 
Ai C#세미나
Ai C#세미나Ai C#세미나
Ai C#세미나Astin Choi
 
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?NAVER Engineering
 
[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담
[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담
[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담Sumin Byeon
 

Similar to GCGC- CGCII 서버 엔진에 적용된 기술 (2) - Perfornance (20)

[2D4]Python에서의 동시성_병렬성
[2D4]Python에서의 동시성_병렬성[2D4]Python에서의 동시성_병렬성
[2D4]Python에서의 동시성_병렬성
 
HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3
 
Multi-thread : producer - consumer
Multi-thread : producer - consumerMulti-thread : producer - consumer
Multi-thread : producer - consumer
 
모어 이펙티브 c++ 5장 스터디
모어 이펙티브 c++ 5장 스터디모어 이펙티브 c++ 5장 스터디
모어 이펙티브 c++ 5장 스터디
 
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshin
 
강의자료4
강의자료4강의자료4
강의자료4
 
Api design for c++ pattern
Api design for c++ patternApi design for c++ pattern
Api design for c++ pattern
 
Api design for c++ ch3 pattern
Api design for c++ ch3 patternApi design for c++ ch3 pattern
Api design for c++ ch3 pattern
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택
 
Kth개발자 세미나 1회
Kth개발자 세미나 1회Kth개발자 세미나 1회
Kth개발자 세미나 1회
 
[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리
 
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
 
[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...
[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...
[C++ korea] Effective Modern C++ study item 19 use shared ptr for shared owne...
 
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성
 
Ai C#세미나
Ai C#세미나Ai C#세미나
Ai C#세미나
 
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?
 
K8s in action02
K8s in action02K8s in action02
K8s in action02
 
[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담
[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담
[야생의 땅: 듀랑고]의 식물 생태계를 담당하는 21세기 정원사의 OpenCL 경험담
 

GCGC- CGCII 서버 엔진에 적용된 기술 (2) - Perfornance

  • 2. 멀티테스크 환경하에서 제한된 메모리를 효율적 사용을 위한 메모리 시스템 . 필요할 경우 필요할 만큼 할당요구하고 다 사용하면 반납하는 시스템 . 풀 (Pool) 시스템 CGCIICho sanghyun’s Game Classes II 풀시스템 성능에 초점을 둔 풀 시스템의 필요성 엄청 대두 !! 힙 (Heap) 즉 “동적 할당기”= 하지만 , 메모리 용량이 커지고 빈번한 사용에 따라 성능 문제도 이슈화 ! 우리가 인식하지 못하는 사이 수도 없이 많은 동적 할당과 해제를 사용 하고 있다 ! (std::list 나 std::map 에 push/pop 을 할 때도 매번 동적 할당과 해제가 일어난 다 .) 풀의 기본적 개념
  • 3.  은 jemalloc 라는 힙을 사용해 속도 향상을 얻었다고 한다 . 동적 할당 성능의 중요성  의 tcmalloc 를 개발해 내놓았다 !!! ( 어떤 곳에서 단지 tcmalloc 의 사용만으로 20% 의 성능향상을 얻었다고 한 다 .)  완전 답게 !! 기본 힙 (heap) 은 그렇게 좋은 성능을 가지고 있지 못했 다 !  그래서 는 성능을 업그레이드 시킨 힙 시스템인 LFH(Low Fragmentation Heap) 을 내놓았다 . (Windows XP 이후는 기본 적용된다 .) CGCIICho sanghyun’s Game Classes II 풀시스템
  • 4. CGCIICho sanghyun’s Game Classes II 풀시스템 객체 풀  객체 풀이 단순한 메모리만을 할당하는 풀에 비해 매우 유리한 장점이 존재한다 . 1. 메모리 할당 자체에 대한 성능 향상을 기대할 수 있다 . ( 이것은 공통 ) 2. 객체의 생성자와 소멸자의 호출 및 초기화 에 드는 비용을 최소화할 수 있다 . → 최소한의 재생 (Recycle) 과정만을 거쳐 할당할 수 있다 . → 복합객체나 초기화 비용이 큰 객체일수록 엄청난 비용 차이가 난다 . → 이 효과는 단순한 메모리 할당자인 tcmalloc 나 jemalloc 로는 기대할 수 없다 . 3. 객체 풀을 기반으로 메모리 풀을 만든다 .
  • 5. CGCII 에서 사용한 객체 Pool 의 기본 원리 CGCIICho sanghyun’s Game Classes II 풀시스템 객체풀 시스템의 기본 원리 Pool AllocAlloc FreeFree Garbage Stack 1. 할당해줘 ! 2.[ 스택 ] 에 저장된 거 있냐 ? 엄슴 ! 3. 새로 만들어 주자 1. 잘썼다 ! 할당받았던 거 돌려줄께 ! 3. [ 스택 ] 에서 꺼내주자 ! 2. 오케 !! Stack 에 쌓아놓자 ! 1. 또 할당해내 !!! 2. [ 스택 ] 에 저장된 거 있냐 ? 어 ! 있슴 ! delete 절약delete 절약 new 절약new 절약 이렇게 new 와 delete 할 것을 계속 절약하는 것이 쌓이면 성능이 향상될 것이다 !!!!!!!!!! 다만… [ 스택 ] 에 쌓고 [ 스택 ] 에서 꺼내오는 것이 new/delete 보다 빠르다는 전제하에서 ~~
  • 6. CGCIICho sanghyun’s Game Classes II 풀시스템 객체풀 시스템의 기본 원리  일단은 성능 좋은 [ 스택 ] 을 구현하는 것이 핵심이닷 !!! 단 , 조건이 있다 ! 첫째 , new/delete 혹은 malloc()/free() 보다 넘사벽으로 빨라야 한다 ! 둘째 , 쓰레드에 대해 안전 (Thread-safe) 해야 한다 !!!  [ 스택 ] 이라면 뭐 .. 간단히 ~~ 간단히 !!! STL 에 std::list<T> 나 std::vector<T> 를 쓰면 간단히 해결되겠네 !!!!!!!! 역시 결론은 STL!! 여기에다 쓰레드 안전을 위해 크리티컬 섹션 걸어서 쓰면 되겠네 !!!! 이건 뭐 엄청 쉽다 !!!! 초딩도 할 수 있을 것 같아 ~ 라고 생각하다 보통 멘붕을 맞게 됨 ... 초간단데스네 !! 셋째 , 안정성이 확보되어야 한다 !!!
  • 7. CGCIICho sanghyun’s Game Classes II 풀시스템 객체풀 시스템의 기본 원리  일단은 std::list 는 push 등을 수행할 때 node 생성을 위해 동적 할당 (new) 을 사용한다 ! pop 을 할 때 delete 를 사용한다 !!! 그것도 allocator 를 통해 .. 앗 ! 그럼 push/pop 때 동적 할당 제거를 위해 pool allocator 를 사용하면 되지 않을까 ??? 라는 생각은 pool 을 만들기 위해 다른 pool 을 쓴다는… 무한반복  그럼 std::vector<T> 가 등장하면 어떨까 ? list 와 같은 동적 할당은 없지만 미리 할당된 크기만큼 reserve 해둬야 하고 만에 하나 이 예측이 틀리면 엄청난 피를 봐야 한다 ~  그래서 !!!! [ Lock-free Singly Linked-List ] 가 등장함 !!! 이미 실패 ! 이미 실패 ! 역시 실패 ! 역시 실패 !
  • 8. CGCIICho sanghyun’s Game Classes II 풀시스템 객체풀 시스템의 기본 원리  Lock-free 의 구현을 위해서는 Stack 되는 객체 자체가 다음 객체를 가리키는 포 인터 변수가 필요하다 !!  그래서 풀의 대상객체들은 ICGPoolable<T> 를 상속받게 만들었다 . - ICGPoolable<T> 에는 [Single Linked List] 를 위한 변수가 존재함 . template<typename TTYPE> struct SCGLIST_ENTRY { public: TTYPE* Next; }; template <typename TOBJECT> class ICGPoolable : public SCGLIST_ENTRY<TOBJECT>, virtual public ICGReferenceCount { protected: ICGPoolable() {} }; template<typename TTYPE> struct SCGLIST_ENTRY { public: TTYPE* Next; }; template <typename TOBJECT> class ICGPoolable : public SCGLIST_ENTRY<TOBJECT>, virtual public ICGReferenceCount { protected: ICGPoolable() {} }; 다음 객체를 가리키기 위한 변수 !!!
  • 9. CGCIICho sanghyun’s Game Classes II 풀시스템 객체풀의 구현  Pool 은 쉽게 말해서 Lock-free Singly Linked List 다 . template<typename TOBJECT> class CGPool::CObject { public: TOBJECT* Alloc(); void Free(TOBJECT* p_pObject); private: lockfree_self_stack<TCREATE*> m_stackObject; }; template<typename TOBJECT> class CGPool::CObject { public: TOBJECT* Alloc(); void Free(TOBJECT* p_pObject); private: lockfree_self_stack<TCREATE*> m_stackObject; }; 1. Lock-free singly linked list stack 2. Alloc()/Free()
  • 10. CGCIICho sanghyun’s Game Classes II 풀시스템 객체풀의 구현 template<typename TOBJECT> TOBJECT* CGPool:: CObject<TOBJECT>::Alloc() { TCREATE* pObject; pObject = m_stackObject.pop(); if(pObject ==nullptr) { pObject = new TOBJECT; } return pObject; } template<typename TOBJECT> TOBJECT* CGPool:: CObject<TOBJECT>::Alloc() { TCREATE* pObject; pObject = m_stackObject.pop(); if(pObject ==nullptr) { pObject = new TOBJECT; } return pObject; } template<typename TOBJECT> void CGPool:: CObject<TOBJECT>::Free(TOBJECT* p_pObject) { m_stackObject.push(p_pObject); } template<typename TOBJECT> void CGPool:: CObject<TOBJECT>::Free(TOBJECT* p_pObject) { m_stackObject.push(p_pObject); } 1. 먼저 [ 스택 ] 에서 Pop 을 해본다 . 2. [ 스택 ] 에 없으면 new 로 새로 생성한다 . 3. 객체 리턴 ~ 1. [ 스택 ] 에 Push 한다 .
  • 11. CGCIICho sanghyun’s Game Classes II 풀시스템 TLS 를 이용한 풀  아무리 Lock-free 를 사용한다 해도 Thread Safe 를 위해 Interlocked 함수만 해 도 성능을 많이 떨어뜨릴 수 있다 .  또 Lock-free 알고리즘 상 Thread 가 많아지거나 부하가 많이 걸리면 쓰레드 간 경쟁으로 성능이 더 떨어진다 .  TLS (Thread Local Storage) 를 사용하면 이 역시도 최소화할 수 있다 .  TLS 는 tcmalloc 나 jemalloc 등에서 성능 향상의 핵심 역할을 하고 있다 !  TLS (Thread Local Storage) 는 특성상 다중 쓰레드 환경 하에 많은 부하가 걸리 는 상태에서 더 엄청난 성능 향상을 준다 .
  • 12. CGCIICho sanghyun’s Game Classes II 풀시스템 TLS 를 이용한 풀  각 Thread 마다 Thread cache 를 두고 중앙은 [Lock-free 로 구현된 스택 ] 버퍼 를 둔다 ! [ 중앙 스 택 ] Thread A Thread B Thread C [TLS 캐시 스택 ] 1. 할당해줘 ! 2. 일단 TLS Cache stack 을 확인한다 . 어 없 네 !! 3. [ 중앙 스택 ] 에서 Chunk 를 가져와 TLS Cache stack 을 채운다 !! (Lock 처리해서 가져 온다 .) Chunk (Bundle) 4. 그리고 TLS Cache Stack 에서 할당해준다 . 5. 또 할당해줘 !! 6. TLS Cache stack 에 있으니 TLS Cache 에서 꺼내온다 !!! ( 이때는 Lock 처리가 전혀 없다 !) Chunk 크기가 커지면 커질 수록 Lock 의 빈도가 낮아져 성능이 그만큼 향상된다 . 하지만 Chunk 크기가 커지면 커질 수록 잔여 객 체수가 많아져 그만큼 메모리 낭비가 커지게 된다 . Lock 처리 전혀 없음Lock 처리 전혀 없음 Lock-free 스택Lock-free 스택
  • 13.  Pool 의 대상이 되려면 ICGPoolable<T> 을 상속받아야 한다 . class foo: virtual public ICGPoolable<foo> { … public virtual void OnFinalRelease() { /*delete this 하면 클난다 !*/} }; class foo: virtual public ICGPoolable<foo> { … public virtual void OnFinalRelease() { /*delete this 하면 클난다 !*/} }; CGOBJ<CGPool::CObject<foo>> g_pool; void main() { foo* pObject = g_pool.Alloc(); … g_pool = g_pool.Free(pObject); } CGOBJ<CGPool::CObject<foo>> g_pool; void main() { foo* pObject = g_pool.Alloc(); … g_pool = g_pool.Free(pObject); } CGCIICho sanghyun’s Game Classes II 풀시스템 객체 풀 사용하기 (1) 1. ICGPoolable<T> 를 상속받는다 .  Pool 은 풀의 대상이 되는 클래스를 템플릿 인자로만 넣으면 된다 . CGPool::CObject<foo> poolTEST; 대상클래스 풀객체 변수명 2. 풀 객체 선언 3. 풀에서 객체 할당하기 ~ 4. 다 사용하고 풀에 객체 반환하기 ~
  • 14.  매번 Pool 을 정의하고 또 객체의 OnFinalRelease() 도 정의하려면 귀찮다 !!! 또 할당 해제할 때 생성된 Pool 을 찾아 Free() 를 해줘야 한다 !!! template <typename TOBJECT> class NCGPoolable : virtual public ICGPoolable<TOBJECT> { protected: NCGPoolable(); public: staticTOBJECT* Alloc() { return m_pPoolObject ->Alloc();} void Free(TOBJECT* p_pItem) { m_pPool ->Free(p_pItem);} private: virtual void OnFinalRelease() { Free((TOBJECT*)this);} private: staticCGPTR<CGPool::CObject<TOBJECT>> m_pPoolObject; CGPTR<CGPool::CObject<TOBJECT>> m_pPool; }; template <typename TOBJECT> class NCGPoolable : virtual public ICGPoolable<TOBJECT> { protected: NCGPoolable(); public: staticTOBJECT* Alloc() { return m_pPoolObject ->Alloc();} void Free(TOBJECT* p_pItem) { m_pPool ->Free(p_pItem);} private: virtual void OnFinalRelease() { Free((TOBJECT*)this);} private: staticCGPTR<CGPool::CObject<TOBJECT>> m_pPoolObject; CGPTR<CGPool::CObject<TOBJECT>> m_pPool; };  그래서 NCGPoolable<T> 클래스가 있다 . CGCIICho sanghyun’s Game Classes II 풀시스템 객체 풀 사용하기 (2)
  • 15. class foo: public NCGPoolable<foo> { … }; class foo: public NCGPoolable<foo> { … }; void main() { CGPTR<foo> pObject = foo ::Alloc(); … } void main() { CGPTR<foo> pObject = foo ::Alloc(); … }  ICGPoolable<T> 대신 NCGPoolable<T> 을 상속받으면 아주 간단해진다 !!! OnFinalRelease() 를 정의할 필요도 없다 !!  따로 전역으로 풀 객체 (CGPool::CObject<T>) 를 선언할 필요가 없다 . CGCIICho sanghyun’s Game Classes II 풀시스템 객체 풀 사용하기 (3)NCGPoolable<T> 자동 참조계수 처리된다 .
  • 16. template <class TOBJECT> CGPTR<TOBJECT> NEW() { __if_exists(TOBJECT::NCGPoolable) { return TOBJECT::Alloc(); } __if_not_exists(TOBJECT::NCGPoolable) { class OBJECT_CREATE : public TOBJECT, public CGReleaser::NDelete { }; return CGPTR<TOBJECT>(new OBJECT_CREATE()); } }; template <class TOBJECT> CGPTR<TOBJECT> NEW() { __if_exists(TOBJECT::NCGPoolable) { return TOBJECT::Alloc(); } __if_not_exists(TOBJECT::NCGPoolable) { class OBJECT_CREATE : public TOBJECT, public CGReleaser::NDelete { }; return CGPTR<TOBJECT>(new OBJECT_CREATE()); } };  매번 Pool 을 정의하고 OnFinalRelease() 하려면 귀찮다 !!! 그래서 NEW<T>() 함수에 기능 추가 ! CGCIICho sanghyun’s Game Classes II 풀시스템  NCGPoolable<T> 을 상속받았으면 T::Alloc() 함수를 호출하여 [Pool 에서 할당 ] 받는다 .  NCGPoolable<T> 을 상속받지 않았으면 [new 로 생성 ] 한다 . 객체 풀 사용하기 (4) NCGPoolable<T> & NEW<T>
  • 17. class foo: virtual public ICGReferenceCount { … }; class gee: virtual public NCGPoolable<gee> { … }; void main() { CGPTR<foo> p1 = NEW<foo>(); // new 를 사용해 동적 할당 CGPTR<gee> p2 = NEW<gee>(); // pool 에서 할당 … } class foo: virtual public ICGReferenceCount { … }; class gee: virtual public NCGPoolable<gee> { … }; void main() { CGPTR<foo> p1 = NEW<foo>(); // new 를 사용해 동적 할당 CGPTR<gee> p2 = NEW<gee>(); // pool 에서 할당 … }  NEW<T>() 함수를 사용하면 • NCGPoolable<T> 를 상속받으면 Pool 에서 할당된다 . • ICGReferenceCoount 만 상속받았다면 new 를 사용하여 동적 할당한다 . CGCIICho sanghyun’s Game Classes II 풀시스템 객체 풀 사용하기 (5) NCGPoolable<T> & NEW<T> 1. 그냥 일반 참조계수 2. 풀을 사용한 객체
  • 18. CGCIICho sanghyun’s Game Classes II 풀시스템 메모리 블록 풀 (1)  메모리를 할당 받아야 할 경우에는 임의의 크기를 할당 받아야 하기 때문에 CGPool::CObject<T> 로 힘들다 .  이를 위해 여러 개의 [ 메모리 블록 풀 ] 을 연결하여 사용한다 . 1k Memory Pool1k Memory Pool 2k Memory Pool2k Memory Pool 3k Memory Pool3k Memory Pool 64k Memory Pool64k Memory Pool . . . 1~1K Byte 할당 요구 시 1K~2K Byte 할당 요구 시 2K~3K Byte 할당 요구 시 63K~64K Byte 할당 요구 시 . . . 여러 크기의 메모리 블록 풀을 마련함 . (Buckets) 64K Byte 이상 할당 요구 시 일반적인 할당 사용 버킷들 (Buckets)
  • 19. CGCIICho sanghyun’s Game Classes II 풀시스템 메모리 블록 풀 (2)  메모리 블록 풀은 처리는 간단히 해당 메모리 풀을 선택하는 처리가 전부 ! class CGPool::CMemoryBlock { … vector<CGPTR<CGPool::CObjectTLS>> m_vectorMemoryBlockPool; … }; class CGPool::CMemoryBlock { … vector<CGPTR<CGPool::CObjectTLS>> m_vectorMemoryBlockPool; … }; #define MEM_POOL_ALLOC(size) CGPool::CMemoryBlock::GetInstance().Alloc(SELECT_BLOCK(size))#define MEM_POOL_ALLOC(size) CGPool::CMemoryBlock::GetInstance().Alloc(SELECT_BLOCK(size)) #define SELECT_BLOCK(size) ((size<=8192) ? ((size<=1024) ? ((size-1)>>7) : (((size-1)>>10)+7)) : ((size<=65536) ? (((size-1)>>12)+13) : (((size-1)>>14)+25))) #define SELECT_BLOCK(size) ((size<=8192) ? ((size<=1024) ? ((size-1)>>7) : (((size-1)>>10)+7)) : ((size<=65536) ? (((size-1)>>12)+13) : (((size-1)>>14)+25))) ICGMemory* CGPool::CMemoryBlock::Alloc(int p_iBlock) { return m_vectorMemoryBlockPool[p_iBlock]->Alloc(); } ICGMemory* CGPool::CMemoryBlock::Alloc(int p_iBlock) { return m_vectorMemoryBlockPool[p_iBlock]->Alloc(); }  할당을 요구하는 메모리의 크기에 따라 어떤 풀을 선택할 것인가는 매크로로 정의
  • 20. CGCIICho sanghyun’s Game Classes II 풀시스템 버퍼 시스템 (1)  할당된 메모리는 메모리 포인터가 아니라 객체를 리턴한다 . class ICGMemory : public CGBUF, virtual public ICGReferenceCount // 참조 계수 .. { … }; class ICGMemory : public CGBUF, virtual public ICGReferenceCount // 참조 계수 .. { … }; 1. WSABUF(CGBUF==WSABUF) 를 상속받는다 . 2. 참조계수 ICGMemory* MEM_POOL_ALLOC(int SIZE); void* MEM_POOL_ALLOC(int SIZE);  ICGMemory 는 참조계수로 동작하기 위해 정의 되었다 .  풀에서 할당을 요구할 때는 MEM_POOL_ALLOC 를 호출하면 된다 . MEM_POOL_ALLOC(30); MEM_POOL_ALLOC(1000); 30 Byte 를 할당요구하려면… 1000 Byte 를 할당요구하려면…
  • 21. CGCIICho sanghyun’s Game Classes II 풀시스템 버퍼 시스템 (2)  CGCII 에서는 풀에서 할당된 메모리를 사용하기 위해 CCGBuffer 를 제공한다 . CCGBuffer = WSABUF • buf → 메모리 버퍼 포인터 • len → 메모리 버퍼 크기 • 참조계수로 할당해제하기 위해 • 버퍼로 사용을 위해 각종 편리한 함수들을 포 함한다 . • 버퍼에 값 쓰기 (Append, <<, …) • 버퍼에서 값 읽기 (Head, Extract, >>, …) + 스마트포인터 + 편리한 버퍼 사용 • 실시간 바운드 체크+ 편리한 디버깅
  • 22. CGCIICho sanghyun’s Game Classes II 풀시스템 버퍼 시스템 (3)  CGCII 에서는 풀에서 할당된 메모리를 사용하기 위해 CCGBuffer 를 제공한다 . class CCGBuffer : public CGBUF { public: CCGBuffer(){} CCGBuffer(ICGMemory* p_ref) : CGBUF(p_ref->buf, 0), m_reference(p_ref) {} CCGBuffer(ICGMemory* p_ref, ULONG p_iP) : CGBUF(p_ref->buf, p_iP), m_reference(p_ref){} … public: Buffer 의 편한 사용을 위한 각종 함수들… private: CGPTR<ICGMemory> m_reference; }; class CCGBuffer : public CGBUF { public: CCGBuffer(){} CCGBuffer(ICGMemory* p_ref) : CGBUF(p_ref->buf, 0), m_reference(p_ref) {} CCGBuffer(ICGMemory* p_ref, ULONG p_iP) : CGBUF(p_ref->buf, p_iP), m_reference(p_ref){} … public: Buffer 의 편한 사용을 위한 각종 함수들… private: CGPTR<ICGMemory> m_reference; }; 1. WSABUF(CGBUF==WSABUF) 를 상속받는다 . 2. ICGMemory 를 멤버로 가진다 .
  • 23. CGCIICho sanghyun’s Game Classes II 풀시스템 버퍼 시스템 (4)  메모리 풀에서 할당 받아 사용하고 참조계수로 자동 할당 해제된다 . { CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000); … }; { CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000); … }; 1. Pool 에서 Memory 를 할당 받는다 . 3. 블록이 끝나면 bufferTEMP 가 소멸되며 자동으로 할당 해제한다 . CCGBuffer bufferCOPY = bufferTEMP;CCGBuffer bufferCOPY = bufferTEMP; 4. 대입하면 참조계수 1 이 증가한다 . 2. 할당 받은 Memory 를 bufferTEMP 에 들어가며 참조계수가 1 이 된다 .  CCGBuffer 는 ‘스마트 포인터’역할도 수행함 .  CCGBuffer 는 편리한 버퍼 사용을 위한 매우 다양한 기능을 제공해 준다 .( 추후 설명 ) CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000); bufferTEMP.Append<int>(1); bufferTEMP.Append<DWORD>(30); … bufferTEMP.Head<int>(4); CCGBuffer bufferTEMP = MEM_POOL_ALLOC(1000); bufferTEMP.Append<int>(1); bufferTEMP.Append<DWORD>(30); … bufferTEMP.Head<int>(4);
  • 24. CGCIICho sanghyun’s Game Classes II 풀시스템 메모리 블록 풀 (2)  이런 형태의 메모리 블록 풀은 LFH, tcmalloc, jemalloc 모두 사용하고 있음 . LFH jemalloc 16K Byte tcmalloc 32K Byte 선택적 CGCII CGPool 8K/64K/256K /1M Byte 블록 크기 (Granularity) 최대 적용 크기 (Max Range) 8/16/32/64 /128/258/512 총 128 개 Buckets 선택적 (*) 선택적 (**) 최대 53 개 풀 Google 뿐 아 니라 게임 클라 이언트 / 서버 등 널리 사용 . 특징 듣보잡 할당 오버헤드 (Overhead) 3~7% 수준4% 수준 4%+alpha 수준 8/16/32/~ /256 Byte 총 170 Buckets TLS 적용 No Yes Yes Yes Slow-start Aver-Convergence- ?Garbage Collection Algorithm Windows XP 이상 기본 동작
  • 25. CGCIICho sanghyun’s Game Classes II 풀시스템 풀 관리 – 재고 관리 (1)  풀 시스템에서 재고 관리를 제대로 하지 않으면 시스템의 불안정을 야기할 수 있다 ! 그래서 매우 중요하다 ! 재고가 너무 많이 저장해 놓으면 !재고가 너무 많이 저장해 놓으면 ! 재고가 너무 적게 저장해 놓으면 !!재고가 너무 적게 저장해 놓으면 !! 메모리 많이 잡아 먹어 시스템에 문제 야기 ! 풀을 하나 마나 !!! 오히려 풀 처리로 성능만 떨어뜨릴 뿐 !  풀 시스템의 핵심은 적절한 재고를 유지하는 것이 풀 시스템 안정화의 핵심 !  적절한 재고처리 시스템이 없다면 사실상 서버에서 풀 시스템을 운용하기에는 위 험부담이 너무 크다 !
  • 26. CGCIICho sanghyun’s Game Classes II 풀시스템 풀 관리 – 재고 관리 (2)  tcmalloc 의 경우 TLS cache 크기를 결정 짓는 데 TCP 의 “슬로우 스타트 (Slow- Start)” 알고리즘을 사용한다 한다 . TCP 의 슬로우 스타트 (Slow Start)  총 TLS cache 크기가 2M 가 넘게 되면 재고 처리를 시작한다 .
  • 27. CGCIICho sanghyun’s Game Classes II 풀시스템 풀 관리 – 재고 관리 (3)  CGPool 은 “평균 수렴 시스템”을 사용한다 .  평균과 표준편차를 구해 최대 저장 재고량을 결정하고 그 이상이 넘으면 재고를 처분하는 알고리즘이다 . 사용량 시간
  • 28. CGCIICho sanghyun’s Game Classes II 풀시스템 풀 관리 – 재고 관리 (3)  CGPool 은 기본적 재고관리뿐만 아니라 4 단계의 위기 관리 시스템이 있다 . Level 1 Level 2 Level 3 Level 4 → 일반적인 상황 앞에서 설명한 대로 동작한다 . → 한계 재고 처리량으로 보다 빨리 수렴하도록 한다 . → 재고를 전혀 쌓지 않는다 . (Pool 을 사용하지 않는 것과 동일하다 .) → 즉시 한계 재고량으로 수렴시킨다 .
  • 29. CGCIICho sanghyun’s Game Classes II 풀시스템 풀 관리 – 통계 처리  CGPool 은 사용량 , 할당량 , 해제량 , 생성량 , 소멸량 등 다양한 통계정보를 실 시간으로 제공한다 .  그 외에 수동 Prepare 기능 , Shrink 기능 등 다양한 관리 기능을 제공해 준다 . 저장된 객체 수 할당 중인 객체 수 현재 존재하는 총 객체 수 총 할당 요구 횟수 최대 객체 수 새로 생성된 객체 수 지워버린 객체 수 재사용률 생성 / 소멸 비율 객체 명
  • 30. CGCIICho sanghyun’s Game Classes II 풀시스템 Performance Time  CGPool 과 tcmalloc 그리고 기존의 LFH 의 성능을 비교한다 . 특정 크기의 메모리를 [1000] 번 할당 후 [1000] 번 할당 해제하는 것을 [1000] 번 반 복하는 테스트를 수행한다 .
  • 31. All Right Reserved ©CGLabs Socket PerformanceCGCIICho sanghyun’s Game Classes II
  • 32.  Windows Socket 에서 성능의 핵심은 뭐니 뭐니해도… • 송수신 중 복사의 최소화 ! [ 무복사 송수신 ] • Gathering! • 쓰레드 풀 • 효율적인 다중 쓰레드 처리 [ProAct 패턴 지원 ]  Windows Socket 에서 바로 이 기능들을 최대로 지원하고 최적화를 하는 것 이 고성능의 서버 엔진의 위한 관건 ! Overlapped I/O IOCP와 CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 의 성능
  • 33.  링 버퍼 ( 혹은 Circular) 사용 CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 수신 (Receive) 방법  선형 버퍼 사용 (Head/Tail Ptr 만 사용 )  CGCII 의 방법  선형 버퍼 사용 (Tail Ptr 만 사용 ) 일반적이고 전통적인 방법 대규모 메모리 사용하는 선형 버퍼 가장 간단한 방법 메모리 풀을 사용하여 한번 사용한 메모리를 버리고 새로 운 버퍼를 할당 받는 방법 Overlapped I/O 를 사용하여 수신 처리하는 방법은 다양할 수 있다 .
  • 34.  전형적인 Ring Buffer 를 사용한 방법 . Receive Message Buffer CGCIICho sanghyun’s Game Classes II 소켓성능 Head PtrTail Ptr Receive Receive  처리가 매우 복잡다 .  메시지의 처리를 위해 일반적으로 다른 버 퍼를 생성해 복사를 수행해야 한다 .  송수신량이 적을 때는 오히려 필요 이상의 부하와 메모리를 사용하게 된다 . 1. Head 포인터 앞부분과 Tail 포인터 뒷부분을 [Receive] 건다 .  Overlapped I/O 걸 메모리 효율이 좋다 .  수신과 메시지 처리를 각각 다른 쓰레드에서 처리가 가능하다 . 2. 수신이 완료 되면 메시지 버퍼에 받은 데이터를 복사한다 . 3. 메시지버퍼는 Work Thread 에 걸어주고 다시 1 번부터 무한반복 ~ 장점 단점 TCP 수신 (Receive) 처리 방법 (1) Head/Tail Ptr Receive Buffer (Ring Buffer) Overlapped I/O 를 걸 버퍼 메시지 처리
  • 35.  아주 간단하게 Receive 버퍼를 만들 수도 있다 . Receive 1. Tail 포인터 ( 혹은 len) 뒤쪽 메모리를 모두 [Receive] 건다 . 3. 다시 1 번부터 무한반복 . CGCIICho sanghyun’s Game Classes II 소켓성능 Tail Ptr 2. 수신이 완료되면 메시지를 처리하고 남은 것은 앞으로 복사해서 땡긴다 .  처리 구조가 매우 간단하다 .  이렇게 되면 메시지가 끊겨서 복사해야 하 는 일은 역시 없다 !!  I/O 와 메시지 처리를 각각 다른 쓰레드에서 처리할 수는 역시 없다 .  매번 메시지를 처리할 때마다 메모리 복사를 할 수도 있고 부하가 늘어나면 그 빈도가 증 가할 수 있다 . 장점 단점 TCP 수신 (Receive) 처리 방법 (2) Receive Buffer Receive
  • 36.  매번 받을 때마다 복사가 될 수 있는 위험이 있기 때문에 크게 버퍼를 잡고 Head 포인터와 Tail 포인터를 사용하여 수신처리를 한다 ! Receive  처리 구조는 매우 간단한 편이다 .  이렇게 되면 메시지가 끊겨서 복사해야 하 는 일은 없다 !! CGCIICho sanghyun’s Game Classes II 소켓성능 Head PtrTail Ptr  I/O 와 메시지 처리를 각각 다른 쓰레드에서 처리할 수는 없다 .  잦은 메모리 복사를 막을 수 있다 .  효율대비 고정적 메모리 사용이 많다 . 1. Tail 포인터 뒷부분에 대해서 [Receive] 를 건다 . 2. 수신이 완료되면 Head 포인터부터 메시지를 처리하고 Head 포인터를 옮긴다 . 3. Head 포인터 ( 혹은 Tail 포인터 ) 가 특정 위치를 넘어서면 앞으로 땡겨서 복사한 후 1 번부터 무한반복 장점 단점 TCP 수신 (Receive) 처리 방법 (3) Head/Tail Ptr Receive Receive Buffer Receive
  • 37.  앞의 방법들과 유사하나 한번 사용한 메모리는 버리고 항상 새로 할당 받는다는 것이 다르다 . Receive 2. 수신이 완료되면 Head 포인터부터 메시지를 처리하고 Head 포인터를 옮긴다 . 1. Tail 포인터 뒷부분에 대해서 [Receive] 를 건다 . CGCIICho sanghyun’s Game Classes II 소켓성능 3. Head 포인터 ( 혹은 Tail 포인터 ) 가 특정 위치를 넘어서면 새로 버퍼를 할당 받아 남은 데이터를 복사한 후 1 번부터 무한반복  처리 구조는 비교적 간단하다 .  이렇게 되면 불연속적 메시지 버퍼로 인해 복사를 해야 하는 일은 역시 없다 !!  I/O 와 메시지 처리를 각각 다른 쓰레드에서 처리하는 것도 문제없다 .  받은 메모리를 저장 후 사용하거나 재송신에 사용해 추가적으로 메모리 복사량 을 줄일 수 있다 .  버퍼 크기를 수신 상황에 따라 바꾼다면 더 금상첨화 .  ? 장점 단점 CGCII TCP 수신 (Receive) 처리 방법 Receive Buffer Head/Tail PtrHead PtrTail Ptr 참조계수 00 11
  • 38.  수신 받는 량에 따라 Buffer 의 크기를 변화시키는 것을 말한다 . 너무 크게 잡으면 CGCIICho sanghyun’s Game Classes II 소켓성능 CGCII 가변 수신 버퍼  고정된 크기로 버퍼를 잡을 경우 . 너무 작게 잡으면 더 많은 메모리가 소모된다 . 성능이 떨어지거나 큰 메시지를 받지 못할 수 있다 .  WSAIoctl() 함수 혹은 ioctlsocket() 함수와 FIONREAD 플래스를 사용하여 수신 한 데이터량을 확인하여 Receive Buffer 의 크기를 변경시킨다 . → 큰 크기의 버퍼를 새로 할당 받아야 하므로 반드시 풀 시스템이 기반이 되어야 한다 !  가변 수신 버퍼를 사용하면 성능과 메모리 사용량 모두를 해결할 수 있 다 .  CGCII 에서는 당연히 적용되어 있다 . ( 옵션으로 MIN SIZE 와 MAX SIZE 를 설 정할 수 있다 .)
  • 39. CGCIICho sanghyun’s Game Classes II 소켓성능 메시지 처리 (1)  데이터 수신과 메시지 처리의 방법적 문제에서도 성능의 차이가 있을 수 있다 .  Overlapped I/O 로 데이터 수신 후 핵심 처리 과정은 대략 아래와 같은 3 가지 단계이다 . • 받은 데이터를 메시지별로 분리하는 작업이 필요하다 . • 일반적으로 메시지 헤드 (Head) 에서 메시지 크기를 확인하고 메 시지 별로 분리작업을 한다 . • 분리된 각 메시지가 어떤 메시지인지를 확인하여 메시지에 맞는 처리를 하는 과정이다 . • 가장 부하가 많이 걸릴 수 있는 부분이다 . • 일반적으로 switch-case 문을 사용하여 처리한다 . • 데이터를 받기 위해 데이터를 받을 버퍼를 준비하고 정리한 후 다 시 Recv() 를 걸어주는 과정이다 .
  • 40.  수신 받은 데이터를 메시지 별로 분리해 처리하는 방법은 대략 아래 3 가지 정도로 정리 CGCIICho sanghyun’s Game Classes II 소켓성능 메시지 처리 (2) • 수신 받는 즉시 메시지 분리와 처리 그리고 Recv() 걸기까지 모두 처리한다 . • 수신 받은 후 메시지를 분리하여 큐잉하고 바로 Recv() 를 걸어준다 . • 메시지의 처리는 다른 쓰레드에서 큐잉된 메시지를 꺼내 처리한다 . • 수신 받은 후 데이터를 즉시 큐잉만 하고 Recv() 를 건다 . • 메시지 분리와 처리는 다른 Thread 에서 한다 . 메시지 분리 메시지 처리 Recv() 걸기+ + 메시지 분리 큐잉 Recv() 걸기+ + 큐잉 Recv() 걸기+ 메시지 처리디큐잉 + 메시지 처리디큐잉 + 메시지 분리 + I/O Thread I/O Thread I/O Thread Work Thread Work Thread
  • 41.  데이터를 받은 후 메시지 분리 , 메시지 처리를 모두 수행하고 Recv() 를 거는 방식이다 . CGCIICho sanghyun’s Game Classes II 소켓성능 메시지 처리 (2) – 방법 1 Recv 다시 걸기Recv 다시 걸기 메시지 수신메시지 수신 종료종료 메시지 처리메시지 처리 메시지 분리메시지 분리  처리 구조는 매우 간단한 편이다 .  데이터 수신이 많지 않을 경우 효율적이다 .  메시지분리와 처리를 쓰레드에 분산해서 처 리할 수 없다 .  단일 소켓으로 많은 데이터를 수신할 경우 다중 코어에 분산처리할 수 없어 비효율적 .  따라서 모든 데이터를 처리한 후 Recv() 를 걸어야 하기 때문에 데이터를 빨리 비울 수 없어 그만큼 수신 성능이 떨어진다 . 장점 단점 I/O Thread
  • 42.  데이터를 받은 후 메시지 분리하여 분리된 메시지 정보를 큐잉한 후 Recv() 를 거는 방식이다 .  링버퍼를 사용할 경우 이런 방법을 많이 사용한다 . CGCIICho sanghyun’s Game Classes II 소켓성능 메시지 처리 (2) – 방법 2 Recv 다시 걸기Recv 다시 걸기 메시지 수신메시지 수신 종료종료 메시지 큐잉 메시지 분리메시지 분리 메시지 처리메시지 처리 메시지 수신메시지 수신 종료종료 메시지 디큐잉 I/O Thread Work Thread  처리 과정이 좀 복잡하다  I/O 처리와 메시지처리를 분리해서 처리할 수 있다 .  빠르게 Recv() 를 다시 걸어 데이터 수신 성능을 향상시킬 수 있다 .  메시지 수신량이 적을 때는 오히려 추가적인 부담만 늘어날 수 있다 .  추가적인 메모리 부담이 있을 수 있다 . 장점 단점
  • 43.  데이터를 받은 후 즉시 큐잉만 하고 Recv() 를 거는 방식이다 . CGCIICho sanghyun’s Game Classes II 소켓성능 메시지 처리 (2) – 방법 3 Recv 다시 걸기Recv 다시 걸기 메시지 수신메시지 수신 종료종료 데이터 큐잉 메시지 처리메시지 처리 메시지 수신메시지 수신 종료종료 데이터 디큐잉 I/O Thread Work Thread  처리 과정이 좀 복잡하다  I/O 처리와 메시지처리를 분리해서 처리할 수 있다 .  가장 빠르게 Recv() 를 다시 걸어 가장 뛰 어난 데이터 수신 성능을 제공한다 .  메시지 수신량이 적을 때는 오히려 추가적인 부담만 늘어날 수 있다 . 장점 단점 메시지 분리메시지 분리
  • 44.  Overlapped I/O 를 사용하여 송신 (Send) 처리를 하는 방법은 크게 아래와 같이 방법이 있다 . CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 송신 (Send) 처리 방법 Gathering (A) Gathering (B) • 전송 요청 즉시 Overlapped I/O 를 걸어 Send 를 수행함 • 전송 빈도가 적을 때 효율적인 반면 많으면 전송 효율 급하락 . Gathering 버퍼를 두어 데이터를 이곳에 복사하여 전송하는 방법 Gathering 버퍼를 사용하지 않고 WSASend 의 Gathering 기능을 사용하 여 전송하는 방법 CGCII 에서 사용하는 방법 • 하나의 소켓당 한번에 하나의 Overlapped I/O 만 걸어 Send 를 수행하는 방법 . • Send 중이면 Queuing 하였다가 Send 가 완료 후 한꺼번에 전송한다 . • 전송 빈도가 많으면 효율 좋음 . 단순 Gathering 전송을 요청하면 무조건 Queuing 하고 일정 시간 마다 Queuing 된 데이터 를 전송하는 방법 ( 반응속도 느림 , 전송용 쓰레드 추가적으로 필요 )
  • 45. Overlapped Send Overlapped Send 전송 요청전송 요청 전송 끝전송 끝 전송 완료전송 완료 완료 처리 끝완료 처리 끝 Buffer AddRef() Buffer AddRef() Buffer Release() Buffer Release() CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 송신 (Send) 처리 방법 (1) – 단순  가장 일반적이고 간단한 Overlapped I/O 를 사용한 송신 처리 방법  한번의 Send 요청에 한번의 Overlapped I/O 를 거는 방법  처리 구조가 매우 간단한다 .  크기가 큰 메시지를 띄엄띄엄 전송한다면 효과가 매우 좋다 .  작은 메시지를 매우 빈번히 전송할 경우 CPU 를 매우 많이 사용하고 성능이 급격히 떨어진다 . ( 그냥 Send() 보다 더 떨어진다 .) 장점 단점
  • 46. 전송 요청전송 요청 자체 송신 버퍼에 memcpy() 자체 송신 버퍼에 memcpy() 전송 끝전송 끝 시작시작 전송할 것이 있나 ? 전송할 것이 있나 ? 완료 처리 끝완료 처리 끝 No Yes Overlapped SendOverlapped Send LOCK CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 송신 (Send) 처리 방법 (2) – 단순 Gathering  전송을 요청하면 무조건 Queuing 하고 Send 용 Thread 가 일정 시간 마다 Queuing 된 데이터를 확인하여 전송하는 방법  반응속도 느리고 전송용 쓰레드가 추가적으로 필요함 .  구시대의 유물로 비추 방법 !!! Send 용 Thread 전송 완료전송 완료 완료 처리 끝완료 처리 끝 완료처리 ( 버퍼 정리 ) 완료처리 ( 버퍼 정리 ) 일정시간 Sleep일정시간 Sleep LOCK
  • 47. 전송 요청전송 요청 자체 송신 버퍼에 memcpy() 자체 송신 버퍼에 memcpy() bSending?bSending? false true 전송 끝전송 끝 bSending=truebSending=true 전송 완료전송 완료 완료 처리완료 처리 전송할 것이 있나 ? 전송할 것이 있나 ? 완료 처리 끝완료 처리 끝 버퍼 내용 전송 버퍼 내용 전송 No Yes bSending=falsebSending=false LOCK LOCK CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 송신 (Send) 처리 방법 (3) – Gathering (A)  내부 송신 버퍼에 송신 내용을 복사한 후 모아서 Overlapped I/O 를 거는 방법  하나의 소켓에서 동시에 한번의 Overlapped Send 만 걸리도록 한다 . Overlapped SendOverlapped Send
  • 48. 1:N 전송은 왜 ? • 같은 버퍼를 여러 곳에 전송하면 각 소켓의 버퍼에 복사를 해 넣어야 한다 !!! • 특히 Server 에서 이런 전송은 매우 빈번하다 ! SocketSocket SocketSocket SocketSocket SocketSocket . . . Message Buffer Send BufferSend Buffer Send BufferSend Buffer Send BufferSend Buffer Send BufferSend Buffer 복 사 복 사 복 사 복 사 CGCIICho sanghyun’s Game Classes II 소켓성능  이렇게 되면 결론적으로 Overlapped I/O 의 가장 큰 장점이 무력화된다 !!!!! TCP 송신 (Send) 처리 방법 (4)  처리 구조가 좀 복잡다 .  한꺼번에 모아 전송하므로 Overlapped I/O 량을 줄일 수 있다 .  작은 메시지를 빈번히 전송할 경우 성능이 우수하다 .  전송 때 반드시 ! memcpy() 를 동반한다 .  따라서 큰 메시지 전송이나 1:N 전송을 수 행하면 잦은 메모리 복사로 인해 성능이 급 격히 떨어질 수 있다 . 장점 단점
  • 49. int WSASend ( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE ); int WSASend ( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE );  어떻게 IOCP 의 장점을 가진체 I/O 를 최소화할 수 있는 방법은 없을까 ? → 하지만 !! WSASend 에 Gathering 기능을 사용하면 어떨까 ? Gathering 전송을 지원한다 ! → 일반적으로 한꺼번에 모으려면 복사를 해야 한다 !!! CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 송신 (Send) 처리 방법 (5)
  • 50. 포인터만 큐잉포인터만 큐잉 bSending?bSending? Overlapped Send Overlapped Send false true 전송 요청전송 요청 전송 끝전송 끝 완료완료 Buffers Release()Buffers Release() 큐잉된 것이 있나 ? 큐잉된 것이 있나 ? 완료 처리 끝완료 처리 끝 큐잉된 것 모두 꺼내서 ~ 큐잉된 것 모두 꺼내서 ~ No Yes bSending=truebSending=true bSending=falsebSending=false LOCK LOCK Buffer AddRef() Buffer AddRef() Overlapped Send Overlapped Send Gathering Send CGCIICho sanghyun’s Game Classes II 소켓성능 TCP 송신 (Send) 처리 방법 (6) - Gathering (B)
  • 51. CGCIICho sanghyun’s Game Classes II 소켓성능 Message Buffer Head Ptr Tail Ptr Send Buffer  자 그렇다면 에코 송수신을 처리할 때 기존의 링 - 버퍼를 사용하고 그냥 모아 전송을 사용하면 어떻게 될까 ? 복 사 복 사 복 사 복 사 복 사 Receive Send 메시지 생성기 복 사 메아리 (Echo) 송수신 (1)
  • 52. CGCIICho sanghyun’s Game Classes II 소켓성능 Message Buffer Head Ptr Tail Ptr Send Buffer  CGCII 의 송수신 방법을 사용하여 Echo 전송을 수행한다고 하면… Receive Send 메아리 (Echo) 송수신 (2)  아무런 복사를 수행하지 않고 단지 참조계수만으로 처리됨 ! 112233445566
  • 53. CGCIICho sanghyun’s Game Classes II 소켓성능 Performance Time  Socket 의 Echo 송수신 테스트를 수행한다 .
  • 54.  CGCII 의 Socket 의 특성에 따라 다른 튜닝을 할 수 있도록 해준다 . CGCIICho sanghyun’s Game Classes II 소켓성능 CGCII Socket Tunning 일반 Overlapped 전송 Gathering 전송을 지원하는 전송 받는 즉시 메시지 처리 받은 데이터 큐잉 후 다른 쓰레드가 처리 (I/O 와 Work 의 분리 ) TCP Socket Send 버퍼 크기 Gather 큐 최대 깊이 TCP Socket Receive 버퍼 크기 수신 버퍼 크기 ( 최소 / 최대 ) 수신 큐 최대 깊이
  • 55.  메신저나 채팅을 위한 소켓 CGCIICho sanghyun’s Game Classes II 소켓성능 CGCII Socket Tunning Send/Receive 가 비교적 적다 대규모 접속이 필요하다 .  다운로드 서버를 위한 소켓 Send 가 엄청나게 많다 . 같은 데이터를 여러 군데 전송한다 .  서버간 연결을 위한 소켓 단일 연결로 많은 Send/Receive 수행 접속 안정성 최대화 필요  일반 게임 서버에 접속하는 유저용 소켓 Send/Receive 가 중간 정도 ~  다운로드 클라이언트를 위한 소켓 Receive 량이 엄청나게 많다 . Receive 후 처리 과정이 매우 길다 . Send-Simple, Receive-Simple 모든 버퍼 최소 크기 설정하여 Socket 당 사용 메모리 최소화 Send-Gather 혹은 전용 모든 버퍼 최소 크기 설정하여 Socket 당 사용 메모리 최소화 Send-Simple, Receive-Queued 수신버퍼 크기 최대화하여 수신효율 극대화 . Send-Gather, Receive-Queued 송수신버퍼 크기 최대화하여 송수신효율 모두 극대화 . Send-Gather, Receive-Simple 송수신 버퍼 크기 중간 정도 . Socket 의 특성에 따라 최적의 성능을 내기 위해서 다양한 튜닝이 있을 수 있다 .
  • 56. CGCIICho sanghyun’s Game Classes II 소켓성능  매번 전송 때마다 메시지를 작성하는 것이 아니라 한번 작성해 놓고 지속적으로 사용 하는 메시지를 의미한다 .  메시지의 많은 부분이 정적 메시지일 수 있다 . 정적 메시지 버퍼 (1)  한번 작성하면 끝날 때까지 메시지를 계속 쓰는 것이 아니라 일정 시간마다 갱신해야 하는 경우 한번 작성한 메시지를 여러 번 사용할 수도 있다 .  어떠한 메시지는 대부분의 값은 동일하고 일부 값만 살짝 틀린 메시지들이 있을 수 있다 . 그럴 경우 나머지 설정된 값들은 그대로 사용하고 수정된 부분만 바꾸어 전송 하는 것을 의미한다 . 이것을 위한 ‘메시지 버퍼 풀 (CGPool::CBuffer<I>)’ 을 제공한다 .  메시지 버퍼 풀  정적 메시지 버퍼 (2)  정적 메시지 버퍼 (1)
  • 57. CGCIICho sanghyun’s Game Classes II 소켓성능 접속종료와 소켓 재사용  접속종료 처리시 DisconnectEx 를 사용하여 소켓 재사용 할 수 있다 . 따라서 매번 socket 을 생성 / 소멸 시킬 필요가 없다 !  하지만 DisconnectEx 가 간혹 완료가 되지 않는 경우를 대비해야 한다 . ( 보통 0.000001% 정도의 낮은 확률로 DisconnectEx 가 완료되지 않는 경우가 있 고 CGCII 는 이를 대비해 DisconnectEx 를 건 Socket 의 List 를 보관 후 일정 시간 동안 접속 종료처리가 되지 않으면 강제 완료 처리한다 .)  AcceptEx 는 접속 받기 성능을 높여준다 .  AcceptEx 와 ConnectEx 를 사용하면 접속 처리를 비동기함수를 사용하여 처리가 가 능하다 .  AcceptEx 와 ConnectEx 는 접속받기와 접속하기를 IOCP 에 통합할 수 있도록 해준 다 .  DisconnectEx 와 Socket Reuse 의 사용  AcceptEx 와 ConnectEx 의 사용
  • 58. CGCIICho sanghyun’s Game Classes II 소켓성능 Lock-free 의 광범위한 적용  Lock-free Stack 이나 Queue 말고도 많은 곳에 Lock-free 처리를 적용하여 Critical- section 의 사용을 최소화하였다 . bool foo::Start() { long preState; preState = InterlockedExchange(&m_bStart, 1); RETURN_IF(preState==0, false); … bool foo::Start() { long preState; preState = InterlockedExchange(&m_bStart, 1); RETURN_IF(preState==0, false); … bool foo::Start() { long preState; preState = InterlockedCompareExchange(&m_bState, STATE_SYN, STATE_CLOSE); RETURN_IF(preState==STATE_CLOSE, false); … bool foo::Start() { long preState; preState = InterlockedCompareExchange(&m_bState, STATE_SYN, STATE_CLOSE); RETURN_IF(preState==STATE_CLOSE, false); …
  • 59. CGCIICho sanghyun’s Game Classes II 소켓성능 최적 자료 구조 사용  빈번한 삽입이나 삭제되는 큐 같은 것의 처리를 위해 CGD::circular_list<T> 등을 사 용한다 . ( 대표적으로 각종 Manager 들… ) std::list CGD::circular_list Head Tail Head Tail
  • 60. CGCIICho sanghyun’s Game Classes II 소켓성능 반복 처리 최소화  최대한 검색하거나 찾는 과정에서 Loop 처리 과정을 제거했다 . void Manager::Remove(foo* p_pobject) { list<CGPTR<foo>>::iterator iter = m_objects.begin(); list<CGPTR<foo>>::iterator iterEnd = m_objects.end(); for(; iter!=iterEnd; ++iter) { if(*iter==p_pObject) { m_objects.erase(iter); break; } } } void Manager::Remove(foo* p_pobject) { list<CGPTR<foo>>::iterator iter = m_objects.begin(); list<CGPTR<foo>>::iterator iterEnd = m_objects.end(); for(; iter!=iterEnd; ++iter) { if(*iter==p_pObject) { m_objects.erase(iter); break; } } } 제거를 위해 Iterator 을 돌아야 한다 !
  • 61. CGCIICho sanghyun’s Game Classes II 소켓성능 반복 처리 최소화  최대한 검색하거나 찾는 과정에서 Loop 처리 과정을 제거했다 . // Error & Exception 처리 생략 !! void Manager::Add(foo* p_pobject) { m_objects.push_front(p_pobject); p_pobject->m_iter = m_objects.begin(); } // Error & Exception 처리 생략 !! void Manager::Add(foo* p_pobject) { m_objects.push_front(p_pobject); p_pobject->m_iter = m_objects.begin(); } 1. 생성 할 때 iterator 을 저장해 놓는다 . void Manager::Remove(foo* p_pobject) { m_objects.erase(m_objects->m_iter); } void Manager::Remove(foo* p_pobject) { m_objects.erase(m_objects->m_iter); } 2. 한 번에 지운다 !  당연히 std::map 보다도 훨씬 빠르다 !! (std::map 도 O(log n) 의 부하가 필요하고 한 스텝 자체의 크기와 Balancing 을 위한 부하도 무시할 수 없다 .)
  • 62. CGCIICho sanghyun’s Game Classes II 질문 ? 중간 질문 ? 소켓성능

Editor's Notes

  1. 풀은 자주 할당하고 할당 해제되는 객체를 매번 할당하고 할당해제 하지 않고 풀을 만들어 재활용하자!!!는 개념의 시스템이다! 기본적인 컨셉은 아주 간단하다! 풀의 구현은 인터넷이나 기존의 서적에 소개되어 있다~ 그럼 어떤 식의 풀들이 있는지 알아보자~
  2. 풀은 자주 할당하고 할당 해제되는 객체를 매번 할당하고 할당해제 하지 않고 풀을 만들어 재활용하자!!!는 개념의 시스템이다! 기본적인 컨셉은 아주 간단하다! 풀의 구현은 인터넷이나 기존의 서적에 소개되어 있다~ 그럼 어떤 식의 풀들이 있는지 알아보자~
  3. TLS를 사용하여 성능을 향상시킬 수 있다!! 아무리 Lock-Free라 해도 그냥 값을 복사하는 것보다는 성능이 확실히 떨어진다. 그래서 Lock의 최소화를 위해 TLS를 사용한다.
  4. 원리는 간단하다. 1. CGCII의 Pool이란 그냥 Garbage를 저장해 놓는 Stack일 뿐이다! 2. 처음에 이 Stack에는 아무런 Garbage는 존재하지 않는다. 3. 할당을 요구하면 먼저 Garbage Stack을 검사한다. 만약 아무것도 없으면 즉석에서 새로 할당해서 되돌려준다. 4. 다 사용 후 할당해제하게 되면 할당해제한 객체를 지우지 않고 Garbage의 Stack에 저장해 놓는다. 5. 다음에 할당 요구할 때 이 저장해 놓은 객체를 하나 빼내어 할당해준다.
  5. 서버는 멀티쓰레드 환경에서 동작하므로 기본적으로 Thread-Safe해야 한다. 그러기 위해 반드시 Lock이 필요하다. 성능을 향상시키기 위해서는 Lock-Free Stack을 사용한다.이것만으로 엄청난 성능 향상을 낼 수 있다.
  6. 서버는 멀티쓰레드 환경에서 동작하므로 기본적으로 Thread-Safe해야 한다. 그러기 위해 반드시 Lock이 필요하다. 성능을 향상시키기 위해서는 Lock-Free Stack을 사용한다.이것만으로 엄청난 성능 향상을 낼 수 있다.
  7. 서버는 멀티쓰레드 환경에서 동작하므로 기본적으로 Thread-Safe해야 한다. 그러기 위해 반드시 Lock이 필요하다. 성능을 향상시키기 위해서는 Lock-Free Stack을 사용한다.이것만으로 엄청난 성능 향상을 낼 수 있다.
  8. 서버는 멀티쓰레드 환경에서 동작하므로 기본적으로 Thread-Safe해야 한다. 그러기 위해 반드시 Lock이 필요하다. 성능을 향상시키기 위해서는 Lock-Free Stack을 사용한다.이것만으로 엄청난 성능 향상을 낼 수 있다.
  9. 서버는 멀티쓰레드 환경에서 동작하므로 기본적으로 Thread-Safe해야 한다. 그러기 위해 반드시 Lock이 필요하다. 성능을 향상시키기 위해서는 Lock-Free Stack을 사용한다.이것만으로 엄청난 성능 향상을 낼 수 있다.
  10. TLS를 사용하여 성능을 향상시킬 수 있다!! 아무리 Lock-Free라 해도 그냥 값을 복사하는 것보다는 성능이 확실히 떨어진다. 그래서 Lock의 최소화를 위해 TLS를 사용한다.
  11. TLS를 사용하여 성능을 향상시킬 수 있다!! 아무리 Lock-Free라 해도 그냥 값을 복사하는 것보다는 성능이 확실히 떨어진다. 그래서 Lock의 최소화를 위해 TLS를 사용한다.
  12. Object Pool은 객체의 할당을 위한 Pool이다. 사용방법은 매우 간단하다!!! 첫째 CGPool::CObject&amp;lt;T&amp;gt; 라고 하면 된다~~ 그러면 그냥 Alloc(), Free()함수를 사용하여 객체의 할당과 할당해제를 수행할 수 있다 Pool 객체의 대상이 되는 객체는 무조건 ICGPoolable&amp;lt;T&amp;gt;을 상속받아야만 한다!!! 그리고 참조계수로 관리되므로 OnFinalRelease에 delete this가 아니라 Free(this)가 호출되어야 한다. 즉 다 사용되면 지워지는 것이 아니라 풀로 되돌려지는 것이다.
  13. 생성하려는 객체는 반드시 ICGPoolable&amp;lt;T&amp;gt;를 상속받아야 한다. 사용할 때는 그냥 Alloc()함수를 호출하면 되고… 사용이 끝나게 되면 Free()함수를 호출하면 되는데 일반적으로 OnFinalRelease()함수에서 해준다.
  14. Alloc()/Free()를 사용하고 또 매번 OnFinalRelease()를 호출하도록 하려면 귀찬다. 따라서 NCGPoolable을 지원해준다.
  15. 또 NCGPoolable&amp;lt;T&amp;gt;만 상속받았으면 NEW&amp;lt;T&amp;gt;로 생성한다면 자동으로 Pool에서 할당을 해준다.
  16. 또 NCGPoolable&amp;lt;T&amp;gt;만 상속받았으면 NEW&amp;lt;T&amp;gt;로 생성한다면 자동으로 Pool에서 할당을 해준다.
  17. 메모리 버퍼의 동적 할당은 이런 객체 풀만으로는 해결할 수 없다. 왜냐하면 임의의 크기로 할당 받아야 하기 때문이다.
  18. 메모리 버퍼의 동적 할당은 이런 객체 풀만으로는 해결할 수 없다. 왜냐하면 임의의 크기로 할당 받아야 하기 때문이다.
  19. 메모리 버퍼의 동적 할당은 이런 객체 풀만으로는 해결할 수 없다. 왜냐하면 임의의 크기로 할당 받아야 하기 때문이다.
  20. Memory Block Pool은 객체 Pool과 다른 크기의 Memory 객체의 Pool을 엮은 것이다. 간단하다!!! 특정 크기의 메모리를 요청하면 그 크기보다 한 단계 큰 메모리 풀에서 해당 블록을 할당해주는 것이다.
  21. Windows의 기본 할당자와 CGPool의 성능을 비교한다.(Windows는 기본적으로 16Kbyte까지 LFH가 적용된다.)