Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Profiling - 실시간 대화식 프로파일러

5,412 views

Published on

PyCon KR 2015에서 Profiling에 대해 이야기 했습니다.

[Profiling]: https://github.com/what-studio/profiling
[발표 소개]: http://www.pycon.kr/2015/program/45

Published in: Software
  • Be the first to comment

Profiling - 실시간 대화식 프로파일러

  1. 1. Profiling 실시간 대화식 프로파일러 이흥섭 @ 넥슨 • 왓 스튜디오 PyCon KR 2015
  2. 2. 이흥섭 sub@nexon.co.kr • http://subl.ee/ • sublee
  3. 3. • 한글라이즈 http://hangulize.org/ • TrueSkill 파이썬 구현 http://trueskill.org/ • Profiling • <카트라이더 대시> • <야생의 땅: 듀랑고> http://durango.nexon.com/
  4. 4. Profiling what-studio/profiling
  5. 5. 게임서버 최적화 파이썬 프로파일러
  6. 6. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/interactive.gif
  7. 7. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/continuous.gif
  8. 8. 게임서버
  9. 9. Linux CPython 2.7 gevent
  10. 10. 요청/응답
  11. 11. 시야 내 동기화
  12. 12. 생태계 AI
  13. 13. 요청/응답 시야 내 동기화 생태계 AI I/O 바운드 CPU 바운드
  14. 14. CPU 병목이 어딜까?
  15. 15. 프로파일러 • 성능 분석 도구 • 함수 별 실행 시간, 호출 횟수, 메모리 사용량 등 수집 • 프로그램 최적화를 보조함.
  16. 16. 1. profile/cProfile 2. Yappi
  17. 17. 이벤트 기반 프로파일링 • 함수 호출/반환 이벤트 추적 • 실행 시간, 호출 횟수를 조사 • Exclusive ― 서브루틴 제외 • Inclusive ― 서브루틴 포함
  18. 18. def spin(sec): t = time.time() while time.time() - t < sec: pass def spin5(): spin(5) spin5() Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/spin_example.py
  19. 19. InclusiveExclusive spin5() spin(5)
  20. 20. profile/cProfile
  21. 21. $ python -m profile script.py $ python -m cProfile script.py
  22. 22. 396029 function calls in 3.000 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 3.000 3.000 script.py:1(<module>) 1 0.000 0.000 1.000 1.000 script.py:10(alice) 1 0.000 0.000 2.000 2.000 script.py:14(bob) 2 1.074 0.537 3.000 1.500 script.py:4(carol) 1 0.000 0.000 0.000 0.000 {method 'disable' of ... 396023 1.926 0.000 1.926 0.000 {time.time}
  23. 23. ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 3.000 3.000 script.py:1(<module>) 1 0.000 0.000 1.000 1.000 script.py:10(alice) 1 0.000 0.000 2.000 2.000 script.py:14(bob) 2 1.074 0.537 3.000 1.500 script.py:4(carol) 1 0.000 0.000 0.000 0.000 {method 'disable' of ... 396023 1.926 0.000 1.926 0.000 {time.time} Exclusive Inclusive
  24. 24. gevent와 잘 붙나?
  25. 25. gevent • 코루틴 기반 네트워킹 라이브러리 • 경량 스레드: Greenlet 참고: <제약을 넘어: Gevent> ― 정민영 • PyCon KR 2014 https://goo.gl/h0OmE7
  26. 26. 서브루틴 코루틴
  27. 27. # gevent-example.py import gevent def slower(): for x in range(4): for y in range(10000000): pass gevent.sleep(0) def faster(): for x in range(2): for y in range(10000000): pass gevent.sleep(0) gevent.spawn(slower) gevent.spawn(faster) gevent.wait() Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/gevent_example.py
  28. 28. 기대 결과 slower > faster (약 2배)
  29. 29. $ python -m profile gevent-example.py
  30. 30. Traceback (most recent call last): ... File ".../profile.py", line 211, in trace_dispatch if self.dispatch[event](self, frame,t): File ".../profile.py", line 283, in trace_dispatch_call frame, frame.f_back) AssertionError: ('Bad call', ('.../gevent/hub.py', 386, 'switch'), ...)
  31. 31. $ python -m cProfile gevent-example.py
  32. 32. 함수 Exclusive Inclusive slower 0.129 0.645 faster 0.340 0.341
  33. 33. profile/cProfile gevent 호환 안 됨
  34. 34. Yappi Yet Another Python Profiler
  35. 35. Yappi • 멀티스레딩 지원 • 스레드 별 CPU 시간 측정 • 빠름 (적은 분석 오버헤드)
  36. 36. $ python -m yappi script.py
  37. 37. Clock type: CPU Ordered by: totaltime, desc name ncall tsub ttot tavg script.py:1 <module> 1 0.000018 1.815449 1.815449 script.py:4 foo 2 0.288737 1.815422 0.907711 script.py:14 bar 1 0.000004 1.001799 1.001799 script.py:10 baz 1 0.000005 0.813632 0.813632 name tid ttot scnt _MainThread 139638664439616 1.816491 1
  38. 38. Clock type: CPU Ordered by: totaltime, desc name ncall tsub ttot tavg script.py:1 <module> 1 0.000018 1.815449 1.815449 script.py:4 foo 2 0.288737 1.815422 0.907711 script.py:14 bar 1 0.000004 1.001799 1.001799 script.py:10 baz 1 0.000005 0.813632 0.813632 name tid ttot scnt _MainThread 139638664439616 1.816491 1 Exclusive Inclusive
  39. 39. # gevent-example.py import gevent def slower(): for x in range(4): for y in range(10000000): pass gevent.sleep(0) def faster(): for x in range(2): for y in range(10000000): pass gevent.sleep(0) gevent.spawn(slower) gevent.spawn(faster) gevent.wait() slower < faster (약 2배) Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/gevent_example.py
  40. 40. 함수 Exclusive Inclusive slower 0.105112 0.641017 faster 0.105722 0.430493 ???
  41. 41. 스레드 식별자 설정 set_context_id_callback( f ) f → 스레드 Id set_context_name_callback( f ) f → 스레드 타입 이름
  42. 42. # gyappi.py from gevent import getcurrent import yappi @yappi.set_context_id_callback def current_greenlet_id(): return id(getcurrent()) @yappi.set_context_name_callback def current_greenlet_name(): return type(getcurrent()).__name__ yappi.main() Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/gyappi.py
  43. 43. $ python -m gyappi gevent-example.py
  44. 44. Clock type: CPU Ordered by: totaltime, desc name ncall tsub ttot tavg gevent-example.py:1 <module> 1 0.000047 0.660959 0.660959 gevent-example.py:3 slower 1 0.421846 0.637329 0.637329 gevent-example.py:8 faster 1 0.214511 0.425404 0.425404 name tid ttot scnt _getframe 139755003744080 0.674929 2 Hub 139754981772656 0.670471 8 Greenlet 139754981772816 0.660287 5 Greenlet 139754981773136 0.534862 3
  45. 45. 함수 Exclusive Inclusive slower 0.421846 0.637329 faster 0.214511 0.425404
  46. 46. Yappi gevent 호환 잘 됨
  47. 47. 프로그램 시작 프로그램 끝 측정 분석결과 출력
  48. 48. 측정…
  49. 49. 측정… 분석결과 출력
  50. 50. 측정 측정 측정 측정 분석결과 출력 분석결과 출력 분석결과 출력 분석결과 출력
  51. 51. 측정 측정 측정 측정 뷰어 분석결과 분석결과 분석결과 분석결과
  52. 52. Yappi 뷰어
  53. 53. Yappi 뷰어
  54. 54. 2 3 1 로깅 문자열 처리 난수 발생기 게임로직
  55. 55. 보편적으로 불리는 함수가 병목처럼 보임
  56. 56. 콜스택 계층구조 보존 로깅로깅 ≠
  57. 57. 직접 만들어보자 Profiling
  58. 58. • Unity3D 프로파일러
  59. 59. 요구사항 콜스택 계층구조 보존 gevent 호환 대화식 뷰어 실시간 프로파일링
  60. 60. 함수 실행 추적 sys.setprofile( f ) threading.setprofile( f )
  61. 61. def spin(sec): f (<frame of spin>, 'call', ...) t = time.time() while time.time() - t < sec: pass f (<frame of spin>, 'return', ...) def spin5(): f (<frame of spin5>, 'call', ...) spin(5) f (<frame of spin5>, 'return', ...)
  62. 62. frame • f_back • f_code • f_lineno 상위 frame code • co_name • co_filename <frame>
  63. 63. frame • f_back • f_code • f_lineno frame frame frame top-frame stack = deque() while frame is not None: stack.appendleft(frame) frame = frame.f_back
  64. 64. 요구사항 콜스택 계층구조 보존 gevent 호환 대화식 뷰어 실시간 프로파일링
  65. 65. Greenlet 문맥 추적 greenlet.settrace( f )
  66. 66. # gevent-example.py import gevent def slower(): for x in range(4): for y in range(10000000): pass gevent.sleep(0) def faster(): for x in range(2): for y in range(10000000): pass gevent.sleep(0) gevent.spawn(slower) gevent.spawn(faster) gevent.wait() slower < faster (약 2배) Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/gevent_example.py
  67. 67. slowerHub faster
  68. 68. slowerHub faster f ('switch', (Hub, slower)) f ('switch', (slower, Hub)) f ('switch', (Hub, faster)) f ('switch', (faster, Hub)) f ('switch', (Hub, slower))
  69. 69. slower faster
  70. 70. 함수 Exclusive Inclusive slower 0.424 0.641 faster 0.212 0.429
  71. 71. 요구사항 콜스택 계층구조 보존 gevent 호환 대화식 뷰어 실시간 프로파일링
  72. 72. 대화식 뷰어는 TUI로
  73. 73. Curses
  74. 74. Urwid • TUI 라이브러리 • 위젯 단위 • TreeListBox • Curses의 저주에서 구원
  75. 75. Exclusive Inclusive Profiling OWN DEEP Yappi tsub ttot profile tottime cumtime
  76. 76. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/interactive.gif
  77. 77. 요구사항 콜스택 계층구조 보존 gevent 호환 대화식 뷰어 실시간 프로파일링
  78. 78. 뷰어 Pickle 프로그램 프로파일링 서버 측정 측정 측정
  79. 79. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/continuous.gif
  80. 80. 요구사항 콜스택 계층구조 보존 gevent 호환 대화식 뷰어 실시간 프로파일링
  81. 81. 느리다!
  82. 82. 특히 YAML 파싱
  83. 83. def f(): pass def shallow(n): for x in range(n): f() def deep(n): if n != 0: deep(n - 1) Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/subcalls_example.py
  84. 84. def f(): pass def shallow(n): for x in range(n): f() def deep(n): if n != 0: deep(n - 1)
  85. 85. def f(): pass def shallow(n): for x in range(n): f() def deep(n): if n != 0: deep(n - 1)
  86. 86. shallow(100) deep(100) 그냥 실행 0.01 0.02 Profiling 2.27 6.07 Yappi 0.07 0.07 cProfile 0.60 0.61 profile 1.26 1.25 (단위: ㎳)
  87. 87. http://goo.gl/Wjc7Jc
  88. 88. 통계적 프로파일링
  89. 89. 이벤트 기반 프로파일링 • 실행 시간, 호출 횟수 측정 • 이벤트 전수 조사 e.g. profile, hotshot, Yappi, line_profiler 통계적 프로파일링 • 상대적 실행 빈도 측정 • 표본 조사 e.g. pyinstrument, plop, pprofile
  90. 90. 통계적 프로파일링 • 샘플링 할 때 실행 중이던 콜스택을 조사 • Exclusive Count ― 콜스택 말단이던 횟수 • Inclusive Count ― 콜스택에 속했던 횟수
  91. 91. def spin(sec): t = time.time() while time.time() - t < sec: pass def spin5(): spin(5) spin5() Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/spin_example.py
  92. 92. spin5() spin(5) Exclusive Count = 1 Inclusive Count = 1 Inclusive Count = 2 Inclusive Count = 3 Inclusive Count = 4 Exclusive Count = 3 Inclusive Count = 3 Exclusive Count = 2 Inclusive Count = 2 Exclusive Count = 1 Inclusive Count = 1
  93. 93. 함수 Exclusive % Inclusive % spin5 25% (1/4) 100% (4/4) spin 75% (3/4) 75% (3/4)
  94. 94. 요구사항 #2 이벤트 기반 프로파일링 통계적 프로파일링
  95. 95. 표본 얻기 sys._current_frames() 스레드 별 <frame> dict
  96. 96. 주기적 조사 signal.setitimer(ITIMER_PROF, t, t) CPU 시간이 t만큼 흐를 때마다 SIGPROF 발동
  97. 97. 시그널 • time.sleep()을 끊는다. • IOError: EINTR • thread.join() 중엔 발동하지 않는다.
  98. 98. pyinstrument가 쓰는 다른 방법 sys.setprofile(f) threading.setprofile(f) 추적 함수에서 시간을 재다가 때가 되면 조사 앞 방법 보단 느리다.
  99. 99. shallow(100) deep(100) 그냥 실행 0.01 0.02 setitimer로 샘플링 0.01 0.02 setprofile로 샘플링 0.19 0.22 Profiling 2.27 6.07 Yappi 0.07 0.07 cProfile 0.60 0.61 profile 1.26 1.25 (단위: ㎳)
  100. 100. 요구사항 #2 이벤트 기반 프로파일링 통계적 프로파일링
  101. 101. 사용법
  102. 102. $ pip install git+https://github.com/what-studio/profiling.git
  103. 103. $ python -m profiling COMMAND • profile • live-profile • remote-profile • view
  104. 104. $ python -m profiling profile SCRIPT Yappi, profile/cProfile 커맨드 처럼 스크립트 통째로 분석
  105. 105. -T --timer= • basic • thread • greenlet -S --sampler= • itimer signal.setitimer • tracing sys.setprofile, threading.setprofile 통계적 프로파일링 SamplingProfiler 이벤트 기반 프로파일링 TracingProfiler
  106. 106. -T (이벤트 기반 프로파일링) -S (통계적 프로파일링)
  107. 107. $ python -m profiling live-profile SCRIPT 스크립트를 돌리면서 실시간 분석 -i 갱신 주기 (기본: 1초)
  108. 108. $ python -m profiling remote-profile SCRIPT 스크립트를 돌리다가 뷰어가 붙으면 분석 --bind 서버 엔드포인트 (기본: 127.0.0.1:8912)
  109. 109. $ python -m profiling view HOST:PORT 대화식 뷰어로 분석결과 보기
  110. 110. H J K L Q ^C [ ] 종료 탐색 (포커스 이동) 접기/펼치기 정렬 기준 변경 갱신 중지/재개 조작법
  111. 111. # facweb.py from flask import Flask app = Flask(__name__) @app.route('/<int:n>') def fac(n): r = 1 for x in range(2, n + 1): r *= x return str(r) app.run(port=8080) Py https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/facweb.py
  112. 112. 웹 서버 HTTP 클라이언트 프로파일링 뷰어
  113. 113. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/remote.gif
  114. 114. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/remote.gif
  115. 115. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/remote.gif
  116. 116. GIF https://github.com/sublee/pyconkr2015-profiling-resources/blob/master/remote.gif
  117. 117. $ python -m profiling COMMAND • profile • live-profile • remote-profile • view
  118. 118. what-studio/profiling
  119. 119. 마무리
  120. 120. profile cProfile Yappi Profiling gevent 호환     콜스택     성능    / 완성도     실시간     UX    
  121. 121. 2.2k • 3회
  122. 122. 함께 해요 Issues Pull requests @falsetru @dahlia @Parkayun @kxepal @ravipudi @lqez @ivuk @sickill @htch
  123. 123. 그밖에 다른 프로파일러들 • line_profiler • memory_profiler • pyinstrument • plop • pprofile
  124. 124. 그밖에 • <Profiling (computer programming)> Wikipedia • http://goo.gl/qeblxo • <What are Exclusive and Inclusive?> Steve Carroll • http://goo.gl/KCysud • <What are Exclusive and Inclusive?> Steve Carroll • http://goo.gl/KCysud • <Beginners Guide to Performance Profiling> MSDN • https://goo.gl/f9Oej9 • <Profiling tools> Alain Leufroy • https://goo.gl/YxcVWF
  125. 125. 함께 해요 #2
  126. 126. 질의응답
  127. 127. 실시간으로 프로파일링 할 때 갱신주기 내 함수 호출만 측정되나요? 네, 현재는 그렇습니다. 분명 갱신주기와 측정주기가 같아서 불편한 경우가 있는데요, Unity3D 프로파일러처럼 실시간 분석결과뿐 아니 라 과거의 분석결과도 탐색할 수 있게 해 해소할 계획입니다. Q. A.
  128. 128. 개발하는 데 얼마나 걸렸나요? 2014년 8월 한 달 동안 TracingProfiler와 뷰어, 서버를 만들 었고, 2015년 6월부터 천천히 두 달, 집중적으로 한 달 동안 SamplingProfiler를 만들었습니다. Q. A.
  129. 129. Python 3의 asyncio와 호환되나요? https://github.com/what-studio/profiling/issues/26 해당 이슈가 올라와 있지만, 아직 확인해보지 못했습니다. Q. A.
  130. 130. 분석결과에서 특정 함수를 필터링할 수 있나요? https://github.com/what-studio/profiling/issues/30 지금은 불가능하지만 마침 제안받은 게 있어 구현할 예정입니다. Q. A.
  131. 131. C 함수도 분석할 수 있나요? sys.setprofile의 콜백함수에 C 함수 호출과 반환 이벤트도 c_call, c_return이라는 이름으로 들어옵니다. 따라서 분석할 수 있겠으나 C 함수 실행에 대응하는 파이썬 frame이 없다 보니 다 소 번거로운 점이 있어 미뤄뒀습니다. 현재 TracingProfiler는 C 함수 이벤트를 무시합니다. 파이썬 frame만 조사할 수 있는 SamplingProfiler의 경우 C 함수는 분석하지 못할 것 같습니다. Q. A.
  132. 132. deferral 유틸리티를 어디엔 썼고 어디엔 안 썼던데 이유가 무엇인가요? 현장에선 Profiler 객체를 with 문에 넣기도 하고 안 넣기도 하 는 이유가 무엇이냐는 질문으로 오해하고, 그 기능이 처음부터 의도한 게 아니라 풀 리퀘스트로 받은 것이라 그런 것 같다고 잘 못 답변 드렸 습니다. deferral 유틸리티는 with 문이나 try-finally 문이 깊어지 는 게 싫어서 만들었습니다. 필요한 곳에 모두 쓸 것을 의도했으나 실 수로 빼먹은 것 같습니다. Q. A.
  133. 133. 프로파일러를 프로파일링할 수 있나요? TracingProfiler 자체의 성능을 분석하기 위해 썼던 방법입니 다. 우선 전달받은 호출/반환 이벤트를 모두 기록해 목록화하는 함수를 sys.setprofile에 등록한 후 예제코드를 실행합니다. 그렇게 모인 이벤트들에 대해 TracingProfiler._profile() 메소 드를 직접 호출하는 코드를 만들고 그걸 프로파일링했습니다. Q. A.
  134. 134. 사용한 서체 • KoPub돋움체 http://www.kopus.org/biz/electronic/font.aspx • DejaVu Sans Mono http://dejavu-fonts.org/ • Octicons https://octicons.github.com/ • Font Awesome http://fontawesome.io/
  135. 135. 감사합니다 이흥섭 • sub@nexon.co.kr

×