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

5,031 views
5,092 views

Published on

Published in: Technology
0 Comments
28 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,031
On SlideShare
0
From Embeds
0
Number of Embeds
138
Actions
Shares
0
Downloads
108
Comments
0
Likes
28
Embeds 0
No embeds

No notes for slide

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

  1. 1. 사례를 통해 살펴보는 프로파일링과 최적화넥슨김이선veblush at gmail
  2. 2. BNB프로그래머카트라이더리드 프로그래머버블파이터프로토타입리드 프로그래머에버플래닛리드 프로그래머던전엔파이터테크니컬 디렉터GTR프로그래머
  3. 3. 오늘 할 이야기 ?최적화를 어떻게 진행했는지 실제 사례를 들어 설명아키텍쳐의 설명 혹은 프로파일링 툴 사용법에 대한 내용은 아님
  4. 4. 클라이언트초기 구동 최적화클라이언트FPS 최적화서버몬스터 이동 최적화조언목차
  5. 5. 던전엔 파이터 (디버그 빌드)초기 구동 시간 단축
  6. 6. 개발 이터레이션코딩 빌드 초기구동 결과확인F5짧을 수록 좋다
  7. 7. 120초코딩 빌드 초기구동 결과확인F5개발 이터레이션Core i5 760 2.80GHz, 4GB RAM, G2 SSD, XP
  8. 8. 해결할 수 있는 문제인가?이미 최적인지?아니라면 최적에서 얼마나 먼 상태인지?
  9. 9. 프로그램 구동: 하드에 있는 내용을 메모리로200MB던파 (디버그) 클라이언트는 구동 직후 200MB 차지
  10. 10. 근데 하드가 제일 느리잖아그래서 하드 속도 이상은 안 될꺼야…200MB60MBps3.3초Rule of Thumb
  11. 11. 120초 / 3.3초 =36x
  12. 12. 목표< 20초
  13. 13. 핫스팟 찾기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 -
  14. 14. 가장 문제가 되는 부분을 고치는 것이일 한 티가 남 효율이 좋음A:90 B:1030 1090 13x10xAmdahls law2x
  15. 15. 프로파일링!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 -
  16. 16. 어떤 프로파일러를 사용할까?= 어떤 최적화를 할지에 따라 다름
  17. 17. 디자인어셈블리컴파일코드문제레벨탑랭커 3명 추려보이기리스트를 정렬해 상위 3명 얻기sort(list)[:3]/Ox예제
  18. 18. 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);}코드 최적화디자인 최적화
  19. 19. 코드 최적화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
  20. 20. 디자인 최적화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
  21. 21. 디자인어셈블리컴파일코드문제탑랭커 3명 추려보이기리스트를 정렬해 상위 3명 얻기sort(list)[:3]/Ox예제SIMD? PREFETCH?임의로 뽑은 100명중 상위 3명순회만으로 상위 3명 얻기sort(&list)[:3]/Ox /NO_DEBUG최적화x4x100x1000x3x2
  22. 22. 디자인어셈블리컴파일코드문제여지 익숙도레벨 툴여부
  23. 23. CPU 프로파일러레벨CodeAnalystVTuneVisual Studio ProfilerVery SleepyGlow CodeIn-house ProfilerBrainGPU 프로파일러PerfhudPerfstudioGPAPIXX Engine ProfilerEyeIn-house Profiler디자인어셈블리컴파일코드문제
  24. 24. s = clock();do_something_big();print(clock() – s);전체 뷰를 보기 위해번거롭지만 무료선택한 프로파일러핫스팟 코드/함수 보기무료, 간편In-house Profiler AMD CodeAnalyst
  25. 25. In-house Profiler
  26. 26. AMD CodeAnalyst
  27. 27. 테스트 시나리오가 쉽게 반복되도록!개선 정도를 쉽게 확인 할 수 있어야!
  28. 28. 목표설정 프로파일링 최적화결과확인& 검증최적화 과정
  29. 29. 최적화 사례문제확인 원인분석 해결
  30. 30. 디버그에서 STL 이 느림STL Debugging
  31. 31. _ITERATOR_DEBUG_LEVEL(>= VS2012)_SECURE_SCL, _HAS_ITERATOR_DEBUGGING(<= VS2010)STL Debugging
  32. 32. STL Debugging
  33. 33. 디버그 빌드 때_ITERATION_DEBUG_LEVEL = 2 → 1STL DebuggingChecked & DebugChecked
  34. 34. zlib 등 라이브러리 함수가프로파일러에 많이 등장함Optimization Flag
  35. 35. 디버그 빌드라고 모든 모듈을디버그 할 필요 없음!Optimization Flag
  36. 36. 라이브러리중 신뢰도가 높으면서빠른 성능이 필요한 것은최적화 옵션을 사용한 빌드를 사용Optimization Flag
  37. 37. Optimization Flag
  38. 38. 프로젝트 내부에 독립된 모듈로 존재하는 라이브러리는모듈만 최적화 옵션을 사용해 성능 향상을 꾀함Optimization Flag
  39. 39. item_map[0] = Item();// Item::Item() { clear(); }// item_map = std::map<Item>Big Constructor & Map
  40. 40. item_map[0] = Item();// item_map[0] – Item::Item() called// Item() – Item::Item() called// a = b – operator =// ~Item() - Item()::~Item() calledBig Constructor & Map
  41. 41. if item_map.has_key(0)item_map[0].clear();elseitem_map[0];Big Constructor & Map
  42. 42. 이상하게 debug.assert 함수가CPU 를 많이 사용하고 있음ASSERT
  43. 43. #define ASSERT(b) debug.assert(b, _T(#b), …)void debug::assert(b, …) {if (b) return;…ASSERT
  44. 44. #define ASSERT(b) if (b) ; else debug.assert(b, _T(#b), …)ASSERT
  45. 45. 폰트 로딩에 CPU 를 많이 사용하고 있다FontLoad
  46. 46. void load_font(font) {for (g in font-glyphs)max_size = max(max_size, getsize(g));…}FontLoad
  47. 47. 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
  48. 48. ParseString 함수가 많은 시간을 소모ParseString
  49. 49. ParseString(String) → Key, ValueParseString(“Key1>테스트”) → “Key1”, “테스트”ParseString// 주석Key1>테스트Key2>개행도n됩니다n하하하
  50. 50. ParseString길이 제한 검사 (strlen)문자열 마지막의 ‘n’ ‘r’ 제거문자열 앞 공백문자 건너뛰기주석인지 검사‘>’ 위치 찾기키 길이 확인‘>’ 앞뒤의 문자열 얻기 (string assign)“n” 를 찾고 개행 문자로 치환Key1>테스트문자열입니다scanscanscanalloc
  51. 51. ParseString공백 건너뛰기주석이거나 빈줄이면 리턴‘>’ 위치 찾기키가 없거나 너무 길면 에러EOS 찾기 및 “n”를 개행 문자로 치환Key1>테스트문자열입니다scan
  52. 52. 함수 load_data 가 CPU 를 많이 차지Flex Performance
  53. 53. void Read_Game_Data() {for (file in files) {d = load_data(file);item = parse(d);}}Flex Performance
  54. 54. Flex PerformanceDataFileData Grammarload_datafunctionTokensFlexOptionselement :=id |num |stringload_data()generated[power] 1 5[pos] 10 20[power]15…
  55. 55. Flex PerformanceOptions
  56. 56. load_data 생성에 최적화 옵션 사용Flex Performance
  57. 57. 목표설정 프로파일링 최적화결과확인& 검증검증특히 라이브 상태라면무엇보다 중요하다
  58. 58. 최적화 전 동작 = 최적화 후 동작검증
  59. 59. ParseString 의 예// 기존 함수와 새 함수가 동일한// 결과를 반환하는지 확인ParseString(line, key, val);ParseStringNew(line, key2, val2);ASSERT(key == key2 && val == val2);
  60. 60. 15초< 20초2012.9 에 5초 도달
  61. 61. 마비노기2 (개발 빌드)몬스터 200 마리 스폰 상태에서 FPS 올리기
  62. 62. 200 마리 몬스터1fps(개발 빌드에서)I7-2600 (3.40GHz), 8GB, GTS 450, W7-64bit
  63. 63. 목표> 20fps
  64. 64. 어느정도 해결 가능한 것으로 추정내부 프로파일러 정보를 통해 렌더링이병목이 아닌 것은 알고 있었다
  65. 65. 프로파일러AMD CodeAnalystVerySleepyIntel VTune, Scaleform AMP보조적으로 사용
  66. 66. 프로파일러In-house CounterIn-house Profiler
  67. 67. 시작 시간 단축과 달리매번 동일한 상황을 만들기 번거로우니 않으니.F5 -> 게임 입장까지 절차 자동화준비 (실행)
  68. 68. 프로파일러가 콜스택을 잘 보이도록준비 (빌드 옵션)• CL Global Optimization 끔(켜 있으면 /Oy- 와 관계없이 FBO 가 되는 현상이 있음)• CL Frame Buffer Omission 끔(프로파일러가 콜 스택을 잘 보도록)• LINK COMDAT Folding 끔(Identical Function 이 합쳐지는 것 방지)
  69. 69. 몬스터 수 20 → 200 비교하기배수가 아닌 항목을 찾자20 200
  70. 70. 목표설정 프로파일링 최적화결과확인& 검증최적화 과정
  71. 71. 최적화 사례문제확인 원인분석 해결
  72. 72. Enum Constructor단순 enum 타입
  73. 73. Enum ConstructorValueTypeObjectGCvirtualAbnormalConditionType
  74. 74. Enum ConstructorAbnormalConditionTypeValueType
  75. 75. 진단 로그 범람printf 류의 함수가 사용하는 함수
  76. 76. DaignosticScene:개발 보조도구로 거의 모든 게임 정보를텍스트 형태로 볼 수 있는 로그진단 로그 범람
  77. 77. 진단 로그 범람msg = format(“%d %d %d”, pos)diag_log(msg);// if (diag_on)// display_and_save_log(msg)
  78. 78. 진단 로그 범람if (diag_on) {msg = format(“%d %d %d”, pos)diag_log(msg);}
  79. 79. PerformanceCounter Lock
  80. 80. PerformanceCounter LockCounter* FindCounter(name) {scope_lock(lock);return map.find(name);}FindCounter(“update”)->Add(1);
  81. 81. PerformanceCounter LockCounter* FindCounter(name) {scope_read_lock(lock);return map.find(name);}FindCounter(“update”)->Add(1);
  82. 82. 두 벡터의 각도를 구하는 함수에서 시간 소모!두 벡터의 각도
  83. 83. 두 벡터의 각도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);
  84. 84. 두 벡터의 각도float getAngleCos(vec a, vec b) {v = dot(a, b) / len(a) / len(b);return v;}if (getAngleCos(a, b) < cosf(0.2f));
  85. 85. 게임 로직의 대부분의 함수가전반적으로 느림Object Component Locality
  86. 86. Object = Component*Character = [Pos] [Status] [Equip] [Account] …Monster = [Pos] [Status] [Equip] [AI] …Object Component Locality
  87. 87. Object* CreateMonster(info) {obj = new Object()obj->create<PosComponent>();obj->create<StatusComponent>();…return obj;}Object Component LocalityObjectPosStatusAI
  88. 88. void ThinkMonster(Object* m) {pc = m->get<PosComponent>();sc = m->get<StatusComponent>();ac = m->get<AIComponent>();(pc … sc … ac…)…}Object Component LocalityObjectPosStatusAI
  89. 89. Object Component LocalityObject PosStatus AIObject PosStatus AIObject PosStatus AI
  90. 90. 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
  91. 91. 메모리 침범 디버깅을 위한 검사 코드가CPU 를 많이 사용함Allocator Guard
  92. 92. 메모리 침범 디버깅을 위해 guard 버퍼 사용Allocator GuardChunk guard Chunk guard Chunk0xabababab … ababab
  93. 93. Allocator Guardbool memcheck(byte* p, int len) {byte* e = p + len;for (; p < e; ++p) {if (*p != 0xab)return false;return true;}
  94. 94. 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;}
  95. 95. 몬스터간 충돌에 Kynapse 를 사용Kynapse Collision왜 원이 아니고 사각형인가?Collision_RectangularBody
  96. 96. Shape(r, r) vs Shape(r)Kynapse Collision
  97. 97. Kynapse Collision
  98. 98. 검증간단히 가능하지 않으면 대충 넘어감(라이브가 아니라서… 그래서 한 번 터짐…)
  99. 99. 34fps> 20fps
  100. 100. 에버플래닛몬스터 이동 서버 처리량 최적화
  101. 101. MMORPG 는 필드에 몬스터가 가장 많음몬스터는 대부분의 시간을 돌아다니거나 추적하며 보냄때문에 개발 초기 단계부터서버에서 처리 할 몬스터 이동에 대해 고민
  102. 102. 길 찾기 알고리즘(몬스터가 현 위치에서 목표 지점 위치까지 어떻게 가는지)
  103. 103. Athlon 64 x2 3800+ (2.0GHz), 2GB, XPA* Hierarchical A* Go & Turn81 ops 3,668 ops 488,281 ops
  104. 104. Go & Turn Hierarchical A*Fail?
  105. 105. Go & Turn 으로 95% 이상 길 찾기 가능▽서버에서 초당 (코어당) 400,000 번 길 찾기 가능
  106. 106. Idle 상태의 몬스터 이동 최적화
  107. 107. t=0t=10t=20f(t, pos, seed)서버가 t, pos, seed 를전송하면 클라이언트가위치 계산 가능이동에 대한 데이터를더 이상 보낼 필요 없음서버 부하 줄어듬네트워크 부하 줄어듬
  108. 108. 나이스?
  109. 109. 조언
  110. 110. 목표설정 프로파일링 최적화결과확인& 검증최적화 과정목표가 있어야작업이 종료됨Rule of Thumb적절한 툴/방법 찾기 상상력 동원 성능 향상 확인 및지식화검증은 라이브라면더더욱!
  111. 111. 최적화 단계를 개발 단계에 넣을 것최적화는 디버깅과 유사 → 나중에 하면 어렵다!최저 사양 / 권장 사양 PC 를 옆에 두고사용하는 것도 좋은 방법.개발 단계
  112. 112. 성능 지표를 개발 / 라이브 단계에서주기적으로 확인 필요성능은 단순한 버그로도 급격하게나빠질 수 있다.개발 단계
  113. 113. 자주 불리는 함수는 최적화에 신경써야초당 1000번 불리는 함수는 초당 1번 불리는 것에 1000배?개발 단계
  114. 114. 설계 단계에서 성능 고려이미 결정된 설계를 나중에 변경하기란 쉽지 않다개발 단계
  115. 115. 감사합니다!

×