SlideShare une entreprise Scribd logo
1  sur  52
Télécharger pour lire hors ligne
사례로 살펴보는
MSVC 빌드 최적화 팁
넥슨지티 서든어택2실 프로그램팀
황의권
발표에 앞서
발표자는 누구고 무슨 이야기를 하려 하는가
황의권
서든어택2 프로그램팀 팀장
2007 - Tibero(RDBMS)
2009.봄 - 가을 카바티나스토리
- 2011.여름 메이플스토리
- 2012.봄 아이러브커피 외
- 현재 서든어택2
발표자 소개
취미 : 코스츔 플레이어
빌드 시간은 개발 생산성에 밀접한 문제
- 특히 게임에선 개발 iteration 비용이 중요
누구나 얘기하지만 잘 챙기긴 어렵다
왜 빌드 발표요?
출처 : compiling (https://xkcd.com/303/)
간단히 검색해 본 NDC 관련 세션
• Unity Build로 빌드타임 반토막내기 – 송창규 [NDC10]
• 레가시 프로젝트의 빌드 자동화 – 최재훈 [NDC11]
• 효율적인 모바일 게임 개발을 위한 모바일 빌드 시스템과 모바일 배포 시스템 구축 노하우 –
윤보선/김태효 [NDC15]
• 하루에 3번! 삼시세끼 빌드 만들기! – 안현석 [NDC15]
빌드 엔지니어링은 단골 주제
다양한 개발 환경은 말할 것도 없고,
하나의 개발 환경에서도 수많은 상황을 고려해야 함
빌드 엔지니어링은 넓고 깊은 주제
사례로 살펴보는
MSVC
빌드 최적화
팁
제목 부연 설명
서든어택2 서버 개발 중 있었던 일
Microsoft Visual C++
빌드 생성 과정에 대한 엔지니어링
Rule of Thumb 중심 + 가벼운 연관지식
사건의 개요
무슨 일이 있었나
게임 서버 주요 환경
• 언어 환경 : VS2013, C++11(의 일부)
• 주요 라이브러리 : boost 1.5x, Intel TBB 4.x
• 저장소 : 아직은 Perforce
그 외 개발 스택
• C#, Python/flask
• MSSQL, Redis, MongoDB
• Linux, Git, AWS, …
개발환경
Incredibuild – 상업 분산 빌드 솔루션 사용 중
모든 빌드 관여 머신에 SSD 장착
-> 적절한 돈으로 해결할 수 있는 건 이미 해둔 상태
-> 대신 빌드 머신은 평범
개발 환경 - 빌드 관련 외부 환경
“서버 프로젝트 CI빌드가 느려요. 언제부터인진 몰라도..”
“원인은 파악 됐나요?”
“실마리는 아직, 해결하려면 좀 봐야 할 것 같아요”
“그럼 이번 릴리즈 끝난 후 봅시다”
이 대화가 세 번 쯤 반복된 후, 드디어 뚜껑을 열어보기로 결정
회의에서 나온 이야기
서버프로젝트 CI 빌드 시간이 15분+
서버 통합바이너리 용량이 109MB
상황 확인
전체 개발 사이클의 병목은 아니지만 commit이 망설여짐
상식 밖의 빌드 시간
• 당시 소스파일 크기(LOC) : 약 175K
• 데디케이티드 서버는 별도
심각성 분석 – 시간 문제
175K
도표 : 개발일정에 따른 LOC 변화
코드 정리
큰 마일스톤
큰 용량이 기능과 성능에 영향을 주지는 않았음
하지만 바이너리가 109메가면 이거 너무 심한 거 아니오
심각성 분석 – 용량 문제
가설 1 : 이런 이상 상황이 갑자기 발생한 특정한 시점이 있지 않을까?
진행 1 : 로그! 로그를 보자!
전략 선택 – 1단계
이분검색을 해서 문제가 되는 커밋을 찾자
- 총 서버 변경 리비전 수 : 약 3000
-> 이분검색 12(2^12=4096)번, 간단하네?
로그를 분석한다
빌드 시간 검색
• CC.NET CI 빌드 로그
• 검색하기 너무 불편
• 검색툴을 만들까? -> 배보다 배꼽이…
• 직접 빌드해보면 되지 않을까?
• 12번 리비전이동/빌드
• 하지만 문제가 생긴 시점이 특정된다는 보장이 없다!
로그분석 – 시간 추적
CI 빌드 바이너리는 저장소에 커밋되지 않고 있었고orz
대신 서비스 서버 관리 툴이 쓰는 중간 저장소에 드문드문 로그가 있었다
- 최초 서비스 배포 버전(리비전 약 1000)이 이미 30MB
- 시간에 따라 거의 선형 증가하는 모습
로그분석 – 용량 추적
30MB
109MB
과거 기록 분석은 FAIL,
지금의 상황만 가지고 문제점을 찾아 돌파하는 정공법으로 전환
가설 2 : 바이너리 용량이 크게 나오는 게 빌드 시간에 영향을 주지 않을까?
진행 2 : 용량! 용량을 보자!
전략 선택 – 2단계
바이너리 용량 문제 분석
내가 빌드가 느린 건 아무리 생각해도 용량 탓이야!
단순화한 MSVC 컴파일 과정
전처리기
컴파일러
링커
전처리 지시자 처리(#include,#define, …)
모든 헤더파일이 풀린 큰 소스파일 생성
한 소스파일을 컴파일하여 obj 파일 생성
obj 파일과 lib 연결
exe/lib/dll 생성
단순화한 SA2 게임서버 빌드 과정
코어 lib 프로젝트
코어.lib
공통로직.lib
네트워크.lib
데이터베이스.lib
역할별 서버
프론트엔드서버.lib
매칭서버.lib
커뮤니티서버.lib
(그 외 로직 서버들)
단일 런처
서버런처.exe
어떤 문제인지 명확히 모르는 상태에서, 해결책 try
용량 관련한 빌드 옵션 체크 리스트
• 최적화 옵션 O1로?
• 디버깅 정보를 빼고 해볼까?
• 문자열 풀링이 안 되어 있다거나?
샷건 디버깅 – 용량 편
-> 효과는 0.1MB 미만
-> 위와 같음
-> 릴리즈 빌드에서는 강제 켜짐
실패
프론트엔드서버.lib 748MB
매칭서버.lib 107MB
커뮤니티서버.lib 74MB
…
• 로직이 집중된 프론트엔드가 가장 크다
• 나머지들도 정상은 아니다. 로직 양에 비례하는 듯
빌드 중간 파일을 분석해보자
맵 파일 : 빌드 때 옵션을 주면 생성되는 바이너리 구조 명세 TXT
영역별 시작 주소와 길이, 코드 영역의 각종 기호 포함
맵 파일 분석
Start Length Name Class
0001:00000000
0001:00025f30
…
0002:00000000
0002:00000b40
…
0002:000078e0
0002:063c7bf0
0003:00000000
…
00025f2cH
002ccb00H
00000b40H
00000008H
0637bf20H
00088b70H
000029b8H
.text$di
.text$mn
.idata$5
.CRT$XCA
.rdata
.xdata
.data
CODE
CODE
DATA
DATA
DATA
DATA
DATA
수상하다!
.rdata : (주로) 전역 상수,
문자열같은 것이 위치
DUMPBIN : MSVC 내장 툴
실행파일/라이브러리 구조를 조금 더 면밀히 보고 싶을 때 사용
File Type: EXECUTABLE IMAGE
Summary
2606000 .data
28000 .pdata
6455000 .rdata
9000 .reloc
1000 .rsrc
31D000 .text
1000 .tls
DUMPBIN 활용 – 기본 정보
0x6455000
= 105205760
(100MB)
DUMPBIN /section:.rdata
File Type: EXECUTABLE IMAGE
SECTION HEADER #2
.rdata name
6454BFA virtual size
31E000 virtual address (000000014031E000 to 0000000146772BF9)
6454C00 size of raw data
31C800 file pointer to raw data (0031C800 to 067713FF)
…
-> 문제가 맞아 보임, 하지만 아직 정보가 부족
DUMPBIN 활용 – 섹션 상세 정보
DUMPBIN 활용 – rawdata 덤프
DUMPBIN /section:.rdata /rawdata
어마무시한 결과 속에서 반복되는 특정 문자열 패턴 발견
0000000140B9DCB0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000000140B9DCC0: 00 00 00 00 00 00 53 00 41 00 32 00 5F 00 45 00 ......S.A.2._.E.
0000000140B9DCD0: 43 00 5F 00 4D 00 55 00 4C 00 54 00 49 00 50 00 C._.M.U.L.T.I.P.
0000000140B9DCE0: 4C 00 45 00 5F 00 43 00 4F 00 4E 00 4E 00 45 00 L.E._.C.O.N.N.E.
0000000140B9DCF0: 43 00 54 00 49 00 4F 00 4E 00 5F 00 44 00 45 00 C.T.I.O.N._.D.E.
0000000140B9DD00: 4E 00 49 00 45 00 44 00 00 00 00 00 00 00 00 00 N.I.E.D.........
0000000140B9DD10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000000140B9DD20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
초창기에 만들고 잊어버린 에러설명 자동생성코드 ErrorCode.h
const ErrorCode ErrorCode_Table[] = { …
{ -150,
L"SA2_EC_MULTIPLE_CONNECTION_DENIED",
L"동일한 아이디로 접속을 시도했습니다", …},…
코드에서 찾아보자!
헤더파일에서 데이터 입력이 이루어짐
struct ErrorCode {
int code;
wchar_t create_part[21];
wchar_t message[201];
…
};
const ErrorCode ErrorCode_Table[] = { …
{ -150, L“SA2_EC_MULTIPLE_CONNECTION_DENIED", L”동일한 아이디로 접속을 시도했습니다.”, …},…
왜 문제였는가?
헤더파일에 선언된
전역 상수 고정크기 문자열이
각 obj 파일마다 공간 차지
상수 데이터가 오브젝트파일마다 중복 생성되는 것을 제거
• ErrorCode_Table을 헤더에서 extern으로 선언
• 테이블 초기화 코드는 ErrorCode.cpp로 옮김
처치
바이너리크기 : 109MB -> 9MB!
빌드시간 : 미미한 영향(약 10초 감소…)
여담 : 고정크기 문자열을 const wchar_t*로 바꾸기만 해도 13MB로
결과!
빌드 시간 문제 분석
우리 서버 빌드가 이렇게 느릴 리가 없어
C++ 코딩 관련 기본 상식을 잘 지키는지 체크
• 헤더, 특히 시스템 헤더 include를 최소화
• 모듈 의존성을 단순하게
• 헤더와 몸통(cpp) 엄격한 분리 등
빌드 옵션 조절
• 디버깅 레벨 조절
샷건 디버깅 – 시간 편
• PCH(Pre-Compiled Header) 사용 중
• 헤더와 몸통의 분리
• 손으로 짠 모듈에서는 엄격히 지키는 편
• Pimpl idiom도 일부 선택 활용 중
• 자동 생성 코드에서 소홀한 점 추가 발견
• 시스템 / 외부 헤더 파일 관리
• 템플릿 전방선언을 끼얹을 요소 발견
• (헤더에서 boost 타입 참조하는 일부 경우)
우리 프로젝트 체크리스트
자주 포함되는 코어 헤더들의 시스템 헤더 include를 정리하고
자동생성된 코드에도 규칙을 엄격히 적용한 결과
PacketAll.h(모든 패킷 헤더를 다 뭉쳐본 파일)의 전처리 끝난 LOC 변화
183,620 줄 -> 5,278 줄
줄어든 시간 :
• 10초 내외로 거의 변화 없음 orz
• 그 외 디버깅 레벨 등 옵션 조절도 모두 미미한 영향
코드 정리 결과
어이쿠 이 야크 봐라 아주 털이 잘 깎였네?(출처:로이터)
그러고보니, 중간중간 링크 과정에서 대부분의 시간을 소요
빌드 과정을 유심히 보자
링크 과정
봐야 할 기호의 수가 너무 많은 경우
• 네임스페이스를 열어버린 경우(예 : 헤더에서 using namespace std;)
헤더파일 의존성이 복잡한 경우
• 컴파일 타임에 드러나지 않고 링크 타임에 드러난다거나?
• 이번에도 자동 생성 파일이 문제인가?
분산 컴파일은 되는데 왜 분산 링크는 없나?
• 분산된 걸 모으는 게 링크인데 링크를 또 분산하면 분산된 링크를 모으는 링크가..
브레인스토밍 : 링크 시간에 관하여
여기서 떠오른 팀원과의 대화
Q: 근데 빌드하는 데 15분이면 로컬 작업도 힘들잖아요?
A: 아뇨 로컬에선 디버그 빌드로..
디버그 빌드는 : 2분 30초
..아 이 이야기를 분명 처음에 들었는데
잠깐!
링크 과정에서의 병목 현상이 없음
디버그 빌드 과정을 유심히 보자
1. 링크 타임만 오래 걸린다
2. 디버그 모드 빌드에서는 링크 타임이 오래 걸리지 않는다 : 최적화 관련?
-> 전체 프로그램 최적화 / 링크 타임 코드 생성(LTCG)
실마리 발견
컴파일 단계에서 하나의 소스만 가지고 진행되는 일반 최적화에 더해,
컴파일 결과물(obj, lib)에 최적화를 위한 정보를 포함시키고
링크 때마다 포함된 정보+실시간 프로파일링으로 최적화된 바이너리 생성
전체 프로그램 최적화 / 링크타임 코드 생성
빌드 옵션 확인
잘 보이지도 않음
심지어 기본 사용..
링크 타임 코드 생성 옵션 OFF
빌드 시간 15분 -> 1분 55초
후속 조치 : Release 다음 단계, Shipping 빌드 신설
• Release : LTCG OFF
• Shipping : LTCG ON
조치 및 결과
원래 오래 걸린다고 한다
• 링크 타임 오래 걸린다고 하면 LTCG부터 체크하라는 답글이…
우리 프로젝트 빌드 과정 특성때문에 더 심했던 것으로 추측
LTCG, 왜 그리 오래 걸리나?
[다시보기] 단순화한 SA2 게임서버 빌드 과정
코어 lib 프로젝트
코어.lib
공통로직.lib
네트워크.lib
데이터베이스.lib
역할별 서버
프론트엔드서버.lib
매칭서버.lib
커뮤니티서버.lib
(그 외 로직 서버들)
단일 런처
서버런처.exe
[다시보기] 개선 전 릴리즈 빌드 과정
링크 과정
들인 시간
• 업무시간(관여한 모든 사람의) 8시간 남짓
한 일
• 시행착오(샷건 디버깅), 야크쉐이빙(코드정리)에 시간 허비
• Map 파일 분석과 DUMPBIN을 써서 용량문제 발견
• 자동생성코드 위치이동으로 해결
• LTCG 옵션 꺼서(…) CI빌드 시간 단축
회고
소위 삽질이 많았다
- 샷건 디버깅은 핵심 문제에 접근하는 데에는 비효율적
- 귀찮아도 문제를 분석하고 접근하는 버릇을 들이자!
- 실무에서 멀어졌던 팀장이 일 잡았을 때의 폐해
교훈
링크 타임 코드 생성의 단위 비용을 줄일 순 없을까?
- 다단계 링크 구조를 개선해보면?
- Unity Build를 쓰면 더 빠를 것 같은 예감?
- 마지막엔 lib들끼리 링크하는데 소용 없을지도..
Future work
감사합니다.

Contenu connexe

Tendances

임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013
devCAT Studio, NEXON
 
전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012
전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012
전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012
devCAT Studio, NEXON
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
devCAT Studio, NEXON
 
이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019
이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019
이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019
devCAT Studio, NEXON
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
devCAT Studio, NEXON
 
임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
devCAT Studio, NEXON
 
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
devCAT Studio, NEXON
 
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
devCAT Studio, NEXON
 
5 tips for your HTML5 games
5 tips for your HTML5 games5 tips for your HTML5 games
5 tips for your HTML5 games
Ernesto Jiménez
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 

Tendances (20)

임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013
 
What is Game Server ?
What is Game Server ?What is Game Server ?
What is Game Server ?
 
전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012
전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012
전형규, 가성비 좋은 렌더링 테크닉 10선, NDC2012
 
NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
 
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
 
이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019
이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019
이무림, Enum의 Boxing을 어찌할꼬? 편리하고 성능좋게 Enum 사용하기, NDC2019
 
AAA게임_UI_최적화_및_빌드하기.pptx
AAA게임_UI_최적화_및_빌드하기.pptxAAA게임_UI_최적화_및_빌드하기.pptx
AAA게임_UI_최적화_및_빌드하기.pptx
 
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
[NDC 2014] 던전앤파이터 클라이언트 로딩 최적화
 
C++20에서 리플렉션 기능 구현
C++20에서 리플렉션 기능 구현C++20에서 리플렉션 기능 구현
C++20에서 리플렉션 기능 구현
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
 
진선웅 유저수만큼다양한섬을만들자 공개용
진선웅 유저수만큼다양한섬을만들자 공개용진선웅 유저수만큼다양한섬을만들자 공개용
진선웅 유저수만큼다양한섬을만들자 공개용
 
NDC2019 - 게임플레이 프로그래머의 역할
NDC2019 - 게임플레이 프로그래머의 역할NDC2019 - 게임플레이 프로그래머의 역할
NDC2019 - 게임플레이 프로그래머의 역할
 
임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
 
NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀
 
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
 
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
 
5 tips for your HTML5 games
5 tips for your HTML5 games5 tips for your HTML5 games
5 tips for your HTML5 games
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 

En vedette

[0122 구경원]게임에서의 충돌처리
[0122 구경원]게임에서의 충돌처리[0122 구경원]게임에서의 충돌처리
[0122 구경원]게임에서의 충돌처리
KyeongWon Koo
 

En vedette (8)

NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스
 
Sejarah Penubuhan Yayasan Salam Malaysia
Sejarah Penubuhan Yayasan Salam MalaysiaSejarah Penubuhan Yayasan Salam Malaysia
Sejarah Penubuhan Yayasan Salam Malaysia
 
[0122 구경원]게임에서의 충돌처리
[0122 구경원]게임에서의 충돌처리[0122 구경원]게임에서의 충돌처리
[0122 구경원]게임에서의 충돌처리
 
Mercy malaysia
Mercy malaysiaMercy malaysia
Mercy malaysia
 
Programa Electoral de IU Miranda de Ebro
Programa Electoral de IU Miranda de EbroPrograma Electoral de IU Miranda de Ebro
Programa Electoral de IU Miranda de Ebro
 
[160404] 유니티 apk 용량 줄이기
[160404] 유니티 apk 용량 줄이기[160404] 유니티 apk 용량 줄이기
[160404] 유니티 apk 용량 줄이기
 
[160402_데브루키_박민근] UniRx 소개
[160402_데브루키_박민근] UniRx 소개[160402_데브루키_박민근] UniRx 소개
[160402_데브루키_박민근] UniRx 소개
 
유니티의 툰셰이딩을 사용한 3D 애니메이션 표현
유니티의 툰셰이딩을 사용한 3D 애니메이션 표현유니티의 툰셰이딩을 사용한 3D 애니메이션 표현
유니티의 툰셰이딩을 사용한 3D 애니메이션 표현
 

Similaire à NDC15 - 사례로 살펴보는 MSVC 빌드 최적화 팁

[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
ChangKyu Song
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
devCAT Studio, NEXON
 
이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017
이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017
이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017
devCAT Studio, NEXON
 
Mongo db 시작하기
Mongo db 시작하기Mongo db 시작하기
Mongo db 시작하기
OnGameServer
 
빌드 속도를 올려보자
빌드 속도를 올려보자빌드 속도를 올려보자
빌드 속도를 올려보자
KyeongWon Koo
 
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
ChangKyu Song
 
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
Esun Kim
 
버전관리시스템 종류와 소개
버전관리시스템 종류와 소개버전관리시스템 종류와 소개
버전관리시스템 종류와 소개
Jong-il Seok
 
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
중선 곽
 

Similaire à NDC15 - 사례로 살펴보는 MSVC 빌드 최적화 팁 (20)

[NDC17] 왓 스튜디오 서비스파트
[NDC17] 왓 스튜디오 서비스파트[NDC17] 왓 스튜디오 서비스파트
[NDC17] 왓 스튜디오 서비스파트
 
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
 
빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
 
이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017
이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017
이승재, 박경재, NDC Replay 제작기: static website, static backoffice, NDC2017
 
Rhea_MMO_SNG_Convergence_Server_Architecture
Rhea_MMO_SNG_Convergence_Server_ArchitectureRhea_MMO_SNG_Convergence_Server_Architecture
Rhea_MMO_SNG_Convergence_Server_Architecture
 
Mongo db 시작하기
Mongo db 시작하기Mongo db 시작하기
Mongo db 시작하기
 
빌드 속도를 올려보자
빌드 속도를 올려보자빌드 속도를 올려보자
빌드 속도를 올려보자
 
Docker 로 Linux 없이 Linux 환경에서 개발하기
Docker 로 Linux 없이 Linux 환경에서 개발하기Docker 로 Linux 없이 Linux 환경에서 개발하기
Docker 로 Linux 없이 Linux 환경에서 개발하기
 
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
 
젠킨스 설치 및 설정
젠킨스 설치 및 설정젠킨스 설치 및 설정
젠킨스 설치 및 설정
 
ifcpp build guide
ifcpp build guideifcpp build guide
ifcpp build guide
 
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
 
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
C# / .NET Framework로 미래 밥그릇을 챙겨보자 (Basic)
 
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
 
버전관리시스템 종류와 소개
버전관리시스템 종류와 소개버전관리시스템 종류와 소개
버전관리시스템 종류와 소개
 
2021년 4월 10일 개발자 이야기
2021년 4월 10일 개발자 이야기2021년 4월 10일 개발자 이야기
2021년 4월 10일 개발자 이야기
 
도메인주도설계
도메인주도설계도메인주도설계
도메인주도설계
 
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
 
ant로 안드로이드 앱을 자동으로 빌드하자
ant로 안드로이드 앱을 자동으로 빌드하자ant로 안드로이드 앱을 자동으로 빌드하자
ant로 안드로이드 앱을 자동으로 빌드하자
 

NDC15 - 사례로 살펴보는 MSVC 빌드 최적화 팁

  • 1. 사례로 살펴보는 MSVC 빌드 최적화 팁 넥슨지티 서든어택2실 프로그램팀 황의권
  • 2. 발표에 앞서 발표자는 누구고 무슨 이야기를 하려 하는가
  • 3. 황의권 서든어택2 프로그램팀 팀장 2007 - Tibero(RDBMS) 2009.봄 - 가을 카바티나스토리 - 2011.여름 메이플스토리 - 2012.봄 아이러브커피 외 - 현재 서든어택2 발표자 소개 취미 : 코스츔 플레이어
  • 4. 빌드 시간은 개발 생산성에 밀접한 문제 - 특히 게임에선 개발 iteration 비용이 중요 누구나 얘기하지만 잘 챙기긴 어렵다 왜 빌드 발표요? 출처 : compiling (https://xkcd.com/303/)
  • 5. 간단히 검색해 본 NDC 관련 세션 • Unity Build로 빌드타임 반토막내기 – 송창규 [NDC10] • 레가시 프로젝트의 빌드 자동화 – 최재훈 [NDC11] • 효율적인 모바일 게임 개발을 위한 모바일 빌드 시스템과 모바일 배포 시스템 구축 노하우 – 윤보선/김태효 [NDC15] • 하루에 3번! 삼시세끼 빌드 만들기! – 안현석 [NDC15] 빌드 엔지니어링은 단골 주제
  • 6. 다양한 개발 환경은 말할 것도 없고, 하나의 개발 환경에서도 수많은 상황을 고려해야 함 빌드 엔지니어링은 넓고 깊은 주제
  • 7. 사례로 살펴보는 MSVC 빌드 최적화 팁 제목 부연 설명 서든어택2 서버 개발 중 있었던 일 Microsoft Visual C++ 빌드 생성 과정에 대한 엔지니어링 Rule of Thumb 중심 + 가벼운 연관지식
  • 9. 게임 서버 주요 환경 • 언어 환경 : VS2013, C++11(의 일부) • 주요 라이브러리 : boost 1.5x, Intel TBB 4.x • 저장소 : 아직은 Perforce 그 외 개발 스택 • C#, Python/flask • MSSQL, Redis, MongoDB • Linux, Git, AWS, … 개발환경
  • 10. Incredibuild – 상업 분산 빌드 솔루션 사용 중 모든 빌드 관여 머신에 SSD 장착 -> 적절한 돈으로 해결할 수 있는 건 이미 해둔 상태 -> 대신 빌드 머신은 평범 개발 환경 - 빌드 관련 외부 환경
  • 11. “서버 프로젝트 CI빌드가 느려요. 언제부터인진 몰라도..” “원인은 파악 됐나요?” “실마리는 아직, 해결하려면 좀 봐야 할 것 같아요” “그럼 이번 릴리즈 끝난 후 봅시다” 이 대화가 세 번 쯤 반복된 후, 드디어 뚜껑을 열어보기로 결정 회의에서 나온 이야기
  • 12. 서버프로젝트 CI 빌드 시간이 15분+ 서버 통합바이너리 용량이 109MB 상황 확인
  • 13. 전체 개발 사이클의 병목은 아니지만 commit이 망설여짐 상식 밖의 빌드 시간 • 당시 소스파일 크기(LOC) : 약 175K • 데디케이티드 서버는 별도 심각성 분석 – 시간 문제 175K 도표 : 개발일정에 따른 LOC 변화 코드 정리 큰 마일스톤
  • 14. 큰 용량이 기능과 성능에 영향을 주지는 않았음 하지만 바이너리가 109메가면 이거 너무 심한 거 아니오 심각성 분석 – 용량 문제
  • 15. 가설 1 : 이런 이상 상황이 갑자기 발생한 특정한 시점이 있지 않을까? 진행 1 : 로그! 로그를 보자! 전략 선택 – 1단계
  • 16. 이분검색을 해서 문제가 되는 커밋을 찾자 - 총 서버 변경 리비전 수 : 약 3000 -> 이분검색 12(2^12=4096)번, 간단하네? 로그를 분석한다
  • 17. 빌드 시간 검색 • CC.NET CI 빌드 로그 • 검색하기 너무 불편 • 검색툴을 만들까? -> 배보다 배꼽이… • 직접 빌드해보면 되지 않을까? • 12번 리비전이동/빌드 • 하지만 문제가 생긴 시점이 특정된다는 보장이 없다! 로그분석 – 시간 추적
  • 18. CI 빌드 바이너리는 저장소에 커밋되지 않고 있었고orz 대신 서비스 서버 관리 툴이 쓰는 중간 저장소에 드문드문 로그가 있었다 - 최초 서비스 배포 버전(리비전 약 1000)이 이미 30MB - 시간에 따라 거의 선형 증가하는 모습 로그분석 – 용량 추적 30MB 109MB
  • 19. 과거 기록 분석은 FAIL, 지금의 상황만 가지고 문제점을 찾아 돌파하는 정공법으로 전환 가설 2 : 바이너리 용량이 크게 나오는 게 빌드 시간에 영향을 주지 않을까? 진행 2 : 용량! 용량을 보자! 전략 선택 – 2단계
  • 20. 바이너리 용량 문제 분석 내가 빌드가 느린 건 아무리 생각해도 용량 탓이야!
  • 21. 단순화한 MSVC 컴파일 과정 전처리기 컴파일러 링커 전처리 지시자 처리(#include,#define, …) 모든 헤더파일이 풀린 큰 소스파일 생성 한 소스파일을 컴파일하여 obj 파일 생성 obj 파일과 lib 연결 exe/lib/dll 생성
  • 22. 단순화한 SA2 게임서버 빌드 과정 코어 lib 프로젝트 코어.lib 공통로직.lib 네트워크.lib 데이터베이스.lib 역할별 서버 프론트엔드서버.lib 매칭서버.lib 커뮤니티서버.lib (그 외 로직 서버들) 단일 런처 서버런처.exe
  • 23. 어떤 문제인지 명확히 모르는 상태에서, 해결책 try 용량 관련한 빌드 옵션 체크 리스트 • 최적화 옵션 O1로? • 디버깅 정보를 빼고 해볼까? • 문자열 풀링이 안 되어 있다거나? 샷건 디버깅 – 용량 편 -> 효과는 0.1MB 미만 -> 위와 같음 -> 릴리즈 빌드에서는 강제 켜짐 실패
  • 24. 프론트엔드서버.lib 748MB 매칭서버.lib 107MB 커뮤니티서버.lib 74MB … • 로직이 집중된 프론트엔드가 가장 크다 • 나머지들도 정상은 아니다. 로직 양에 비례하는 듯 빌드 중간 파일을 분석해보자
  • 25. 맵 파일 : 빌드 때 옵션을 주면 생성되는 바이너리 구조 명세 TXT 영역별 시작 주소와 길이, 코드 영역의 각종 기호 포함 맵 파일 분석 Start Length Name Class 0001:00000000 0001:00025f30 … 0002:00000000 0002:00000b40 … 0002:000078e0 0002:063c7bf0 0003:00000000 … 00025f2cH 002ccb00H 00000b40H 00000008H 0637bf20H 00088b70H 000029b8H .text$di .text$mn .idata$5 .CRT$XCA .rdata .xdata .data CODE CODE DATA DATA DATA DATA DATA 수상하다! .rdata : (주로) 전역 상수, 문자열같은 것이 위치
  • 26. DUMPBIN : MSVC 내장 툴 실행파일/라이브러리 구조를 조금 더 면밀히 보고 싶을 때 사용 File Type: EXECUTABLE IMAGE Summary 2606000 .data 28000 .pdata 6455000 .rdata 9000 .reloc 1000 .rsrc 31D000 .text 1000 .tls DUMPBIN 활용 – 기본 정보 0x6455000 = 105205760 (100MB)
  • 27. DUMPBIN /section:.rdata File Type: EXECUTABLE IMAGE SECTION HEADER #2 .rdata name 6454BFA virtual size 31E000 virtual address (000000014031E000 to 0000000146772BF9) 6454C00 size of raw data 31C800 file pointer to raw data (0031C800 to 067713FF) … -> 문제가 맞아 보임, 하지만 아직 정보가 부족 DUMPBIN 활용 – 섹션 상세 정보
  • 28. DUMPBIN 활용 – rawdata 덤프 DUMPBIN /section:.rdata /rawdata 어마무시한 결과 속에서 반복되는 특정 문자열 패턴 발견 0000000140B9DCB0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0000000140B9DCC0: 00 00 00 00 00 00 53 00 41 00 32 00 5F 00 45 00 ......S.A.2._.E. 0000000140B9DCD0: 43 00 5F 00 4D 00 55 00 4C 00 54 00 49 00 50 00 C._.M.U.L.T.I.P. 0000000140B9DCE0: 4C 00 45 00 5F 00 43 00 4F 00 4E 00 4E 00 45 00 L.E._.C.O.N.N.E. 0000000140B9DCF0: 43 00 54 00 49 00 4F 00 4E 00 5F 00 44 00 45 00 C.T.I.O.N._.D.E. 0000000140B9DD00: 4E 00 49 00 45 00 44 00 00 00 00 00 00 00 00 00 N.I.E.D......... 0000000140B9DD10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0000000140B9DD20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  • 29. 초창기에 만들고 잊어버린 에러설명 자동생성코드 ErrorCode.h const ErrorCode ErrorCode_Table[] = { … { -150, L"SA2_EC_MULTIPLE_CONNECTION_DENIED", L"동일한 아이디로 접속을 시도했습니다", …},… 코드에서 찾아보자!
  • 30. 헤더파일에서 데이터 입력이 이루어짐 struct ErrorCode { int code; wchar_t create_part[21]; wchar_t message[201]; … }; const ErrorCode ErrorCode_Table[] = { … { -150, L“SA2_EC_MULTIPLE_CONNECTION_DENIED", L”동일한 아이디로 접속을 시도했습니다.”, …},… 왜 문제였는가? 헤더파일에 선언된 전역 상수 고정크기 문자열이 각 obj 파일마다 공간 차지
  • 31. 상수 데이터가 오브젝트파일마다 중복 생성되는 것을 제거 • ErrorCode_Table을 헤더에서 extern으로 선언 • 테이블 초기화 코드는 ErrorCode.cpp로 옮김 처치
  • 32. 바이너리크기 : 109MB -> 9MB! 빌드시간 : 미미한 영향(약 10초 감소…) 여담 : 고정크기 문자열을 const wchar_t*로 바꾸기만 해도 13MB로 결과!
  • 33. 빌드 시간 문제 분석 우리 서버 빌드가 이렇게 느릴 리가 없어
  • 34. C++ 코딩 관련 기본 상식을 잘 지키는지 체크 • 헤더, 특히 시스템 헤더 include를 최소화 • 모듈 의존성을 단순하게 • 헤더와 몸통(cpp) 엄격한 분리 등 빌드 옵션 조절 • 디버깅 레벨 조절 샷건 디버깅 – 시간 편
  • 35. • PCH(Pre-Compiled Header) 사용 중 • 헤더와 몸통의 분리 • 손으로 짠 모듈에서는 엄격히 지키는 편 • Pimpl idiom도 일부 선택 활용 중 • 자동 생성 코드에서 소홀한 점 추가 발견 • 시스템 / 외부 헤더 파일 관리 • 템플릿 전방선언을 끼얹을 요소 발견 • (헤더에서 boost 타입 참조하는 일부 경우) 우리 프로젝트 체크리스트
  • 36. 자주 포함되는 코어 헤더들의 시스템 헤더 include를 정리하고 자동생성된 코드에도 규칙을 엄격히 적용한 결과 PacketAll.h(모든 패킷 헤더를 다 뭉쳐본 파일)의 전처리 끝난 LOC 변화 183,620 줄 -> 5,278 줄 줄어든 시간 : • 10초 내외로 거의 변화 없음 orz • 그 외 디버깅 레벨 등 옵션 조절도 모두 미미한 영향 코드 정리 결과
  • 37. 어이쿠 이 야크 봐라 아주 털이 잘 깎였네?(출처:로이터)
  • 38. 그러고보니, 중간중간 링크 과정에서 대부분의 시간을 소요 빌드 과정을 유심히 보자 링크 과정
  • 39. 봐야 할 기호의 수가 너무 많은 경우 • 네임스페이스를 열어버린 경우(예 : 헤더에서 using namespace std;) 헤더파일 의존성이 복잡한 경우 • 컴파일 타임에 드러나지 않고 링크 타임에 드러난다거나? • 이번에도 자동 생성 파일이 문제인가? 분산 컴파일은 되는데 왜 분산 링크는 없나? • 분산된 걸 모으는 게 링크인데 링크를 또 분산하면 분산된 링크를 모으는 링크가.. 브레인스토밍 : 링크 시간에 관하여
  • 40. 여기서 떠오른 팀원과의 대화 Q: 근데 빌드하는 데 15분이면 로컬 작업도 힘들잖아요? A: 아뇨 로컬에선 디버그 빌드로.. 디버그 빌드는 : 2분 30초 ..아 이 이야기를 분명 처음에 들었는데 잠깐!
  • 41. 링크 과정에서의 병목 현상이 없음 디버그 빌드 과정을 유심히 보자
  • 42. 1. 링크 타임만 오래 걸린다 2. 디버그 모드 빌드에서는 링크 타임이 오래 걸리지 않는다 : 최적화 관련? -> 전체 프로그램 최적화 / 링크 타임 코드 생성(LTCG) 실마리 발견
  • 43. 컴파일 단계에서 하나의 소스만 가지고 진행되는 일반 최적화에 더해, 컴파일 결과물(obj, lib)에 최적화를 위한 정보를 포함시키고 링크 때마다 포함된 정보+실시간 프로파일링으로 최적화된 바이너리 생성 전체 프로그램 최적화 / 링크타임 코드 생성
  • 44. 빌드 옵션 확인 잘 보이지도 않음 심지어 기본 사용..
  • 45. 링크 타임 코드 생성 옵션 OFF 빌드 시간 15분 -> 1분 55초 후속 조치 : Release 다음 단계, Shipping 빌드 신설 • Release : LTCG OFF • Shipping : LTCG ON 조치 및 결과
  • 46. 원래 오래 걸린다고 한다 • 링크 타임 오래 걸린다고 하면 LTCG부터 체크하라는 답글이… 우리 프로젝트 빌드 과정 특성때문에 더 심했던 것으로 추측 LTCG, 왜 그리 오래 걸리나?
  • 47. [다시보기] 단순화한 SA2 게임서버 빌드 과정 코어 lib 프로젝트 코어.lib 공통로직.lib 네트워크.lib 데이터베이스.lib 역할별 서버 프론트엔드서버.lib 매칭서버.lib 커뮤니티서버.lib (그 외 로직 서버들) 단일 런처 서버런처.exe
  • 48. [다시보기] 개선 전 릴리즈 빌드 과정 링크 과정
  • 49. 들인 시간 • 업무시간(관여한 모든 사람의) 8시간 남짓 한 일 • 시행착오(샷건 디버깅), 야크쉐이빙(코드정리)에 시간 허비 • Map 파일 분석과 DUMPBIN을 써서 용량문제 발견 • 자동생성코드 위치이동으로 해결 • LTCG 옵션 꺼서(…) CI빌드 시간 단축 회고
  • 50. 소위 삽질이 많았다 - 샷건 디버깅은 핵심 문제에 접근하는 데에는 비효율적 - 귀찮아도 문제를 분석하고 접근하는 버릇을 들이자! - 실무에서 멀어졌던 팀장이 일 잡았을 때의 폐해 교훈
  • 51. 링크 타임 코드 생성의 단위 비용을 줄일 순 없을까? - 다단계 링크 구조를 개선해보면? - Unity Build를 쓰면 더 빠를 것 같은 예감? - 마지막엔 lib들끼리 링크하는데 소용 없을지도.. Future work