사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
Upcoming SlideShare
Loading in...5
×
 

사례를 통해 살펴보는 프로파일링과 최적화 NDC2013

on

  • 3,904 views

 

Statistics

Views

Total Views
3,904
Views on SlideShare
3,777
Embed Views
127

Actions

Likes
12
Downloads
71
Comments
0

2 Embeds 127

https://twitter.com 103
http://10.1.1.10 24

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

사례를 통해 살펴보는 프로파일링과 최적화 NDC2013 사례를 통해 살펴보는 프로파일링과 최적화 NDC2013 Presentation Transcript

  • 사례를 통해 살펴보는 프로파일링과 최적화넥슨김이선veblush at gmail
  • BNB프로그래머카트라이더리드 프로그래머버블파이터프로토타입리드 프로그래머에버플래닛리드 프로그래머던전엔파이터테크니컬 디렉터GTR프로그래머
  • 오늘 할 이야기 ?최적화를 어떻게 진행했는지 실제 사례를 들어 설명아키텍쳐의 설명 혹은 프로파일링 툴 사용법에 대한 내용은 아님
  • 클라이언트초기 구동 최적화클라이언트FPS 최적화서버몬스터 이동 최적화조언목차
  • 던전엔 파이터 (디버그 빌드)초기 구동 시간 단축
  • 개발 이터레이션코딩 빌드 초기구동 결과확인F5짧을 수록 좋다
  • 120초코딩 빌드 초기구동 결과확인F5개발 이터레이션Core i5 760 2.80GHz, 4GB RAM, G2 SSD, XP
  • 해결할 수 있는 문제인가?이미 최적인지?아니라면 최적에서 얼마나 먼 상태인지?
  • 프로그램 구동: 하드에 있는 내용을 메모리로200MB던파 (디버그) 클라이언트는 구동 직후 200MB 차지
  • 근데 하드가 제일 느리잖아그래서 하드 속도 이상은 안 될꺼야…200MB60MBps3.3초Rule of Thumb
  • 120초 / 3.3초 =36x
  • 목표< 20초
  • 핫스팟 찾기A hot spot in computer science is most usually defined as a region of a computer programwhere a high proportion of executed instructions occur orwhere most time is spent during the programs execution(not necessarily the same thing since some instructions are faster than others)- Wikipedia -
  • 가장 문제가 되는 부분을 고치는 것이일 한 티가 남 효율이 좋음A:90 B:1030 1090 13x10xAmdahls law2x
  • 프로파일링!In software engineering, profiling ("program profiling", "software profiling") is a form ofdynamic program analysis that measures, for example, the space (memory) or time complexity of a program,the usage of particular instructions, or frequency and duration of function calls.The most common use of profiling information is to aid program optimization.- Wikipedia -
  • 어떤 프로파일러를 사용할까?= 어떤 최적화를 할지에 따라 다름
  • 디자인어셈블리컴파일코드문제레벨탑랭커 3명 추려보이기리스트를 정렬해 상위 3명 얻기sort(list)[:3]/Ox예제
  • typedef vector<pair<int, string>> Scores;void GetRanker(const Scores& l, Scores& r) {// sort(list)[:3]ScoreList m = l;sort(m.begin(), m.end(), Scores_less);r.assign(m.begin(), m.begin() + 3);}코드 최적화디자인 최적화
  • 코드 최적화void GetRanker(const Scores& l, Scores& r) {vector<const Scores::value_type*> m(l.size());for (size_t i=0, j=l.size(); i != j; ++i)m[i] = &(l[i]);sort(m.begin(), m.end(), pScoreList_less);for (size_t i=0; i < 3; i++)r.push_back(*m[i]);}4x
  • 디자인 최적화void GetRanker(const Scores& l, Scores& r) {// 리스트를 순회하며 가장 높은 점수 3개 찾기for (auto i=l.begin(), j=l.end(); i != j; ++i) {if (r.empty() || r.back().first < i->first) {auto j = upper_bound(r.begin(), r.end(), *i);r.insert(j, *i);if (r.size() > 3)r.pop_back();}}}+100x
  • 디자인어셈블리컴파일코드문제탑랭커 3명 추려보이기리스트를 정렬해 상위 3명 얻기sort(list)[:3]/Ox예제SIMD? PREFETCH?임의로 뽑은 100명중 상위 3명순회만으로 상위 3명 얻기sort(&list)[:3]/Ox /NO_DEBUG최적화x4x100x1000x3x2
  • 디자인어셈블리컴파일코드문제여지 익숙도레벨 툴여부
  • CPU 프로파일러레벨CodeAnalystVTuneVisual Studio ProfilerVery SleepyGlow CodeIn-house ProfilerBrainGPU 프로파일러PerfhudPerfstudioGPAPIXX Engine ProfilerEyeIn-house Profiler디자인어셈블리컴파일코드문제
  • s = clock();do_something_big();print(clock() – s);전체 뷰를 보기 위해번거롭지만 무료선택한 프로파일러핫스팟 코드/함수 보기무료, 간편In-house Profiler AMD CodeAnalyst
  • In-house Profiler
  • AMD CodeAnalyst
  • 테스트 시나리오가 쉽게 반복되도록!개선 정도를 쉽게 확인 할 수 있어야!
  • 목표설정 프로파일링 최적화결과확인& 검증최적화 과정
  • 최적화 사례문제확인 원인분석 해결
  • 디버그에서 STL 이 느림STL Debugging
  • _ITERATOR_DEBUG_LEVEL(>= VS2012)_SECURE_SCL, _HAS_ITERATOR_DEBUGGING(<= VS2010)STL Debugging
  • STL Debugging
  • 디버그 빌드 때_ITERATION_DEBUG_LEVEL = 2 → 1STL DebuggingChecked & DebugChecked
  • zlib 등 라이브러리 함수가프로파일러에 많이 등장함Optimization Flag
  • 디버그 빌드라고 모든 모듈을디버그 할 필요 없음!Optimization Flag
  • 라이브러리중 신뢰도가 높으면서빠른 성능이 필요한 것은최적화 옵션을 사용한 빌드를 사용Optimization Flag
  • Optimization Flag
  • 프로젝트 내부에 독립된 모듈로 존재하는 라이브러리는모듈만 최적화 옵션을 사용해 성능 향상을 꾀함Optimization Flag
  • item_map[0] = Item();// Item::Item() { clear(); }// item_map = std::map<Item>Big Constructor & Map
  • item_map[0] = Item();// item_map[0] – Item::Item() called// Item() – Item::Item() called// a = b – operator =// ~Item() - Item()::~Item() calledBig Constructor & Map
  • if item_map.has_key(0)item_map[0].clear();elseitem_map[0];Big Constructor & Map
  • 이상하게 debug.assert 함수가CPU 를 많이 사용하고 있음ASSERT
  • #define ASSERT(b) debug.assert(b, _T(#b), …)void debug::assert(b, …) {if (b) return;…ASSERT
  • #define ASSERT(b) if (b) ; else debug.assert(b, _T(#b), …)ASSERT
  • 폰트 로딩에 CPU 를 많이 사용하고 있다FontLoad
  • void load_font(font) {for (g in font-glyphs)max_size = max(max_size, getsize(g));…}FontLoad
  • void load_font(font) {if size_cache_map.has(font, size)max_size = size_cache_map[font, size];elsefor (g in font-glyphs)max_size = max(max_size, getsize(g));log(“missing:”, font, size);…FontLoad
  • ParseString 함수가 많은 시간을 소모ParseString
  • ParseString(String) → Key, ValueParseString(“Key1>테스트”) → “Key1”, “테스트”ParseString// 주석Key1>테스트Key2>개행도n됩니다n하하하
  • ParseString길이 제한 검사 (strlen)문자열 마지막의 ‘n’ ‘r’ 제거문자열 앞 공백문자 건너뛰기주석인지 검사‘>’ 위치 찾기키 길이 확인‘>’ 앞뒤의 문자열 얻기 (string assign)“n” 를 찾고 개행 문자로 치환Key1>테스트문자열입니다scanscanscanalloc
  • ParseString공백 건너뛰기주석이거나 빈줄이면 리턴‘>’ 위치 찾기키가 없거나 너무 길면 에러EOS 찾기 및 “n”를 개행 문자로 치환Key1>테스트문자열입니다scan
  • 함수 load_data 가 CPU 를 많이 차지Flex Performance
  • void Read_Game_Data() {for (file in files) {d = load_data(file);item = parse(d);}}Flex Performance
  • Flex PerformanceDataFileData Grammarload_datafunctionTokensFlexOptionselement :=id |num |stringload_data()generated[power] 1 5[pos] 10 20[power]15…
  • Flex PerformanceOptions
  • load_data 생성에 최적화 옵션 사용Flex Performance
  • 목표설정 프로파일링 최적화결과확인& 검증검증특히 라이브 상태라면무엇보다 중요하다
  • 최적화 전 동작 = 최적화 후 동작검증
  • ParseString 의 예// 기존 함수와 새 함수가 동일한// 결과를 반환하는지 확인ParseString(line, key, val);ParseStringNew(line, key2, val2);ASSERT(key == key2 && val == val2);
  • 15초< 20초2012.9 에 5초 도달
  • 마비노기2 (개발 빌드)몬스터 200 마리 스폰 상태에서 FPS 올리기
  • 200 마리 몬스터1fps(개발 빌드에서)I7-2600 (3.40GHz), 8GB, GTS 450, W7-64bit
  • 목표> 20fps
  • 어느정도 해결 가능한 것으로 추정내부 프로파일러 정보를 통해 렌더링이병목이 아닌 것은 알고 있었다
  • 프로파일러AMD CodeAnalystVerySleepyIntel VTune, Scaleform AMP보조적으로 사용
  • 프로파일러In-house CounterIn-house Profiler
  • 시작 시간 단축과 달리매번 동일한 상황을 만들기 번거로우니 않으니.F5 -> 게임 입장까지 절차 자동화준비 (실행)
  • 프로파일러가 콜스택을 잘 보이도록준비 (빌드 옵션)• CL Global Optimization 끔(켜 있으면 /Oy- 와 관계없이 FBO 가 되는 현상이 있음)• CL Frame Buffer Omission 끔(프로파일러가 콜 스택을 잘 보도록)• LINK COMDAT Folding 끔(Identical Function 이 합쳐지는 것 방지)
  • 몬스터 수 20 → 200 비교하기배수가 아닌 항목을 찾자20 200
  • 목표설정 프로파일링 최적화결과확인& 검증최적화 과정
  • 최적화 사례문제확인 원인분석 해결
  • Enum Constructor단순 enum 타입
  • Enum ConstructorValueTypeObjectGCvirtualAbnormalConditionType
  • Enum ConstructorAbnormalConditionTypeValueType
  • 진단 로그 범람printf 류의 함수가 사용하는 함수
  • DaignosticScene:개발 보조도구로 거의 모든 게임 정보를텍스트 형태로 볼 수 있는 로그진단 로그 범람
  • 진단 로그 범람msg = format(“%d %d %d”, pos)diag_log(msg);// if (diag_on)// display_and_save_log(msg)
  • 진단 로그 범람if (diag_on) {msg = format(“%d %d %d”, pos)diag_log(msg);}
  • PerformanceCounter Lock
  • PerformanceCounter LockCounter* FindCounter(name) {scope_lock(lock);return map.find(name);}FindCounter(“update”)->Add(1);
  • PerformanceCounter LockCounter* FindCounter(name) {scope_read_lock(lock);return map.find(name);}FindCounter(“update”)->Add(1);
  • 두 벡터의 각도를 구하는 함수에서 시간 소모!두 벡터의 각도
  • 두 벡터의 각도float getAngle(vec a, vec b) {r = atan2f(a.y, a.x);v = atan2f(b.y, b.x);return r - v;}if (abs(getAngle(a, b)) < 0.2f);
  • 두 벡터의 각도float getAngleCos(vec a, vec b) {v = dot(a, b) / len(a) / len(b);return v;}if (getAngleCos(a, b) < cosf(0.2f));
  • 게임 로직의 대부분의 함수가전반적으로 느림Object Component Locality
  • Object = Component*Character = [Pos] [Status] [Equip] [Account] …Monster = [Pos] [Status] [Equip] [AI] …Object Component Locality
  • Object* CreateMonster(info) {obj = new Object()obj->create<PosComponent>();obj->create<StatusComponent>();…return obj;}Object Component LocalityObjectPosStatusAI
  • void ThinkMonster(Object* m) {pc = m->get<PosComponent>();sc = m->get<StatusComponent>();ac = m->get<AIComponent>();(pc … sc … ac…)…}Object Component LocalityObjectPosStatusAI
  • Object Component LocalityObject PosStatus AIObject PosStatus AIObject PosStatus AI
  • Object* CreateMonster(info) {obj = new Object()tls_obj = obj;obj->create<PosComponent>();…void* Component::operator new(size_t s) {return tls_obj->malloc(s);}Object Component Locality
  • 메모리 침범 디버깅을 위한 검사 코드가CPU 를 많이 사용함Allocator Guard
  • 메모리 침범 디버깅을 위해 guard 버퍼 사용Allocator GuardChunk guard Chunk guard Chunk0xabababab … ababab
  • Allocator Guardbool memcheck(byte* p, int len) {byte* e = p + len;for (; p < e; ++p) {if (*p != 0xab)return false;return true;}
  • Allocator Guardbool memcheck(byte* p, int len) {dword* d = (dword*)pdword* e = p + len / 4;for (; d < e; ++d) {if (*d != 0xabababab)return false;return true;}
  • 몬스터간 충돌에 Kynapse 를 사용Kynapse Collision왜 원이 아니고 사각형인가?Collision_RectangularBody
  • Shape(r, r) vs Shape(r)Kynapse Collision
  • Kynapse Collision
  • 검증간단히 가능하지 않으면 대충 넘어감(라이브가 아니라서… 그래서 한 번 터짐…)
  • 34fps> 20fps
  • 에버플래닛몬스터 이동 서버 처리량 최적화
  • MMORPG 는 필드에 몬스터가 가장 많음몬스터는 대부분의 시간을 돌아다니거나 추적하며 보냄때문에 개발 초기 단계부터서버에서 처리 할 몬스터 이동에 대해 고민
  • 길 찾기 알고리즘(몬스터가 현 위치에서 목표 지점 위치까지 어떻게 가는지)
  • Athlon 64 x2 3800+ (2.0GHz), 2GB, XPA* Hierarchical A* Go & Turn81 ops 3,668 ops 488,281 ops
  • Go & Turn Hierarchical A*Fail?
  • Go & Turn 으로 95% 이상 길 찾기 가능▽서버에서 초당 (코어당) 400,000 번 길 찾기 가능
  • Idle 상태의 몬스터 이동 최적화
  • t=0t=10t=20f(t, pos, seed)서버가 t, pos, seed 를전송하면 클라이언트가위치 계산 가능이동에 대한 데이터를더 이상 보낼 필요 없음서버 부하 줄어듬네트워크 부하 줄어듬
  • 나이스?
  • 조언
  • 목표설정 프로파일링 최적화결과확인& 검증최적화 과정목표가 있어야작업이 종료됨Rule of Thumb적절한 툴/방법 찾기 상상력 동원 성능 향상 확인 및지식화검증은 라이브라면더더욱!
  • 최적화 단계를 개발 단계에 넣을 것최적화는 디버깅과 유사 → 나중에 하면 어렵다!최저 사양 / 권장 사양 PC 를 옆에 두고사용하는 것도 좋은 방법.개발 단계
  • 성능 지표를 개발 / 라이브 단계에서주기적으로 확인 필요성능은 단순한 버그로도 급격하게나빠질 수 있다.개발 단계
  • 자주 불리는 함수는 최적화에 신경써야초당 1000번 불리는 함수는 초당 1번 불리는 것에 1000배?개발 단계
  • 설계 단계에서 성능 고려이미 결정된 설계를 나중에 변경하기란 쉽지 않다개발 단계
  • 감사합니다!