SlideShare a Scribd company logo
TERA 서버의
Modern C++ 활용기
Bluehole
이상헌
1
목차
1. Introduction
2. 스마트포인터의 오남용과 Move Semantics
3. Variadic Template and some pratical techniques
4. 미래와 약속 (Future-Promise)
5. Q&A
2
발표자 소개
• Software Engineer
• Interest: C++, Concurrency, Server-Side Techs...
• 경력
• 2014 – 현재 : Bluehole 테라본부 서버팀
• 2012 – 2014 : M.S. Programming Languages Lab, KAIST
• 2004 : 전국 이야기 말하기 대회 대구지역 2등, 지역대표
• Contact
• developistbv@gmail.com
3
The Exiled Realm of Arborea
Free-Targeting Action MMORPG
2007-2011: 개발
2011.01 : 한게임 서비스
2016.01 : NEXON 서비스
NA, EU 포함 7개 지역에서 서비스 중
4
발표하기 앞서
• C++03 기준으로 작성된 테라서버 코드에서 Modern C++를 활용한 사례를 소개합니다
• 더 많은 활용 예가 공유되었으면 하는 바램에 발표하게 되었습니다.
• 과거가 ‘잘못 되었다’가 아니라, 이제 ‘더 나은 길’이 열렸다는 것을 소개 하는게 목적입니다.
• 보안 문제로 실제 테라 코드를 담진 않았습니다.
• (*)가 표시된 슬라이드는 부연 설명을 위한 슬라이드입니다.
• 발표 땐 시간 관계상 다루지 않을 수 있습니다.
• 저작권법 제 35조의 제 3 ‘공정이용’ 조항에 따라 교육과 연구 목적으로 일부 저작물을 인용
하였습니다. 혹시 문제가 있을 경우 메일로 연락주시면 적절한 조치를 취하겠습니다.
5
1. Introduction
2. 스마트포인터의 오남용과 Move Semantics
3. Variadic Template and some pratical techniques
4. 미래와 약속(Future-Promise)
5. Q&A
6
리빙 포인트: 발표 중간중간에 있는 이런 슬라이드는 진행 상황을 알려준다
필요한 사전 지식
• smart pointer (RAII)
• 객체의 생성 소멸 관리를 위해서 사용하는 객체
• shared_ptr<T>, unique_ptr<T>
• t_ptr<T>
• shared_ptr<T>처럼 Reference Count를 공유
• 침습형(invasive) 카운터를 사용
• Move Semantics, Rvalue Reference, Perforect Forwarding...
7
Cppcon2015 – Writing Good C++14 中
• “Smart pointers” are popular
• To represent ownership
• To avoid dangling pointers
• “Smart pointers” are overused
• Can be expensive
• E,g., shared_ptr
• Can mess up interfaces fore otherwise simple function
• E.g. unique_ptr and shared_ptr
• Often, we don’t need a pointer
• Scoped objects
• We need pointers
8
Reference Count의 증가 연산이 많이 일어나기 때문
shared_ptr의 문제는 t_ptr의 문제와 같다
void BattleField::OnAttack(int gameId) {
t_ptr<User> attacker = GUserManager->GetUser(gameId);
for(auto it = mUserList.begin(); it != mUserList.end(); ++it) {
t_ptr<User> target = *it;
IsSameBattleFieldTeam (attacker, target);
}
}
bool BattleField::IsSameBattleFieldTeam(t_ptr<User> user1, t_ptr<User> user2) {
t_ptr<BFChannel> channel = type_cast<BFChannel*>(user1->GetChannel());
if(channel) { … }
}
9
Bad Code!
사례1) 빠른줄만 알았던 type_cast
template<typename T, typename S>
t_ptr<T> type_cast<T>(t_ptr<S> s)
{<29.9%
if(s && TypeTable[Idx<S>][Idx<T>])<0.1%
{
return t_ptr<T>(*s);
}<70.0%
return nullptr;
}
template<typename T, typename S>
T* type_cast<T>(S* s)
{
if(s && TypeTable[Idx<S>][Idx<T>])
{
return static_cast<T>(s);
}
return nullptr;
}
뜻밖의 복사생성자와 소멸자가 호출!
10
사례1) 빠른줄만 알았던 type_cast (수정)
template<typename T, typename S>
t_ptr<T> type_cast<T>(t_ptr<S>& s)
{
if(s && TypeTable[Idx<S>][Idx<T>])
{
return t_ptr<T>(*s);
}
return nullptr;
}
template<typename T, typename S>
t_ptr<T> type_cast<T>(t_ptr<S>&& s)
{
if(s && TypeTable[Idx<S>][Idx<T>])
{
return t_ptr<T>(*s);
}<29.9%
return nullptr;
}
뜻 밖의 호출들이 사라졌다!
11
다시는 atomic 연산을 무시하지마라
• Atomic Increment/Decrement 연산은 (생각보다) 비싸다.
• ++ 처럼 가볍게 여기지 말자
• 본의아니게 dynamic_cast만큼 느린 type_cast를 쓰고 있었다.
• 서비스엔 아무런 지장이 없었음 (0.1ms vs 0.09ms, 호출양도 적음)
• 이처럼 스마트포인터의 복사생성이 의미 없이 발생할 때가 많다.
• type_cast는 그저 하나의 예제일 뿐
12
사례 2) 빠른줄만 알았던 Vector
• GatherGameObjectList 라는 함수가 오래 걸린다.
• 주위에 있는 GameObject의 t_ptr를 Vector에 저장 하는데 왜 느리지?
• Vector 예찬론자로서 인정할 수 없는 일
• Reallocation에서 RefCount++ / RefCount-- 가 일어나는걸 확인
13
Vector Reallocation (push_back)
A B C D
A B C D E
Step 1. 재할당 및 복사 A B C D
Step 2. 원본 객체 소멸
Step 3. 새 객체 삽입
RefCount ++ *4
RefCount -- *4
RefCount ++
E객체 E를 추가하려 할 때 R1
R2
R1
R2
불필요한 연산!
14
Move Semantics
• Rvalue Reference
• Object&& object = GetObject();
(cf. Object& object = otherObject;)
• 생성자나 대입연산자가 Rvalue를 사용할 땐 ‘소유권’ 을 넘겨주는 것만으로 충분
하다
Object(Object&& other){
mChild = other.mChild;
other.mChild = nullptr;
}
Object(Object& other){
DeepCopy(mChild, other.mChild);
}
15
소유권을 계승하는 중입니다
• 사라질 객체는 소유권을 위임하는 것으로 충분하다.
t_ptr<T>(t_ptr<T>&& t) {
mT = t.mT
t.SetNullWithoutRelease();
}
t_ptr<T>& operator=(t_ptr<T>&& t) {
release();
mT = t.mT
t.SetNullWithoutRelease();
}
• 와! 이제 Vector Reallocation 때 Move Constructor가 호출 되겠죠?
• 아니요.
16
Succeeding you, rvalue(?)
Perfect Forwarding
• Custom allocator에서 쓰는 C++03 버전 construct 함수가 문제
void construct(pointer _ptr, const _Ty& _Val) → Lvalue Reference가 되어버렸다!
{
::new ((void*)_Ptr) value_type(_Val));
}
• Perfect Forwarding을 하도록 수정하여 해결
void construct(pointer _Ptr, _Ty&& _Val) → Universal Reference!
{
::new ((void*)_Ptr) value_type(forward<_Ty>(_Val)); // R->R, L->L로 처리
}
17
개선 후 CPU 점유율 변화
• 시나리오: 다수의 채널에서 다수의 PC가 뭉쳐서 NPC 무리와 전투
주위의 NPC 수 Before Avg After Avg
N 12.6% 7.8%
2N 23.6% 18.4%
3N 40.0% 30.0%
4N 57.5% 37.8%
18
(*) Live 서버에서 평균 CPU 사용률은 1번 케이스보다 낮음
개선 후 Sample Rate 변화 (3N)
• RefCount 연산의 Sample Rate 변화: 47.5% → 26.5%
• Reallocation의 Sample Rate 변화
Before(14.8%)
After(2.8%)
19
(*) Cppcon2015 – Writing Good C++14 中
• 스트롭스트룹 할아버지께서 말씀하시길
• Shared_Ptr이 지나치게 남용(Overuse)되고 있다는 것을 느꼈다.
• 많은 사람들이 shared_ptr를 파라미터(by value)로 쓰고, 또 리턴하고 있다.
• 이러면 RefCount가 의미에 맞지않게 올라가고 내려가는 것이 반복된다.
• Dangling Pointer는 룰을 잘 따르고(툴을 이용하면) 다 잡을 수 있다.
• Rules for Eliminating Dangling Pointers
• Owner와 Non-Owner를 구분한다.
• Raw-Pointer는 모두 Non-Owner이다.
• Pointer가 Owner의 Scope를 벗어나려하는 시도를 모두 잡는다.
• Parameter로 받아서 Return으로 돌려주는건 괜찮음
• Owner를 가지고 있는 객체는 Owner다.
• vector<T*>, owner<T*>
20
(*) Cppcon2015 – Writing Good C++14 中
int* f(int* p)
{
return p; //괜찮음
return new int{7} //Owner로
받으면 O.K.
int x = 4;
return &x; //극혐
}
owner<int*> k = f(p); // O.K.
21
(*) GSL: Owner<T>
• GSL = Guideline Support Library (Global Starcraft II League)
• https://github.com/Microsoft/GSL
• STL = The ISO C++ standard library
• C++ Core Guideline의 규칙들을 지원하는 라이브러리
• https://github.com/isocpp/CppCoreGuidelines
• template <class T> using owner = T;
• 정적분석을 위해
• 문서화를 위해
22
1. Introduction
2. 스마트포인터의 오남용과 Move Semantics
3. Variadic Template and some pratical techniques
4. 미래와 약속(Future-Promise)
5. Q&A
23
리빙 포인트: 과거에 작성된 스마트포인터엔 Move Semantics를 구현해주자
사전 지식: Template in C++
• template <typename T>
class Stack;
{
…
bool push(T elem);
T pop();
}
• Stack<int> mStack;
24
‘tnew’ in TERA Server (과거)
#define MEMORY_ALLOC_ARG_NUM 40
#define MEMORY_tnew_print(z, n, _) 
template<class Type BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> 
Type* tnew(BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) 
{ 
Type* memory = (Type*)AllocMemory(sizeof(Type)); 
new(memory) Type(BOOST_PP_ENUM_PARAMS(n, arg)); 
return memory; 
} 
BOOST_PP_REPEAT(BOOST_PP_INC(MEMORY_ALLOC_ARG_NUM), MEMORY_tnew_print, _)
25
‘tnew’ in TERA Server (먼 과거)
template<class Type, class Arg1, class Arg2, class Arg3>
Type* tnew(Arg1 arg1, Arg2 arg2, Arg3 arg3)
{
Type* memory = (Type*)AllocMemory(sizeof(Type));
new(memory) Type(arg1, arg2, arg3);
return (Type *)memory;
}
• 과거의 나: 이게 더 깔끔한거 같은데요? -_-
26
• 사수님: 헷갈린다면 이 리비전의 코드를 보고 공부 하거라.
‘tnew’ in TERA Server(먼 과거)
27
Variadic Template
• 임의의 인자 개수를 지원하는 템플릿 형식(클래스, 함수 모두 지원)
• Ellipsis operator(= …)와 함께 사용
- arguments, parameters, sizeof op, initializer list of array, member initializer list, lambda capture,
exception specification list
• template<typename... Args >
void DoFunc(Args... args) {
Func(args…);
}
DoFunc(1, “hi”) = void DoFunc<int, string>(1, “hi”);
DoFunc() = void DoFunc<>()
28
당연히 될 것 같은 것들
• template<typename T, typename... Args>
void DoFunc(T obj, Args&... args) {
Func(obj, args...);
}
• template<typename... Args>
void DoFunc(Args... args) {
const unsigned argSize = sizeof...(Args);
}
• template<typename... Base>
class DoClass : public Base... {
DoClass(Base&&... base): Base(base)... {}
}
29
#define MEMORY_ALLOC_ARG_NUM 40
#define MEMORY_tnew_print(z, n, _) 
template<class Type BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> 
Type* tnew(BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) 
{ 
Type* memory = (Type*)AllocMemory(sizeof(Type)); 
new(memory) Type(BOOST_PP_ENUM_PARAMS(n, arg)); 
return memory; 
} 
BOOST_PP_REPEAT(BOOST_PP_INC(MEMORY_ALLOC_ARG_NUM), MEMORY_tnew_print, _)
‘tnew’ in TERA Server
template<class Type, class... Args>
Type* tnew(Args&& ... args)
{
Type* memory = (Type*)AllocMemory(sizeof(Type));
new(memory)Type(forward<Args>(args)...);
return memory;
}
30
성공에 힘 입어 여기저기 수정
Typelist<typename Head, typename Tail>
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, TYPELIST_2(T2)>
#define TYPELIST_3(T1, T2,T3) Typelist<T1, TYPELIST_3(T2, T3)>
…
TYPELIST_5(GameObject, Creature, User, Npc, Item);
Typelist<typename... T>
Typelist<GameObject, Creature, User, Npc, Item>;
• TypeList, PacketWriter, LogWriter 등등…
Before
After
31
끝판왕에 도전
#define ASYNC_JOB_print(z, n, _)
template <class T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> 
struct BOOST_PP_CAT(AsyncJob, n) : public Job 
{ 
typedef void (T::*MemFunc)(BOOST_PP_ENUM_PARAMS(n, Arg)); 
BOOST_PP_CAT(AsyncJob, n)(T* obj, MemFunc memFunc BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) 
: mObj(obj), mMemFunc(memFunc) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, ASYNC_PARAM_ASSIGN_print, ~) 
{ 
AddRef(mObj, REUSE_NDC); 
BOOST_PP_REPEAT(n, ASYNC_ARG_ADDREF_print, REUSE_NDC) 
} 
void OnExecute() 
{ 
(mObj->*mMemFunc)(BOOST_PP_ENUM_PARAMS(n, mArg)); 
ReleaseRef(mObj, REUSE_LFECALL); 
BOOST_PP_REPEAT(n, ASYNC_ARG_RELREF_print, REUSE_NDC) 
} 
MemFunc mMemFunc; 
T* mObj; 
BOOST_PP_REPEAT(n, ASYNC_ARG_DECL_print, _) 
};
#define ASYNC_print(z, n, _) 
template <class T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> 
bool Async(void (T::*memFunc)(BOOST_PP_ENUM_PARAMS(n, Arg)) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) 
{ 
Job* job = tnew<BOOST_PP_CAT(AsyncJob, n)<T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, Arg)> >(static_cast<T*>(this),
memFunc BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, arg)); 
return mJobQueue.Do(job); 
}
32
template <typename T, typename Arg1T, typename Arg2T>
struct AsyncJob2 : public Job {
typedef void (T::*MemFunc)(Arg1T, Arg2T);
AsyncJob2(T* obj, MemFunc memFunc, Arg1T arg1, Arg2T arg2): mObj(obj), mMemFunc(memFunc), mArg1(arg1), mArg2(arg2) {
AddRef(mObj, REASON_NDC);
AddRef(mArg1, REASON_NDC);
AddRef(mArg2, REASON_NDC);
}
void OnExecute() {
(mObj->*mMemFunc)(mArg1, mArg2);
ReleaseRef(mObj, REASON_NDC);
ReleaseRef(mArg1, REASON_NDC);
ReleaseRef(mArg2, REASON_NDC);
}
MemFunc mMemFunc;
T* mObj;
Arg1T mArg1;
Arg2T mArg2;
};
template <typename T, typename Arg1T, typename Arg2T>
bool Async(void (T::*memFunc)(Arg1T, Arg2T), Arg1T arg1, Arg2T arg2) {
Job* job = tnew<AsyncJob2<T, Arg1T, Arg2T> >(static_cast<T*>(this), memFunc, arg1, arg2);
return mJobQueue.Do(job);
}
어떻게 처리해야 할까?
33
std::tuple
• 정의
template<typename... Types >
class tuple;
• 예제
tuple<int, bool, string> tpl = make_tuple(3, true, “Hi”);
cout << get<2> tpl << endl;
• 구현
template<typename Head, typename... Tail>
class Tuple_impl {
Head head;
Tuple_impl<Tail...> tail;
…
}
34
Template <class T, class… Args>
Struct AsyncJob : public Job
{
typedef void (T::*MemFunc)(Args…);
AsyncJob(T* obj, MemFunc memFunc, Args… args)
: mObj(obj), mMemFunc(memFunc), mParams(args…)
{
AddRef(mObj, REASON_NDC);
//AddRefForEach???(…)
}
void OnExecute()
{
//(mObj->*mMemFunc)(mParams???);
ReleaseRef(mObj, REASON_NDC);
//ReleaseRefForEach???(…)
}
MemFunc mMemFunc;
T* mObj;
tuple<Args...> mParams;
};
template <class T, class... Args>
bool Async(void (T::*memFunc)(Args...), Args... args)
{
Job* job = tnew<AsyncJob<T, Args...>>(static_cast<T*>(this), memFunc, args...);
return mJobQueue.Do(job);
}
감독님…
“튜플의 각 원소들에 대해서
함수를 수행하고 싶고,
함수의 인자로도 사용”
하고 싶어요….
35
Integer_sequence
• template<int... Remains>
struct seq{};
• template<int N, int... Remains>
struct gen_seq : gen_seq <N - 1, N - 1, Remains...> {};
• template<int... Remains>
struct gen_seq<0, Remains...> : seq <Remains...> {};
• gen_seq<3> → <2, 2> → <1, 1, 2> → <0, 0, 1, 2> → seq<0, 1, 2>
(*) C++14부턴 간단하게 std::make_index_sequence<N>로 대체 가능
36
Tuple을 매개변수로 써보자(Apply)
• template<typename Ret, typename... Args>
Ret apply(Ret (*func)(Args...), tuple<Args...>& tup)
{
return apply_impl(func, gen_seq<sizeof...(Args)>(), tup);
}
• template<typename Ret, typename... Args, int... Is>
Ret apply_impl(Ret(*func)(Args...), seq<Is...>, tuple<Args...>& tup)
{
return func(std::get<Is>(tup)...);
}
37
Sequence 이용해서 Tuple을 순회하자
• template<typename... Ts, typename F>
void for_each(tuple<Ts...>& tup, F func)
{
for_each_impl(tup, func, gen_seq<sizeof...(Ts)>());
}
• template<typename T, typename F, int... Is>
void for_each_impl(T& tup, F func, seq<Is...>)
{
auto l = { ( func(get<Is>(tup)) , 0 )... }; //Using Ellipsis Op
}
• for_each(make_tuple(3, 3.0), functor());
38
(*) Ellipsis Operator의 활용
int k = (0, 3); k = ?
1. Brace-Init-List
int k[] = {1, 2, 3};
auto l = { ( func(get<Is>(tup)) , 0 )... };
2. Function Argument
template<typename... Args>
void MagicFunc(Args&&... args) {}
MagicFunc( ( func(get<Is>(tup)) , 0 )...); → 실행 순서 보장이 안됨!
39
완성?! ?! ?!
template <class T, class… Args>
struct AsyncJob : public Job
{
typedef void (T::*MemFunc)(Args…);
AsyncJob(T* obj, MemFunc memFunc, Args… args)
: mObj(obj), mMemFunc(memFunc), mParams(args…)
{
AddRef(mObj, REASON_NDC);
AddRefForEach(mParams, REASON_NDC);
}
void OnExecute()
{
Apply(mObj, mMemFunc, mParams);
ReleaseRef(mObj, REASON_NDC);
ReleaseRefForEach(mParams, REASON_NDC);
}
MemFunc mMemFunc;
T* mObj;
tuple<Args...> mParams;
};
template <class T, class... Args>
bool Async(void (T::*memFunc)(Args...), Args... args)
{
Job* job = tnew<AsyncJob<T, Args...>>(static_cast<T*>(this), memFunc, args...);
return mJobQueue.Do(job);
}
40
안 돼 안 추론해줘. 해줄 생각 없어. 빨리 돌아가.
session->Async(&NdcSession::End, name, reason);
error: Cannot deduce template argument as function argument is
ambiguous
class NdcSession
{
…
void End(string name, int reason);
void End();
…
}
41
해결법?
1. session->Async(((void (NdcSession::*)(string, int))&NdcSession::End, name, reason);
2. session->Async(((void (NdcSession::*)(decltype(name), decltype(reason)))
&NdcSession::End, name, reason);
3. session->Async<NdcSession, string, int>(&NdcSession::End, name, reason);
4. #define Async(obj, func, ...) _Async((void (Obj::*)(decl_helper(__VA_ARGS__))
&Obj::func, __VA_ARGS__)
5. 오버로드 함수를 다 지운다.
42
잘 모르겠어서 일단 훈련소를 다녀왔습니다
장병들과 함께한 행복한 연말...
43
논산의 지혜: 각!개!전!투!
• 오버로딩이 존재하는 케이스에 대해서만 특수화(Specialization)를 해주면 된다.
• 성공적으로 컴파일이 되었다
• 근데 이러면 ‘먼 과거’와 코드량이 비슷하지 않나요?
• Async 함수 개수: 45개 → 6개 / AsyncJob 클래스 개수: 45개 → 1개
44
template <class T, class... Args>
bool Async(void (T::*memFunc)(Args...), Args... args)
template <class T, class Arg1, class Arg2>
bool Async(void (T::*memFunc)(Args1, Arg2), Arg1 arg1, Arg2 arg2)
(*) 콜스택 줄이기
• 과거엔 this의 타입을 추론하기 위해서 *_impl 함수를 만들었다.
template <class T, class... Args>
bool AsyncOther(T* obj, void (T::*memFunc)(Args...), Args... args)
{
return AsyncOther_Impl(this, obj, memFunc, args...);
}
template <class Owner, class T, class... Args>
bool AsyncOther_Impl(Owner* owner, T* obj, void (T::*memFunc)(Args...), Args... args)
{
Job* job = tnew<AsyncOtherJob<Owner, T, Args...>>(owner, obj, memFunc, args...);
return mJobQueue.Do(job);
}
45
(*) 콜스택 줄이기 with decltype
template <class T, class... Args>
bool AsyncOther(T* obj, void (T::*memFunc)(Args...), Args... args)
{
Job* job = tnew<AsyncOtherJob<remove_pointer<decltype(this)>::type, T, Args...>>
(obj, memFunc, args...);
return mJobQueue.Do(job);
}
template <class _Ty>
struct remove_pointer<_Ty*>
{
Typedef _Ty type;
}
46
1. Introduction
2. 스마트포인터의 오남용과 Move Semantics
3. Variadic Template and some pratical techniques
4. 미래와 약속(Future-Promise)
5. Q&A
47
리빙 포인트: Variadic Template을 적절히 활용하면 가독성을 높힐 수 있다.
C++ 11/14 비동기 키워드
• async, future, promise, packaged_task, thread...
• Cross Platform
• 바퀴를 다시 만들지 않아도 되도록
• STL 내부의 알고리즘을 믿으며 써야한다.
• Future-Promise 패턴에 익숙하다면 어려울게 없다
• 하지만 저도 처음 볼 땐 이해가 잘 안되었습니다.
48
std::thread
void Func(int a) { printf("%d", a); }
main()
{
thread t(&Func, 3);
thread t2([]{ printf(“Do you know Sae-dol Lee~?n");} );
t.join();
t2.join();
}
49
std::promise / std::future
main()
{
promise<int> prom;
future<int> fut = prom.get_future();
auto t = thread([&]{ prom.set_value(1); });
cout << fut.get() << endl;
t.join();
}
50
std::promise / std::future
• 약속을 통해 미래라는 객체를 받을 수 있다.
• future<int> fut = prom.get_future();
• 미래엔 언젠가 지켜질 약속의 결과가 담긴다.
• prom.set_value(1);
• 약속이 언제 행해질진 모르지만 우리는 미래에 결과가 담겼는지 확인 할 수 있다.
• fut.get()
• (미래라는 관념을 실체화 할 수 있다는 것에서 감동)
51
std::async
int foo(double k) { return k*0; }
main()
{
auto myFuture = async(launch::async, &foo, 1.0);
cout << myFuture.get() << endl;
}
• std::launch::async
• 호출 즉시 비동기로 실행
• 다른 쓰레드에서 실행되는 것이 보장
• std::launch::deferred
• 나중에 값을 필요로 할 때 실행
• Lazy Evaluation
52
그래서 future 패턴을 어디에 쓰는게 좋을까?
• 콜백 지옥으로부터 벗어나고 싶을 때
• 순차적인 비동기 처리를 만들면 콜백 지옥이 만들어지곤 한다.
• doIO1([..](Data io1Result){ doIO2([..](Data io2Result) { doIO3([..](Data io3Result){ .... }, io2Result)}, io1Result)}, io1Input)
• 싱글 쓰레드 로직 중간에 분산처리를 넣고 싶을 때
<Reference: Futures for C++11 at Facebook>
53
Reference: “Futures for C++11 at Facebook”
async를 활용한 분산 작업 예제
original
bool result = true;
for(auto& job : jobList) {
if(job.Execute() == false)
result = false;
}
distribution
bool result = true;
vector<future<bool>> futures;
for(auto& job : jobList) {
futures.push_back(
async(launch::async, [&]{ job.Execute();}));
}
for(const auto& fut : futures) {
if(fut.get() == false)
result = false;
}
54
서버 데이터 로드 과정
Group 1
UserData
.
.
UserSkillData
UserAppear
Group 4
NpcData
NpcPartyData
NpcSkillData
(NpcSkill_1.xml
~
NpcSkill_101.xml)
Waiting…
Group 3
GuildData
.
.
Waiting…
Group 2
ItemData
EnchantData
GachaData
..
StoreData
Waiting…
• 각 쓰레드마다 한 그룹씩 배치
• 종속성 때문에 더는 분산 시킬 수 없다.
• 휴리스틱한 방법으로는 확실한 개선이 힘듦.
• NpcSkillData에 Future-Promise 패턴을
적용해보면 어떨까?
55
std::async를 활용해서 프로토 타이핑
• NpcSkill 파일들을 각각 std::async를 통해서 읽도록 수정
• 효과는 굉장했다!
• 하지만…
• 테라서버의 구조에 맞게 적당히 구현해봐야겠다!
56
서버 데이터 로드 과정 (수정)
Group 1
UserData
UserSkillData
UserAppear
Group 4
NpcData
NpcPartyData
Make Promise
NpcSkill_1.xml
NpcSkill_7.xml
…
NpcSkill_101.xml
Group 3
GuildData
NpcSkill_5.xml
NpcSkill_9.xml
…
…
NpcSkill_100.xml
Group 2
ItemData
EnchantData
GachaData
..
StoreData
Wait Future..NpcSkill_2.xml
…
NpcSkill_99.xml
Waiting…
Waiting… Waiting…
• Promise를 만들어 다른 쓰레드로 작업을 분산
• Future에 결과값이 다 모이면 다음 작업을 수행
• 종속성을 해치지 않고 분산작업이 가능!
57
구성원들이 익숙한 형태로 만들기
template<class Ret, class T, class Args...>
future<Ret> DoFutureWork(T* obj, Ret(T::*memFunc)(Args...), Args... args) { … }
template<class Ret, class T, class Args...>
struct FutureJob: Public Job {
FutureJob(T* obj, Ret(T::*memFunc)(Args...), Args... args) {
mFunc = [=]{ return (obj->*memFunc)(args...); };
}
void OnExecute() { mProm.set_value(mFunc()); }
future<Ret> GetFuture() { mProm.get_future(); }
private:
function<Ret(Args…)> mFunc;
promise<Ret> mProm;
}
58
DataManager::LoadGroup6
if(!LoadNpcData())
return -1;
if(LoadNpcSkillData())
return -1;
if(LoadDungeonData())
return -1;
…
DataManager::LoadNpcSkillData
…
bool result = true;
vector<future<bool>> futures;
for(auto& dataFile : fileList) {
futures.push_back(DoFutureWork(GDataManager,&DataMan
ager::LoadSkillData, dataFile);
}
for(const auto& fut : futures) {
if(fut.get() == false)
result = false;
}
…
DoFutureWork를 활용한 분산 작업 예제
59
개선 결과
• Intel i7-4770, Windows Server 2012
• 최 팀장님: 어 나는 Before도 저렇게 빠르지 않았는데?
BUILD DATA Before(sec) After(sec)
DEBUG XML 150 77
DEBUG Binary 70 50
RELEASE XML 90 45
RELEASE Binary 24 17
60
1. Introduction
2. 스마트포인터의 오남용과 Move Semantics
3. Variadic Template and some pratical techniques
4. 미래와 약속(Future-Promise)
5. Q&A
61
리빙 포인트: 윈도우 피시가 느릴 땐 전원 관리 옵션을 살펴보자
Reference
• P8, 20~22: Writing Good C++14 - CPPCON2015, Bjarne Stroustrup
(https://github.com/isocpp/CppCoreGuidelines/blob/master/talks/Stroustrup%20-%20CppCon%202015%20keynote.pdf)
• P13: 잡았다 요놈! - 황준호 작가님, 네이버, 인간의 숲
(http://comic.naver.com/webtoon/detail.nhn?titleId=163295&no=15&weekday=tue)
• P16: 아서스 - Warcraft 3, Blizzard Entertainment
• P24: 더 이상 자세한 설명을 생략한다: 김성모 화백님, 나무위키(http://namu.wiki)
• P27: 착각 했던 것 같다 - 살아남아라 개복치!, SELECT Button inc.
• P35: 감독님 ... - 이노우에 다케히코, 대원씨아이, 나무위키(http://namu.wiki)
• P43: 논산훈련소 정문 - Newsis(http://www.newsis.com/)
• P41: 안돼 안바꿔줘 - SBS, 천종호 판사님, 나무위키(http://namu.wiki)
• P49: Why so asynchronous? - http://minocys.azurewebsites.net/guest-post/
• P54: Futures for C++11 at Facebook - Facebook Code (https://code.facebook.com/posts/1661982097368498/futures-for-
c-11-at-facebook/)
62
Q & A
63
감사합니다.
64

More Related Content

What's hot

나만의 엔진 개발하기
나만의 엔진 개발하기나만의 엔진 개발하기
나만의 엔진 개발하기
YEONG-CHEON YOU
 
실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략
YEONG-CHEON YOU
 
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
Jaeseung Ha
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012devCAT Studio, NEXON
 
NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현
noerror
 
빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)
YEONG-CHEON YOU
 
Ndc14 분산 서버 구축의 ABC
Ndc14 분산 서버 구축의 ABCNdc14 분산 서버 구축의 ABC
Ndc14 분산 서버 구축의 ABC
Ho Gyu Lee
 
중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직
Hoyoung Choi
 
Windows Registered I/O (RIO) vs IOCP
Windows Registered I/O (RIO) vs IOCPWindows Registered I/O (RIO) vs IOCP
Windows Registered I/O (RIO) vs IOCP
Seungmo Koo
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
devCAT Studio, NEXON
 
게임 분산 서버 구조
게임 분산 서버 구조게임 분산 서버 구조
게임 분산 서버 구조
Hyunjik Bae
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
Amazon Web Services Korea
 
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
Lee Dustin
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심흥배 최
 
마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건
강 민우
 
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
devCAT Studio, NEXON
 
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
Heungsub Lee
 
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012Esun Kim
 
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
YEONG-CHEON YOU
 
Multithread & shared_ptr
Multithread & shared_ptrMultithread & shared_ptr
Multithread & shared_ptr
내훈 정
 

What's hot (20)

나만의 엔진 개발하기
나만의 엔진 개발하기나만의 엔진 개발하기
나만의 엔진 개발하기
 
실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략
 
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
 
NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현
 
빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)
 
Ndc14 분산 서버 구축의 ABC
Ndc14 분산 서버 구축의 ABCNdc14 분산 서버 구축의 ABC
Ndc14 분산 서버 구축의 ABC
 
중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직
 
Windows Registered I/O (RIO) vs IOCP
Windows Registered I/O (RIO) vs IOCPWindows Registered I/O (RIO) vs IOCP
Windows Registered I/O (RIO) vs IOCP
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
 
게임 분산 서버 구조
게임 분산 서버 구조게임 분산 서버 구조
게임 분산 서버 구조
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
 
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건마비노기듀얼 이야기-넥슨 김동건
마비노기듀얼 이야기-넥슨 김동건
 
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
 
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
 
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
 
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
GPGPU(CUDA)를 이용한 MMOG 캐릭터 충돌처리
 
Multithread & shared_ptr
Multithread & shared_ptrMultithread & shared_ptr
Multithread & shared_ptr
 

Viewers also liked

8년동안 테라에서 배운 8가지 교훈
8년동안 테라에서 배운 8가지 교훈8년동안 테라에서 배운 8가지 교훈
8년동안 테라에서 배운 8가지 교훈
Harns (Nak-Hyoung) Kim
 
[NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기
[NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기 [NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기
[NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기
Jungsoo Lee
 
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
Daehee Kim
 
NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들
NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들
NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들
Young Keun Choe
 
NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출
NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출 NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출
NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출
정주 김
 
NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술
NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술
NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술
Ki Hyunwoo
 
C++11에서 주의해야할 것들
C++11에서 주의해야할 것들C++11에서 주의해야할 것들
C++11에서 주의해야할 것들
Sangwook Kwon
 
NDC 2016 마이크로토크 - 프로그래머가 투자하는 법
NDC 2016 마이크로토크 - 프로그래머가 투자하는 법NDC 2016 마이크로토크 - 프로그래머가 투자하는 법
NDC 2016 마이크로토크 - 프로그래머가 투자하는 법
Sumin Byeon
 
Tera_skilldatabalanceexport제작기
Tera_skilldatabalanceexport제작기Tera_skilldatabalanceexport제작기
Tera_skilldatabalanceexport제작기
준규 이
 
오토데스트세미나 스케일폼적용사례 김효영
오토데스트세미나 스케일폼적용사례 김효영오토데스트세미나 스케일폼적용사례 김효영
오토데스트세미나 스케일폼적용사례 김효영MinGeun Park
 
[박민근] 3 d렌더링 옵티마이징_3 공간분할
[박민근] 3 d렌더링 옵티마이징_3 공간분할[박민근] 3 d렌더링 옵티마이징_3 공간분할
[박민근] 3 d렌더링 옵티마이징_3 공간분할MinGeun Park
 
Intro to Massively Multiplayer Online Game (MMOG) Design
Intro to Massively Multiplayer Online Game (MMOG) DesignIntro to Massively Multiplayer Online Game (MMOG) Design
Intro to Massively Multiplayer Online Game (MMOG) Design
Christopher Mohritz
 
MMORPG 경제의 공정성
MMORPG 경제의 공정성MMORPG 경제의 공정성
MMORPG 경제의 공정성SeungYeon Jeong
 
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
Hwanhee Kim
 
NDC 2015 마비노기 듀얼 패치 시스템
NDC 2015 마비노기 듀얼 패치 시스템NDC 2015 마비노기 듀얼 패치 시스템
NDC 2015 마비노기 듀얼 패치 시스템tcaesvk
 
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템 메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
ByungTak Kang
 
20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발
20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발
20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발
Heeyoung Lee
 
졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회
졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회
졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회
ChangHyun Won
 
[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들MinGeun Park
 
NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지
NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지
NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지
Kwangyoung Lee
 

Viewers also liked (20)

8년동안 테라에서 배운 8가지 교훈
8년동안 테라에서 배운 8가지 교훈8년동안 테라에서 배운 8가지 교훈
8년동안 테라에서 배운 8가지 교훈
 
[NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기
[NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기 [NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기
[NDC 16] 당신은 사랑 받기 위해 태어난 사람: 3년차 게임 디자이너의 자존감 찾기
 
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
 
NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들
NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들
NDC16 - 화성에서 온 사업팀 금성에서 온 개발팀 : 성공적인 라이브 서비스를 위해 필요한 것들
 
NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출
NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출 NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출
NDC 2016 김정주 - 기계학습을 활용한 게임어뷰징 검출
 
NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술
NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술
NDC2016 프로젝트 A1의 AAA급 캐릭터 렌더링 기술
 
C++11에서 주의해야할 것들
C++11에서 주의해야할 것들C++11에서 주의해야할 것들
C++11에서 주의해야할 것들
 
NDC 2016 마이크로토크 - 프로그래머가 투자하는 법
NDC 2016 마이크로토크 - 프로그래머가 투자하는 법NDC 2016 마이크로토크 - 프로그래머가 투자하는 법
NDC 2016 마이크로토크 - 프로그래머가 투자하는 법
 
Tera_skilldatabalanceexport제작기
Tera_skilldatabalanceexport제작기Tera_skilldatabalanceexport제작기
Tera_skilldatabalanceexport제작기
 
오토데스트세미나 스케일폼적용사례 김효영
오토데스트세미나 스케일폼적용사례 김효영오토데스트세미나 스케일폼적용사례 김효영
오토데스트세미나 스케일폼적용사례 김효영
 
[박민근] 3 d렌더링 옵티마이징_3 공간분할
[박민근] 3 d렌더링 옵티마이징_3 공간분할[박민근] 3 d렌더링 옵티마이징_3 공간분할
[박민근] 3 d렌더링 옵티마이징_3 공간분할
 
Intro to Massively Multiplayer Online Game (MMOG) Design
Intro to Massively Multiplayer Online Game (MMOG) DesignIntro to Massively Multiplayer Online Game (MMOG) Design
Intro to Massively Multiplayer Online Game (MMOG) Design
 
MMORPG 경제의 공정성
MMORPG 경제의 공정성MMORPG 경제의 공정성
MMORPG 경제의 공정성
 
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
 
NDC 2015 마비노기 듀얼 패치 시스템
NDC 2015 마비노기 듀얼 패치 시스템NDC 2015 마비노기 듀얼 패치 시스템
NDC 2015 마비노기 듀얼 패치 시스템
 
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템 메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
 
20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발
20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발
20160427 ndc16 글로벌 원빌드 모바일 게임 런칭과 라이브 개발
 
졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회
졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회
졸업작품을 앞둔 게임 기획/프로그래밍 전공 교류회
 
[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들
 
NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지
NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지
NDC 2015 이광영 [야생의 땅: 듀랑고] 전투 시스템 개발 일지
 

Similar to [NDC2016] TERA 서버의 Modern C++ 활용기

불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
명신 김
 
[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary
Chris Ohk
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택
JinTaek Seo
 
Visual studio 2010
Visual studio 2010Visual studio 2010
Visual studio 2010
MinGeun Park
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
Chris Ohk
 
20150212 c++11 features used in crow
20150212 c++11 features used in crow20150212 c++11 features used in crow
20150212 c++11 features used in crow
Jaeseung Ha
 
C++20 Key Features Summary
C++20 Key Features SummaryC++20 Key Features Summary
C++20 Key Features Summary
Chris Ohk
 
Pyconkr2019 features for using python like matlab
Pyconkr2019 features for using python like matlabPyconkr2019 features for using python like matlab
Pyconkr2019 features for using python like matlab
Intae Cho
 
Python programming for Bioinformatics
Python programming for BioinformaticsPython programming for Bioinformatics
Python programming for Bioinformatics
Hyungyong Kim
 
Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉
HyunJoon Park
 
Let's Go (golang)
Let's Go (golang)Let's Go (golang)
Let's Go (golang)
상욱 송
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11OnGameServer
 
C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부
Gwangwhi Mah
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013Esun Kim
 
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
Sang Don Kim
 
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
Chris Ohk
 
C언어 세미나 - 함수
C언어 세미나 - 함수C언어 세미나 - 함수
C언어 세미나 - 함수
SeungHyun Lee
 
HI-ARC PS 101
HI-ARC PS 101HI-ARC PS 101
HI-ARC PS 101
Jae-yeol Lee
 

Similar to [NDC2016] TERA 서버의 Modern C++ 활용기 (20)

불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
 
[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택
 
Visual studio 2010
Visual studio 2010Visual studio 2010
Visual studio 2010
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
 
20150212 c++11 features used in crow
20150212 c++11 features used in crow20150212 c++11 features used in crow
20150212 c++11 features used in crow
 
C++20 Key Features Summary
C++20 Key Features SummaryC++20 Key Features Summary
C++20 Key Features Summary
 
강의자료 2
강의자료 2강의자료 2
강의자료 2
 
Pyconkr2019 features for using python like matlab
Pyconkr2019 features for using python like matlabPyconkr2019 features for using python like matlab
Pyconkr2019 features for using python like matlab
 
Python programming for Bioinformatics
Python programming for BioinformaticsPython programming for Bioinformatics
Python programming for Bioinformatics
 
Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉
 
Let's Go (golang)
Let's Go (golang)Let's Go (golang)
Let's Go (golang)
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
 
C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
 
6 function
6 function6 function
6 function
 
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
 
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
 
C언어 세미나 - 함수
C언어 세미나 - 함수C언어 세미나 - 함수
C언어 세미나 - 함수
 
HI-ARC PS 101
HI-ARC PS 101HI-ARC PS 101
HI-ARC PS 101
 

[NDC2016] TERA 서버의 Modern C++ 활용기

  • 1. TERA 서버의 Modern C++ 활용기 Bluehole 이상헌 1
  • 2. 목차 1. Introduction 2. 스마트포인터의 오남용과 Move Semantics 3. Variadic Template and some pratical techniques 4. 미래와 약속 (Future-Promise) 5. Q&A 2
  • 3. 발표자 소개 • Software Engineer • Interest: C++, Concurrency, Server-Side Techs... • 경력 • 2014 – 현재 : Bluehole 테라본부 서버팀 • 2012 – 2014 : M.S. Programming Languages Lab, KAIST • 2004 : 전국 이야기 말하기 대회 대구지역 2등, 지역대표 • Contact • developistbv@gmail.com 3
  • 4. The Exiled Realm of Arborea Free-Targeting Action MMORPG 2007-2011: 개발 2011.01 : 한게임 서비스 2016.01 : NEXON 서비스 NA, EU 포함 7개 지역에서 서비스 중 4
  • 5. 발표하기 앞서 • C++03 기준으로 작성된 테라서버 코드에서 Modern C++를 활용한 사례를 소개합니다 • 더 많은 활용 예가 공유되었으면 하는 바램에 발표하게 되었습니다. • 과거가 ‘잘못 되었다’가 아니라, 이제 ‘더 나은 길’이 열렸다는 것을 소개 하는게 목적입니다. • 보안 문제로 실제 테라 코드를 담진 않았습니다. • (*)가 표시된 슬라이드는 부연 설명을 위한 슬라이드입니다. • 발표 땐 시간 관계상 다루지 않을 수 있습니다. • 저작권법 제 35조의 제 3 ‘공정이용’ 조항에 따라 교육과 연구 목적으로 일부 저작물을 인용 하였습니다. 혹시 문제가 있을 경우 메일로 연락주시면 적절한 조치를 취하겠습니다. 5
  • 6. 1. Introduction 2. 스마트포인터의 오남용과 Move Semantics 3. Variadic Template and some pratical techniques 4. 미래와 약속(Future-Promise) 5. Q&A 6 리빙 포인트: 발표 중간중간에 있는 이런 슬라이드는 진행 상황을 알려준다
  • 7. 필요한 사전 지식 • smart pointer (RAII) • 객체의 생성 소멸 관리를 위해서 사용하는 객체 • shared_ptr<T>, unique_ptr<T> • t_ptr<T> • shared_ptr<T>처럼 Reference Count를 공유 • 침습형(invasive) 카운터를 사용 • Move Semantics, Rvalue Reference, Perforect Forwarding... 7
  • 8. Cppcon2015 – Writing Good C++14 中 • “Smart pointers” are popular • To represent ownership • To avoid dangling pointers • “Smart pointers” are overused • Can be expensive • E,g., shared_ptr • Can mess up interfaces fore otherwise simple function • E.g. unique_ptr and shared_ptr • Often, we don’t need a pointer • Scoped objects • We need pointers 8 Reference Count의 증가 연산이 많이 일어나기 때문
  • 9. shared_ptr의 문제는 t_ptr의 문제와 같다 void BattleField::OnAttack(int gameId) { t_ptr<User> attacker = GUserManager->GetUser(gameId); for(auto it = mUserList.begin(); it != mUserList.end(); ++it) { t_ptr<User> target = *it; IsSameBattleFieldTeam (attacker, target); } } bool BattleField::IsSameBattleFieldTeam(t_ptr<User> user1, t_ptr<User> user2) { t_ptr<BFChannel> channel = type_cast<BFChannel*>(user1->GetChannel()); if(channel) { … } } 9 Bad Code!
  • 10. 사례1) 빠른줄만 알았던 type_cast template<typename T, typename S> t_ptr<T> type_cast<T>(t_ptr<S> s) {<29.9% if(s && TypeTable[Idx<S>][Idx<T>])<0.1% { return t_ptr<T>(*s); }<70.0% return nullptr; } template<typename T, typename S> T* type_cast<T>(S* s) { if(s && TypeTable[Idx<S>][Idx<T>]) { return static_cast<T>(s); } return nullptr; } 뜻밖의 복사생성자와 소멸자가 호출! 10
  • 11. 사례1) 빠른줄만 알았던 type_cast (수정) template<typename T, typename S> t_ptr<T> type_cast<T>(t_ptr<S>& s) { if(s && TypeTable[Idx<S>][Idx<T>]) { return t_ptr<T>(*s); } return nullptr; } template<typename T, typename S> t_ptr<T> type_cast<T>(t_ptr<S>&& s) { if(s && TypeTable[Idx<S>][Idx<T>]) { return t_ptr<T>(*s); }<29.9% return nullptr; } 뜻 밖의 호출들이 사라졌다! 11
  • 12. 다시는 atomic 연산을 무시하지마라 • Atomic Increment/Decrement 연산은 (생각보다) 비싸다. • ++ 처럼 가볍게 여기지 말자 • 본의아니게 dynamic_cast만큼 느린 type_cast를 쓰고 있었다. • 서비스엔 아무런 지장이 없었음 (0.1ms vs 0.09ms, 호출양도 적음) • 이처럼 스마트포인터의 복사생성이 의미 없이 발생할 때가 많다. • type_cast는 그저 하나의 예제일 뿐 12
  • 13. 사례 2) 빠른줄만 알았던 Vector • GatherGameObjectList 라는 함수가 오래 걸린다. • 주위에 있는 GameObject의 t_ptr를 Vector에 저장 하는데 왜 느리지? • Vector 예찬론자로서 인정할 수 없는 일 • Reallocation에서 RefCount++ / RefCount-- 가 일어나는걸 확인 13
  • 14. Vector Reallocation (push_back) A B C D A B C D E Step 1. 재할당 및 복사 A B C D Step 2. 원본 객체 소멸 Step 3. 새 객체 삽입 RefCount ++ *4 RefCount -- *4 RefCount ++ E객체 E를 추가하려 할 때 R1 R2 R1 R2 불필요한 연산! 14
  • 15. Move Semantics • Rvalue Reference • Object&& object = GetObject(); (cf. Object& object = otherObject;) • 생성자나 대입연산자가 Rvalue를 사용할 땐 ‘소유권’ 을 넘겨주는 것만으로 충분 하다 Object(Object&& other){ mChild = other.mChild; other.mChild = nullptr; } Object(Object& other){ DeepCopy(mChild, other.mChild); } 15
  • 16. 소유권을 계승하는 중입니다 • 사라질 객체는 소유권을 위임하는 것으로 충분하다. t_ptr<T>(t_ptr<T>&& t) { mT = t.mT t.SetNullWithoutRelease(); } t_ptr<T>& operator=(t_ptr<T>&& t) { release(); mT = t.mT t.SetNullWithoutRelease(); } • 와! 이제 Vector Reallocation 때 Move Constructor가 호출 되겠죠? • 아니요. 16 Succeeding you, rvalue(?)
  • 17. Perfect Forwarding • Custom allocator에서 쓰는 C++03 버전 construct 함수가 문제 void construct(pointer _ptr, const _Ty& _Val) → Lvalue Reference가 되어버렸다! { ::new ((void*)_Ptr) value_type(_Val)); } • Perfect Forwarding을 하도록 수정하여 해결 void construct(pointer _Ptr, _Ty&& _Val) → Universal Reference! { ::new ((void*)_Ptr) value_type(forward<_Ty>(_Val)); // R->R, L->L로 처리 } 17
  • 18. 개선 후 CPU 점유율 변화 • 시나리오: 다수의 채널에서 다수의 PC가 뭉쳐서 NPC 무리와 전투 주위의 NPC 수 Before Avg After Avg N 12.6% 7.8% 2N 23.6% 18.4% 3N 40.0% 30.0% 4N 57.5% 37.8% 18 (*) Live 서버에서 평균 CPU 사용률은 1번 케이스보다 낮음
  • 19. 개선 후 Sample Rate 변화 (3N) • RefCount 연산의 Sample Rate 변화: 47.5% → 26.5% • Reallocation의 Sample Rate 변화 Before(14.8%) After(2.8%) 19
  • 20. (*) Cppcon2015 – Writing Good C++14 中 • 스트롭스트룹 할아버지께서 말씀하시길 • Shared_Ptr이 지나치게 남용(Overuse)되고 있다는 것을 느꼈다. • 많은 사람들이 shared_ptr를 파라미터(by value)로 쓰고, 또 리턴하고 있다. • 이러면 RefCount가 의미에 맞지않게 올라가고 내려가는 것이 반복된다. • Dangling Pointer는 룰을 잘 따르고(툴을 이용하면) 다 잡을 수 있다. • Rules for Eliminating Dangling Pointers • Owner와 Non-Owner를 구분한다. • Raw-Pointer는 모두 Non-Owner이다. • Pointer가 Owner의 Scope를 벗어나려하는 시도를 모두 잡는다. • Parameter로 받아서 Return으로 돌려주는건 괜찮음 • Owner를 가지고 있는 객체는 Owner다. • vector<T*>, owner<T*> 20
  • 21. (*) Cppcon2015 – Writing Good C++14 中 int* f(int* p) { return p; //괜찮음 return new int{7} //Owner로 받으면 O.K. int x = 4; return &x; //극혐 } owner<int*> k = f(p); // O.K. 21
  • 22. (*) GSL: Owner<T> • GSL = Guideline Support Library (Global Starcraft II League) • https://github.com/Microsoft/GSL • STL = The ISO C++ standard library • C++ Core Guideline의 규칙들을 지원하는 라이브러리 • https://github.com/isocpp/CppCoreGuidelines • template <class T> using owner = T; • 정적분석을 위해 • 문서화를 위해 22
  • 23. 1. Introduction 2. 스마트포인터의 오남용과 Move Semantics 3. Variadic Template and some pratical techniques 4. 미래와 약속(Future-Promise) 5. Q&A 23 리빙 포인트: 과거에 작성된 스마트포인터엔 Move Semantics를 구현해주자
  • 24. 사전 지식: Template in C++ • template <typename T> class Stack; { … bool push(T elem); T pop(); } • Stack<int> mStack; 24
  • 25. ‘tnew’ in TERA Server (과거) #define MEMORY_ALLOC_ARG_NUM 40 #define MEMORY_tnew_print(z, n, _) template<class Type BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> Type* tnew(BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) { Type* memory = (Type*)AllocMemory(sizeof(Type)); new(memory) Type(BOOST_PP_ENUM_PARAMS(n, arg)); return memory; } BOOST_PP_REPEAT(BOOST_PP_INC(MEMORY_ALLOC_ARG_NUM), MEMORY_tnew_print, _) 25
  • 26. ‘tnew’ in TERA Server (먼 과거) template<class Type, class Arg1, class Arg2, class Arg3> Type* tnew(Arg1 arg1, Arg2 arg2, Arg3 arg3) { Type* memory = (Type*)AllocMemory(sizeof(Type)); new(memory) Type(arg1, arg2, arg3); return (Type *)memory; } • 과거의 나: 이게 더 깔끔한거 같은데요? -_- 26 • 사수님: 헷갈린다면 이 리비전의 코드를 보고 공부 하거라.
  • 27. ‘tnew’ in TERA Server(먼 과거) 27
  • 28. Variadic Template • 임의의 인자 개수를 지원하는 템플릿 형식(클래스, 함수 모두 지원) • Ellipsis operator(= …)와 함께 사용 - arguments, parameters, sizeof op, initializer list of array, member initializer list, lambda capture, exception specification list • template<typename... Args > void DoFunc(Args... args) { Func(args…); } DoFunc(1, “hi”) = void DoFunc<int, string>(1, “hi”); DoFunc() = void DoFunc<>() 28
  • 29. 당연히 될 것 같은 것들 • template<typename T, typename... Args> void DoFunc(T obj, Args&... args) { Func(obj, args...); } • template<typename... Args> void DoFunc(Args... args) { const unsigned argSize = sizeof...(Args); } • template<typename... Base> class DoClass : public Base... { DoClass(Base&&... base): Base(base)... {} } 29
  • 30. #define MEMORY_ALLOC_ARG_NUM 40 #define MEMORY_tnew_print(z, n, _) template<class Type BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> Type* tnew(BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) { Type* memory = (Type*)AllocMemory(sizeof(Type)); new(memory) Type(BOOST_PP_ENUM_PARAMS(n, arg)); return memory; } BOOST_PP_REPEAT(BOOST_PP_INC(MEMORY_ALLOC_ARG_NUM), MEMORY_tnew_print, _) ‘tnew’ in TERA Server template<class Type, class... Args> Type* tnew(Args&& ... args) { Type* memory = (Type*)AllocMemory(sizeof(Type)); new(memory)Type(forward<Args>(args)...); return memory; } 30
  • 31. 성공에 힘 입어 여기저기 수정 Typelist<typename Head, typename Tail> #define TYPELIST_1(T1) Typelist<T1, NullType> #define TYPELIST_2(T1, T2) Typelist<T1, TYPELIST_2(T2)> #define TYPELIST_3(T1, T2,T3) Typelist<T1, TYPELIST_3(T2, T3)> … TYPELIST_5(GameObject, Creature, User, Npc, Item); Typelist<typename... T> Typelist<GameObject, Creature, User, Npc, Item>; • TypeList, PacketWriter, LogWriter 등등… Before After 31
  • 32. 끝판왕에 도전 #define ASYNC_JOB_print(z, n, _) template <class T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> struct BOOST_PP_CAT(AsyncJob, n) : public Job { typedef void (T::*MemFunc)(BOOST_PP_ENUM_PARAMS(n, Arg)); BOOST_PP_CAT(AsyncJob, n)(T* obj, MemFunc memFunc BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) : mObj(obj), mMemFunc(memFunc) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, ASYNC_PARAM_ASSIGN_print, ~) { AddRef(mObj, REUSE_NDC); BOOST_PP_REPEAT(n, ASYNC_ARG_ADDREF_print, REUSE_NDC) } void OnExecute() { (mObj->*mMemFunc)(BOOST_PP_ENUM_PARAMS(n, mArg)); ReleaseRef(mObj, REUSE_LFECALL); BOOST_PP_REPEAT(n, ASYNC_ARG_RELREF_print, REUSE_NDC) } MemFunc mMemFunc; T* mObj; BOOST_PP_REPEAT(n, ASYNC_ARG_DECL_print, _) }; #define ASYNC_print(z, n, _) template <class T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, class Arg)> bool Async(void (T::*memFunc)(BOOST_PP_ENUM_PARAMS(n, Arg)) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, arg)) { Job* job = tnew<BOOST_PP_CAT(AsyncJob, n)<T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, Arg)> >(static_cast<T*>(this), memFunc BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, arg)); return mJobQueue.Do(job); } 32
  • 33. template <typename T, typename Arg1T, typename Arg2T> struct AsyncJob2 : public Job { typedef void (T::*MemFunc)(Arg1T, Arg2T); AsyncJob2(T* obj, MemFunc memFunc, Arg1T arg1, Arg2T arg2): mObj(obj), mMemFunc(memFunc), mArg1(arg1), mArg2(arg2) { AddRef(mObj, REASON_NDC); AddRef(mArg1, REASON_NDC); AddRef(mArg2, REASON_NDC); } void OnExecute() { (mObj->*mMemFunc)(mArg1, mArg2); ReleaseRef(mObj, REASON_NDC); ReleaseRef(mArg1, REASON_NDC); ReleaseRef(mArg2, REASON_NDC); } MemFunc mMemFunc; T* mObj; Arg1T mArg1; Arg2T mArg2; }; template <typename T, typename Arg1T, typename Arg2T> bool Async(void (T::*memFunc)(Arg1T, Arg2T), Arg1T arg1, Arg2T arg2) { Job* job = tnew<AsyncJob2<T, Arg1T, Arg2T> >(static_cast<T*>(this), memFunc, arg1, arg2); return mJobQueue.Do(job); } 어떻게 처리해야 할까? 33
  • 34. std::tuple • 정의 template<typename... Types > class tuple; • 예제 tuple<int, bool, string> tpl = make_tuple(3, true, “Hi”); cout << get<2> tpl << endl; • 구현 template<typename Head, typename... Tail> class Tuple_impl { Head head; Tuple_impl<Tail...> tail; … } 34
  • 35. Template <class T, class… Args> Struct AsyncJob : public Job { typedef void (T::*MemFunc)(Args…); AsyncJob(T* obj, MemFunc memFunc, Args… args) : mObj(obj), mMemFunc(memFunc), mParams(args…) { AddRef(mObj, REASON_NDC); //AddRefForEach???(…) } void OnExecute() { //(mObj->*mMemFunc)(mParams???); ReleaseRef(mObj, REASON_NDC); //ReleaseRefForEach???(…) } MemFunc mMemFunc; T* mObj; tuple<Args...> mParams; }; template <class T, class... Args> bool Async(void (T::*memFunc)(Args...), Args... args) { Job* job = tnew<AsyncJob<T, Args...>>(static_cast<T*>(this), memFunc, args...); return mJobQueue.Do(job); } 감독님… “튜플의 각 원소들에 대해서 함수를 수행하고 싶고, 함수의 인자로도 사용” 하고 싶어요…. 35
  • 36. Integer_sequence • template<int... Remains> struct seq{}; • template<int N, int... Remains> struct gen_seq : gen_seq <N - 1, N - 1, Remains...> {}; • template<int... Remains> struct gen_seq<0, Remains...> : seq <Remains...> {}; • gen_seq<3> → <2, 2> → <1, 1, 2> → <0, 0, 1, 2> → seq<0, 1, 2> (*) C++14부턴 간단하게 std::make_index_sequence<N>로 대체 가능 36
  • 37. Tuple을 매개변수로 써보자(Apply) • template<typename Ret, typename... Args> Ret apply(Ret (*func)(Args...), tuple<Args...>& tup) { return apply_impl(func, gen_seq<sizeof...(Args)>(), tup); } • template<typename Ret, typename... Args, int... Is> Ret apply_impl(Ret(*func)(Args...), seq<Is...>, tuple<Args...>& tup) { return func(std::get<Is>(tup)...); } 37
  • 38. Sequence 이용해서 Tuple을 순회하자 • template<typename... Ts, typename F> void for_each(tuple<Ts...>& tup, F func) { for_each_impl(tup, func, gen_seq<sizeof...(Ts)>()); } • template<typename T, typename F, int... Is> void for_each_impl(T& tup, F func, seq<Is...>) { auto l = { ( func(get<Is>(tup)) , 0 )... }; //Using Ellipsis Op } • for_each(make_tuple(3, 3.0), functor()); 38
  • 39. (*) Ellipsis Operator의 활용 int k = (0, 3); k = ? 1. Brace-Init-List int k[] = {1, 2, 3}; auto l = { ( func(get<Is>(tup)) , 0 )... }; 2. Function Argument template<typename... Args> void MagicFunc(Args&&... args) {} MagicFunc( ( func(get<Is>(tup)) , 0 )...); → 실행 순서 보장이 안됨! 39
  • 40. 완성?! ?! ?! template <class T, class… Args> struct AsyncJob : public Job { typedef void (T::*MemFunc)(Args…); AsyncJob(T* obj, MemFunc memFunc, Args… args) : mObj(obj), mMemFunc(memFunc), mParams(args…) { AddRef(mObj, REASON_NDC); AddRefForEach(mParams, REASON_NDC); } void OnExecute() { Apply(mObj, mMemFunc, mParams); ReleaseRef(mObj, REASON_NDC); ReleaseRefForEach(mParams, REASON_NDC); } MemFunc mMemFunc; T* mObj; tuple<Args...> mParams; }; template <class T, class... Args> bool Async(void (T::*memFunc)(Args...), Args... args) { Job* job = tnew<AsyncJob<T, Args...>>(static_cast<T*>(this), memFunc, args...); return mJobQueue.Do(job); } 40
  • 41. 안 돼 안 추론해줘. 해줄 생각 없어. 빨리 돌아가. session->Async(&NdcSession::End, name, reason); error: Cannot deduce template argument as function argument is ambiguous class NdcSession { … void End(string name, int reason); void End(); … } 41
  • 42. 해결법? 1. session->Async(((void (NdcSession::*)(string, int))&NdcSession::End, name, reason); 2. session->Async(((void (NdcSession::*)(decltype(name), decltype(reason))) &NdcSession::End, name, reason); 3. session->Async<NdcSession, string, int>(&NdcSession::End, name, reason); 4. #define Async(obj, func, ...) _Async((void (Obj::*)(decl_helper(__VA_ARGS__)) &Obj::func, __VA_ARGS__) 5. 오버로드 함수를 다 지운다. 42
  • 43. 잘 모르겠어서 일단 훈련소를 다녀왔습니다 장병들과 함께한 행복한 연말... 43
  • 44. 논산의 지혜: 각!개!전!투! • 오버로딩이 존재하는 케이스에 대해서만 특수화(Specialization)를 해주면 된다. • 성공적으로 컴파일이 되었다 • 근데 이러면 ‘먼 과거’와 코드량이 비슷하지 않나요? • Async 함수 개수: 45개 → 6개 / AsyncJob 클래스 개수: 45개 → 1개 44 template <class T, class... Args> bool Async(void (T::*memFunc)(Args...), Args... args) template <class T, class Arg1, class Arg2> bool Async(void (T::*memFunc)(Args1, Arg2), Arg1 arg1, Arg2 arg2)
  • 45. (*) 콜스택 줄이기 • 과거엔 this의 타입을 추론하기 위해서 *_impl 함수를 만들었다. template <class T, class... Args> bool AsyncOther(T* obj, void (T::*memFunc)(Args...), Args... args) { return AsyncOther_Impl(this, obj, memFunc, args...); } template <class Owner, class T, class... Args> bool AsyncOther_Impl(Owner* owner, T* obj, void (T::*memFunc)(Args...), Args... args) { Job* job = tnew<AsyncOtherJob<Owner, T, Args...>>(owner, obj, memFunc, args...); return mJobQueue.Do(job); } 45
  • 46. (*) 콜스택 줄이기 with decltype template <class T, class... Args> bool AsyncOther(T* obj, void (T::*memFunc)(Args...), Args... args) { Job* job = tnew<AsyncOtherJob<remove_pointer<decltype(this)>::type, T, Args...>> (obj, memFunc, args...); return mJobQueue.Do(job); } template <class _Ty> struct remove_pointer<_Ty*> { Typedef _Ty type; } 46
  • 47. 1. Introduction 2. 스마트포인터의 오남용과 Move Semantics 3. Variadic Template and some pratical techniques 4. 미래와 약속(Future-Promise) 5. Q&A 47 리빙 포인트: Variadic Template을 적절히 활용하면 가독성을 높힐 수 있다.
  • 48. C++ 11/14 비동기 키워드 • async, future, promise, packaged_task, thread... • Cross Platform • 바퀴를 다시 만들지 않아도 되도록 • STL 내부의 알고리즘을 믿으며 써야한다. • Future-Promise 패턴에 익숙하다면 어려울게 없다 • 하지만 저도 처음 볼 땐 이해가 잘 안되었습니다. 48
  • 49. std::thread void Func(int a) { printf("%d", a); } main() { thread t(&Func, 3); thread t2([]{ printf(“Do you know Sae-dol Lee~?n");} ); t.join(); t2.join(); } 49
  • 50. std::promise / std::future main() { promise<int> prom; future<int> fut = prom.get_future(); auto t = thread([&]{ prom.set_value(1); }); cout << fut.get() << endl; t.join(); } 50
  • 51. std::promise / std::future • 약속을 통해 미래라는 객체를 받을 수 있다. • future<int> fut = prom.get_future(); • 미래엔 언젠가 지켜질 약속의 결과가 담긴다. • prom.set_value(1); • 약속이 언제 행해질진 모르지만 우리는 미래에 결과가 담겼는지 확인 할 수 있다. • fut.get() • (미래라는 관념을 실체화 할 수 있다는 것에서 감동) 51
  • 52. std::async int foo(double k) { return k*0; } main() { auto myFuture = async(launch::async, &foo, 1.0); cout << myFuture.get() << endl; } • std::launch::async • 호출 즉시 비동기로 실행 • 다른 쓰레드에서 실행되는 것이 보장 • std::launch::deferred • 나중에 값을 필요로 할 때 실행 • Lazy Evaluation 52
  • 53. 그래서 future 패턴을 어디에 쓰는게 좋을까? • 콜백 지옥으로부터 벗어나고 싶을 때 • 순차적인 비동기 처리를 만들면 콜백 지옥이 만들어지곤 한다. • doIO1([..](Data io1Result){ doIO2([..](Data io2Result) { doIO3([..](Data io3Result){ .... }, io2Result)}, io1Result)}, io1Input) • 싱글 쓰레드 로직 중간에 분산처리를 넣고 싶을 때 <Reference: Futures for C++11 at Facebook> 53 Reference: “Futures for C++11 at Facebook”
  • 54. async를 활용한 분산 작업 예제 original bool result = true; for(auto& job : jobList) { if(job.Execute() == false) result = false; } distribution bool result = true; vector<future<bool>> futures; for(auto& job : jobList) { futures.push_back( async(launch::async, [&]{ job.Execute();})); } for(const auto& fut : futures) { if(fut.get() == false) result = false; } 54
  • 55. 서버 데이터 로드 과정 Group 1 UserData . . UserSkillData UserAppear Group 4 NpcData NpcPartyData NpcSkillData (NpcSkill_1.xml ~ NpcSkill_101.xml) Waiting… Group 3 GuildData . . Waiting… Group 2 ItemData EnchantData GachaData .. StoreData Waiting… • 각 쓰레드마다 한 그룹씩 배치 • 종속성 때문에 더는 분산 시킬 수 없다. • 휴리스틱한 방법으로는 확실한 개선이 힘듦. • NpcSkillData에 Future-Promise 패턴을 적용해보면 어떨까? 55
  • 56. std::async를 활용해서 프로토 타이핑 • NpcSkill 파일들을 각각 std::async를 통해서 읽도록 수정 • 효과는 굉장했다! • 하지만… • 테라서버의 구조에 맞게 적당히 구현해봐야겠다! 56
  • 57. 서버 데이터 로드 과정 (수정) Group 1 UserData UserSkillData UserAppear Group 4 NpcData NpcPartyData Make Promise NpcSkill_1.xml NpcSkill_7.xml … NpcSkill_101.xml Group 3 GuildData NpcSkill_5.xml NpcSkill_9.xml … … NpcSkill_100.xml Group 2 ItemData EnchantData GachaData .. StoreData Wait Future..NpcSkill_2.xml … NpcSkill_99.xml Waiting… Waiting… Waiting… • Promise를 만들어 다른 쓰레드로 작업을 분산 • Future에 결과값이 다 모이면 다음 작업을 수행 • 종속성을 해치지 않고 분산작업이 가능! 57
  • 58. 구성원들이 익숙한 형태로 만들기 template<class Ret, class T, class Args...> future<Ret> DoFutureWork(T* obj, Ret(T::*memFunc)(Args...), Args... args) { … } template<class Ret, class T, class Args...> struct FutureJob: Public Job { FutureJob(T* obj, Ret(T::*memFunc)(Args...), Args... args) { mFunc = [=]{ return (obj->*memFunc)(args...); }; } void OnExecute() { mProm.set_value(mFunc()); } future<Ret> GetFuture() { mProm.get_future(); } private: function<Ret(Args…)> mFunc; promise<Ret> mProm; } 58
  • 59. DataManager::LoadGroup6 if(!LoadNpcData()) return -1; if(LoadNpcSkillData()) return -1; if(LoadDungeonData()) return -1; … DataManager::LoadNpcSkillData … bool result = true; vector<future<bool>> futures; for(auto& dataFile : fileList) { futures.push_back(DoFutureWork(GDataManager,&DataMan ager::LoadSkillData, dataFile); } for(const auto& fut : futures) { if(fut.get() == false) result = false; } … DoFutureWork를 활용한 분산 작업 예제 59
  • 60. 개선 결과 • Intel i7-4770, Windows Server 2012 • 최 팀장님: 어 나는 Before도 저렇게 빠르지 않았는데? BUILD DATA Before(sec) After(sec) DEBUG XML 150 77 DEBUG Binary 70 50 RELEASE XML 90 45 RELEASE Binary 24 17 60
  • 61. 1. Introduction 2. 스마트포인터의 오남용과 Move Semantics 3. Variadic Template and some pratical techniques 4. 미래와 약속(Future-Promise) 5. Q&A 61 리빙 포인트: 윈도우 피시가 느릴 땐 전원 관리 옵션을 살펴보자
  • 62. Reference • P8, 20~22: Writing Good C++14 - CPPCON2015, Bjarne Stroustrup (https://github.com/isocpp/CppCoreGuidelines/blob/master/talks/Stroustrup%20-%20CppCon%202015%20keynote.pdf) • P13: 잡았다 요놈! - 황준호 작가님, 네이버, 인간의 숲 (http://comic.naver.com/webtoon/detail.nhn?titleId=163295&no=15&weekday=tue) • P16: 아서스 - Warcraft 3, Blizzard Entertainment • P24: 더 이상 자세한 설명을 생략한다: 김성모 화백님, 나무위키(http://namu.wiki) • P27: 착각 했던 것 같다 - 살아남아라 개복치!, SELECT Button inc. • P35: 감독님 ... - 이노우에 다케히코, 대원씨아이, 나무위키(http://namu.wiki) • P43: 논산훈련소 정문 - Newsis(http://www.newsis.com/) • P41: 안돼 안바꿔줘 - SBS, 천종호 판사님, 나무위키(http://namu.wiki) • P49: Why so asynchronous? - http://minocys.azurewebsites.net/guest-post/ • P54: Futures for C++11 at Facebook - Facebook Code (https://code.facebook.com/posts/1661982097368498/futures-for- c-11-at-facebook/) 62