SlideShare a Scribd company logo
리눅스 게임 서버 성능 분석하기
김진욱 (jinuk.kim@ifunfactory.com)
2
성능 분석하기
성능 지표
3
처리량: 얼마나 많은 일을 하고 있는가
= 이 서버 동시 접속 몇 명까지 받을 수 있나?
응답 시간: 하나의 일을 얼마나 빨리 처리하는가
= 이 서버 랙 있는지?
게임 서비스
4
……게임 서버 (PvP) …
Game
Server
Game
Server
Matchmaking
Server
Matchmaking
Server
게임 서버 …
게임 서버 군
유저 세션 랭킹 캐시 …
Redis / memcached
유저 데이터 랭킹 통계 데이터
Databases
로드 밸런서
Nutcracker 캐시 / 디비 미들웨어
유저 인증
서비스
GooglePlay
결제
3rd 파티 플랫폼 서비스
BI / 통계
운영 API
운영 서비스
유저
랭킹
CDN
Matchmaking
Server
Matchmaking
Server매치메이킹
실시간 PVP 서버 군
게임 서비스
5
전체 시스템이 여러 개의 다양한 요소로 구성
각 요소 중에 성능 문제가 있는 부분을 찾을 수 있을까?
6
접근 방법
원하는 것: 성능 저하가 작을 것
7
• 가장 이상적인 테스트 환경: 실 서비스 환경
• Valgrind 처럼 수 배 - 수십배의 성능 저하는 쓸 수 없다
• 프로덕션 환경에서 크래시 할 수 있다면 사용하기 힘들다
원하는 것: 상시 모니터링
8
• 빨리 성능 문제를 감지할 수록 좋다
• 문제 생긴 환경에서 바로 확인할 수록 문제 해결이 쉽다.
원하는 것: 충분한 데이터
9
• 실행 속도가 평균 얼마 (X)
• 실행 속도의 분포가 이렇다 (O)
실행 시간의 분포 (백분율이나 유사한 통계) 가 있어야 파악하기 쉽다

(예: 평균 응답시간이 1ms 라도 1,000 ms 걸려서 처리하는 요청이 있다)
어떻게 측정할까?
10
서버 코드에 측정하는 기능을 추가
성능 모델의 각 요소를 측정
측정 코드를 (직접) 추가
외부에서 관찰
측정 도구 이용
(프로파일러, 성능 모니터링 툴)
11
접근 방법 1: 측정 코드 삽입
성능 측정 코드 삽입
12
게임 서버 실행 방법을 (단순화해서) 모델링
필요한 코드를 서버에 추가
측정 결과를 얻고 분석 및 코드 개선
간단한 성능 모델
13
성능 모델 (1)
14
요청 하나를 여러 개의 이벤트로 쪼개서 측정한다
• 웹 API의 요청 - 응답 쌍 하나
vs.
1. 유저 로그인 요청을 받고 해당하는 DB 객체 가져오기
2. DB객체와 로그인 요청을 비교해서 바른 요청인지 확인
3. 인증 서비스에 유저 로그인 요청 보내기
4. 인증 서비스 결과를 클라이언트에 응답
성능 모델 (2)
15
이벤트 하나
→ 클라이언트 메시지, 타이머 만료, DB 연산 완료, API 호출에 대한 응답
측정할 성능:
하나의 작업 (= 하나 혹은 여러 개의 이벤트) 가 얼마나 걸렸는가
각 이벤트 실행 시간을 작업 별로 모아서 합산
평균적/최소/최대로는 얼마나 빠른지 확인 (혹은 분포 확인)
성능 측정 코드 넣기
16
모든 분리된 이벤트 단위에서 측정한다.
• 클라이언트 메시지 받고 처리 시작까지 걸린 시간 (큐 대기 시간)
• DB 혹은 redis 요청 전송 / 요청 완료 콜백에 걸린 시간
• 외부 API 요청 / 완료 콜백까지 걸린 시간
• 데이터를 가지고 복잡한 연산 (예를 들어 길찾기, AI tick, …) 에 걸린 시간
• DB 에서 가져온 row 수 혹은 ORM 객체 수
• 외부 API 호출 실패에 대한 재시도 횟수
측정 예시
17
아이펀 엔진에서 측정하는 실행 통계
장점
18
개발자가 원하는 수치를 측정한다:
외부 API 성능이 궁금하다면 호출 시간 분포나, 호출 실패 분포를 측정
측정 범위를 제어하기 쉽다: 코드를 추가한 곳만 측정한다.
원하는 추상화 수준을 고를 수 있다: 시스템, 프로세스, 스레드, …
단점
19
측정 코드를 직접 추가 해야한다: 작업량 문제
빠진 부분이 있다면 측정 자체가 유의미하지 않을 수 있다
성능 모델을 직접 만들어야 한다
측정 방법/내용을 바꾸려면 다시 실행하거나 로드해야
20
접근 방법 2: 외부 도구 이용
리눅스 성능 측정 도구들
21
Linux perf kernel 이벤트 카운터 (v2.6 이상)
Linux eBPF 프레임웍의 트레이싱 기능 (v3.18 이상; 가능하면 v4.9 이상)
그리고 이를 이용한 스크립트 / 성능 모니터링 툴
• BCC (https://github.com/iovisor/bcc): eBPF 프로그램 컴파일러 (python, lua)
• PLY (https://github.com/wkz/ply): eBPF 스크립트 툴
• eBPF trace (https://github.com/iovisor/bpftrace): D-Trace 유사 기능
eBPF + BCC
22
eBPF: 리눅스 커널 안에서 동작하는 (제한된) VM
BCC: eBPF 프로그램을 생성하고,

해당 프로그램과 통신하는 python / lua 스크립트
성능 측정이 커널 안에서 이뤄져서 빠르다 (컨텍스트 스위칭 부하 없음)
eBPF: 할 수 있는 일
23
eBPF: 리눅스 커널 안에서 동작하는 (제한된) VM;
OS 실행 중 아무때나 추가하거나 제거할 수 있다.
커널 안의 함수, 시스템 콜, 프로그램 내의 함수를 후킹
해당 함수 호출할 때 마다 특정 동작(=호출 내용 통계내기)을 하거나,
동작을 변경할 수 있다. (=패킷 필터링)
BCC: 할 수 있는 일
24
eBPF 프로그램을 (자동으로) 생성하고
해당 프로그램을 커널에 넣어 실행 한 후, 통신한다.
시스템/프로그램의 동작을 지속적으로 추적하거나 (트레이싱)
성능 지표 등을 통계낼 수 있다. (프로파일링)
예: CPU 프로파일링
25
1초에 수십-수백번 정도 특정 프로세스나 스레드를 살펴본다
이때 각 CPU가 실행 중인 콜스택을 key로 map에 저장
Map에 있는 데이터를 FlameGraph 로 시각화

(https://github.com/brendangregg/FlameGraph )
예: CPU 프로파일링
26
어떤 질문을 답할 수 있을까?
27
어떤 lock 을 제일 오래 기다리는가?
Disk 읽기 요청을 어떤 크기로 주로 보내는가?
CPU 수가 작업 수에 비해서 적은가?
어느 시스템 콜을 제일 많이 하는가?
어떤 Disk IO가 제일 느린가?
Kernel - 유저 스페이스 역할 분리
28
데이터를 측정 대상의 kernel 영역 안에 안전하게 갈무리하고 (eBPF)
분석 프로그램의 유저 스페이스에서 후처리 (BCC)
성능 측정 / 디버깅 코드가 문제를 일으키기 어렵다
(측정 대상과 측정하는 툴이 서로 다른 프로그램)
장점
29
안전성 - OS나 측정 대상을 크래시하지 않는다 (eBPF 컴파일러가 보장)
시스템 / 프로그램 재시작 없이 측정 방식 변경
3rd 파티 라이브러리 / 프레임웍이 존재
단점
30
최신 커널 필요 - 일정 이상의 kernel 버전이 필요하다 (CentOS 7 은 커널 추가 설치 필요)
측정 대상이 사용하는 라이브러리 / OS 지식이 필요

(측정할 함수나 결과로 나온 함수들을 추측해야)
컴파일러 최적화로 인해서 변경된 콜스택 분석이 어려운 경우가 있다
31
예제 프로그램 분석
flaskr
32
Python 웹 개발 프레임워크인 flask 의 튜토리얼 프로그램
해당 프로그램을 C++ 로 포팅하고, 간단한 블로깅 서버 제작
(MySQL에 데이터 + 웹서버 및 HTML 생성 부분을 C++ 로 구현)
33
• HTTP 요청을 처리하는 몇
개의 핸들러
• HTML 템플릿 로드
• DB 연결
• 서버 시작 (4 스레드)
34
• HTTP 요청 핸들러
• DB 에서 글 목록을 획득
• key, value 로 묶기
• HTML 렌더링
35
• DB 미들웨어

(커넥션 풀링형식)
• 초기화
• 요청마다 DB 연결 제공
flaskr
36
다음과 같은 라이브러리 이용
Crow: C++14 기반의 고성능 HTTP 서버
CrowDB: C++14 기반의 DB API (MySQL / Mariadb 연결해서 사용)
Jinja2cpp: C++14 기반의 HTML 템플릿 렌더링 라이브러리
flaskr: 성능 목표
37
• 4 core 머신에서 초당 1000 요청 이상 처리하기
⇨ 코어마다 초당 250건 이상 처리해야
⇨ 요청 당 쓸 수 있는 CPU 시간 ≦ 4 ms
부하 테스트
38
• HTTP 부하 테스트: siege 이용
siege -c 32 -r 16384 -b http://example.com



지정한 URL에 32개의 연결로,

총 16k 개의 요청을,

요청 사이에 쉬는 것 없이 전송
부하 테스트: 결과
39
개선이 필요한 수치
• 초당 110요청
• CPU 코어 당 27.5 요청
• 요청 당 처리 시간 ≒ 36 ms
40
예제 프로그램: 직접 측정
flaskr: 직접 측정
41
웹 요청에 대한 비동기 처리 없음 - DB 및 기타 처리는 직렬로 처리
하나의 HTTP 요청에 대한 처리 시간을 구분해서 기록
• 총 처리 시간
• DB 요청 - 응답 시간
• HTML 응답 생성 시간 (template -> HTML 렌더링 시간)
측정 코드 삽입 (1)
42
HTTP Request 에 대한 middleware 추가
• 요청 처리 시작 / 종료 시각 기록 (총 요청 처리 시간 계산)
• DB 요청 시작 / 완료 시각 기록
• HTML 렌더링 시작 / 완료 시각 기록
만일, DB / HTML 렌더링 두 부분이 문제가 아니라면,
총 요청 시간 - DB 요청 시간 - HTML 렌더링 시간
값이 크게 나올 것이다. (예측)
측정 코드 삽입 (2)
43
각 측정 값 (총, DB, 렌더링)에 대해서 처리 시간 분포를 측정 (log 분포 이용)
시간은 마이크로 초 (us) 수준에서 측정
측정 결과
44
16 - 32
32 - 64
64 - 128
128 - 256
256 - 512
512 - 1024
1024 - 2048
0 750 1500 2250 3000
응답시간 (ms)
측정 결과: DB 처리
45
32 - 64
64 - 128
128 - 256
256 - 512
0 1000 2000 3000 4000
DB 처리 시간 (us)
측정 결과: HTML 렌더링
46
32 - 64
64 - 128
128 - 256
256 - 512
0 1000 2000 3000 4000
페이지 렌더링 시간 (us)
측정 결과
47
• 요청 당 소요 시간인 36 ms (= 36,000 us) 에 가까운 값이 없다.
• 잠정 결론: DB, HTML 렌더링은 병목이 아니다. (전체 시간 중 일부)
• 추가 분석을 미뤄두고, 다른 방법으로 접근 시도.
48
예제 프로그램: 툴을 써서 측정
flaskr: eBPF + BCC를 써서 측정
49
BCC 에서는 예제 및 기본 기능 제공을 위해서 몇 가지 도구를 제공한다.
해당 도구를 사용해서 성능 측정 / 병목 분석을 진행한다.
측정 진행 방식
50
1. 가설을 세운다
2. 가설을 확인할 적당한 도구로 측정한다 (혹은 도구를 만든다)
3. 측정 후 가설이 맞는지 확인한다

예를 들어 CPU가 병목이라고 가정한다면,

profile 을 실행해서 CPU 병목을 찾아보고,

CPU 병목이 있다면 많이 실행되는 곳을 최적화한다
가설 #1: CPU 병목이 있는가?
51
CPU 를 많이 쓰는 코드를 어떻게 찾을까?
BCC profile 명령 이용
앞서 언급한 샘플링 방식의 프로파일링 도구
예: 30초간, 매초 199번 샘플링
sudo profile -p $(pgrep -nx flaskr) -f -F 199 30
CPU 프로파일링 결과
52
CPU 병목이 있는가?
53
전체 프로파일링 시간 중 3% 이상 사용하는 곳:
• System call (open, read, TCP send, …)
• DNS 설정 읽기 (libnss)
CPU 병목이 있는가?
54
htop 으로 확인



CPU를 다 쓰고 있지 않다.
(코어 1.4개 = 140% 수준)
가설 #2: 어딘가에서 대기하는 문제?
55
Lock 이나 메모리 같은 다른 무언가에서 대기하면 오래 걸린다
코드의 대기 시간을 측정해보자: BCC offcputime
스레드가 락을 기다리거나 / 블럭킹 시스템 콜을 해서

CPU를 반납하는 순간의 콜 스택 분포를 구한다
sudo offcputime -p $(pgrep -nx flaskr) -f 30
대기하는 코드는 어디인가?
56
poll() 에서 3/4 이상의 대기가 발생
epoll() 에서 나머지가 대기
poll() 은 누가 부르는가?
57
프로파일링 결과의 콜 스택에서 찾아보면?
누가 poll() 대기를 깨우는가?
58
poll() 호출한 코드가 무엇을 기다렸는지 반대방향에서 보기
깨우는 쪽에서 뭘 했는지 확인한다
offwaketime 툴 이용

다른 스레드를 실행 가능하게 해줬을 때의 콜 스택 분포를 본다
sudo offwaketime -p $(pgrep -nx flaskr) 30
Waker stack 확인
59
UDP 메시지를 받은 경우 깨운다
UDP 문제 확인
60
웹 서버 어떤 부분에 UDP를 쓰는가?
• Google QUIC (HTTP/2 의 비호환 이전 버전)
• RTP
• DNS
• …
DNS 호스트 주소 조회를 위해 사용하고 있다 (미들웨어)
가설 #3: DNS 조회 시간 문제?
61
DNS 조회 시간을 어떻게 확인하는가?
특정 함수를 후킹해서, 함수 호출/종료 사이의 시간을 잰다.
BCC funclatency: DNS 주소를 조회하는 C API 를 측정한다
DNS 응답 시간: getnameinfo
62
16 - 32
32 - 64
64 - 128
128 - 256
0 1000 2000 3000 4000
응답 시간 (ms)
DNS 응답시간: 16 - 128 ms
요청 당 소요 시간 36 ms 과 비슷함
재확인: 직접 측정
63
16 - 32
32 - 64
64 - 128
128 - 256
0 750 1500 2250 3000
응답 시간 (ms)
DNS resolver 시간을 앞서 사용한

코드 수준의 측정 방식으로 확인
DNS 문제 수정
64
DNS 처리가 대기하는 호출이 되지 않도록 수정한다.
• Caching DNS 서버를 사용한다 (dnsmasq)
• 비동기 DNS resolver 를 이용한다. (boost::asio 의 async_resolve)
수정 후 실행 결과
65
• 4 core 머신에서 초당 1400 요청 처리
⇨ 코어마다 초당 350건 이상 (vs. 27.5건)
⇨ 요청 당 소요 시간 ≦ 3 ms (vs. 36 ms)
수정 후 실행 결과: 대기 시간
66
• 이전 부하 테스트에선 poll() 이 전체 대기시간의 3/4 이상
해석
67
• poll() 함수 실행 시간 개선 (대기 시간 개선)
• 요청 하나를 처리하는 응답 시간 감소
⇨ 하나의 CPU 코어가 처리하는 초당 요청 수 증가
68
정리
어떻게 접근할까?
69
서버 코드에 측정하는 기능을 추가
모델에 대해서 한 번에 측정/확인
모델 범위를 벗어나는 경우 문제
외부에서 관찰
도구가 제공하는 범위 내에서 측정
한 번에 모두 볼 수 없으나 반복/수정
더 해보고 싶은 것: 클라이언트
70
• 직접 측정: Unity 나 Unreal Engine 의 자체 프로파일러
• 도구 사용: Android adeb (https://github.com/joelagnel/adeb)

adb root 사용 가능한 arm64 / android N 이상의 기기 지원

(사실상 커스텀 빌드 커널 필요)
더 해보고 싶은 것: Windows 서버
71
• 직접 측정: Linux 의 경우와 차이가 크지 않다
• 도구 사용
• Event Tracing for Windows / Xperf

(https://randomascii.wordpress.com/2012/05/11/the-lost-xperf-
documentationcpu-scheduling/ )
• Windows Performance Analyzer (WPA)

(https://docs.microsoft.com/ko-kr/windows-hardware/test/wpt/
graphs#flame_graphs)
Q&A
72
감사합니다
73
리눅스 게임 서버 성능 분석하기
김진욱 (jinuk.kim@ifunfactory.com)

More Related Content

PDF
게임 서버 성능 분석하기
PDF
KGC 2014 프로파일러를 이용한 게임 클라이언트 최적화
PDF
임태현, 게임 서버 디자인 가이드, NDC2013
PDF
서버학개론(백엔드 서버 개발자를 위한)
PPTX
금융It시스템의 이해 1편 202201
PDF
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
PDF
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
PDF
NDC12_Lockless게임서버설계와구현
게임 서버 성능 분석하기
KGC 2014 프로파일러를 이용한 게임 클라이언트 최적화
임태현, 게임 서버 디자인 가이드, NDC2013
서버학개론(백엔드 서버 개발자를 위한)
금융It시스템의 이해 1편 202201
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
이승재, 사례로 배우는 디스어셈블리 디버깅, NDC2014
NDC12_Lockless게임서버설계와구현

What's hot (20)

PPTX
그럴듯한 랜덤 생성 컨텐츠 만들기
PPTX
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
PPTX
NDC 11 자이언트 서버의 비밀
PDF
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
PDF
[236] 카카오의데이터파이프라인 윤도영
PDF
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
PDF
[NDC2017] 딥러닝으로 게임 콘텐츠 제작하기 - VAE를 이용한 콘텐츠 생성 기법 연구 사례
PDF
행동 트리
PPTX
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
PDF
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
PDF
중앙 서버 없는 게임 로직
PDF
Multiplayer Game Sync Techniques through CAP theorem
PPTX
금융It시스템의 이해 2편
PPTX
AAA게임_UI_최적화_및_빌드하기.pptx
PPTX
GameInstance에 대해서 알아보자
PPTX
웹 프로그래밍 팀프로젝트 최종발표
PDF
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
PDF
MMOG Server-Side 충돌 및 이동처리 설계와 구현
PDF
Measuring Real User Performance in the Browser
PPTX
KGC 2016: HTTPS 로 모바일 게임 서버 구축한다는 것 - Korea Games Conference
그럴듯한 랜덤 생성 컨텐츠 만들기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
NDC 11 자이언트 서버의 비밀
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[236] 카카오의데이터파이프라인 윤도영
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[NDC2017] 딥러닝으로 게임 콘텐츠 제작하기 - VAE를 이용한 콘텐츠 생성 기법 연구 사례
행동 트리
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
중앙 서버 없는 게임 로직
Multiplayer Game Sync Techniques through CAP theorem
금융It시스템의 이해 2편
AAA게임_UI_최적화_및_빌드하기.pptx
GameInstance에 대해서 알아보자
웹 프로그래밍 팀프로젝트 최종발표
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
MMOG Server-Side 충돌 및 이동처리 설계와 구현
Measuring Real User Performance in the Browser
KGC 2016: HTTPS 로 모바일 게임 서버 구축한다는 것 - Korea Games Conference
Ad

Similar to [MGDC] 리눅스 게임 서버 성능 분석하기 - 아이펀팩토리 김진욱 CTO (20)

PDF
실전 서버 부하테스트 노하우
PDF
웹서버 부하테스트 실전 노하우
PDF
클라우드 환경에서 알아야할 성능 이야기
PDF
실무로 배우는 시스템 성능 최적화 8부 - 1,2,3장
PDF
Profiling - 실시간 대화식 프로파일러
PDF
[오픈소스컨설팅]파일럿진행예제 on AWS
PDF
NDC14 모바일 게임서비스를 위한 사설 클라우드 구축/운영 분투기
PDF
[232] 성능어디까지쥐어짜봤니 송태웅
PPTX
Performance Testing using Loadrunner
PDF
클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
PDF
Linux Performan tuning Part I
PDF
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
PPTX
practical perf testing - d2startup
PDF
서버 성능에 대한 정의와 이해
PPTX
오픈 소스 도구를 활용한 성능 테스트 방법 및 사례
PDF
[오픈소스컨설팅]Performance Tuning How To
PDF
스마트폰 앱 백-엔드 솔루션 개발을 위한 Node.js 실전 가이드
PDF
Play node conference
PDF
애플리케이션 개발 단계에서의 성능 품질과 생산성 효율, 둘 다 잡기
PDF
Custom DevOps Monitoring System in MelOn (with InfluxDB + Telegraf + Grafana)
실전 서버 부하테스트 노하우
웹서버 부하테스트 실전 노하우
클라우드 환경에서 알아야할 성능 이야기
실무로 배우는 시스템 성능 최적화 8부 - 1,2,3장
Profiling - 실시간 대화식 프로파일러
[오픈소스컨설팅]파일럿진행예제 on AWS
NDC14 모바일 게임서비스를 위한 사설 클라우드 구축/운영 분투기
[232] 성능어디까지쥐어짜봤니 송태웅
Performance Testing using Loadrunner
클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
Linux Performan tuning Part I
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
practical perf testing - d2startup
서버 성능에 대한 정의와 이해
오픈 소스 도구를 활용한 성능 테스트 방법 및 사례
[오픈소스컨설팅]Performance Tuning How To
스마트폰 앱 백-엔드 솔루션 개발을 위한 Node.js 실전 가이드
Play node conference
애플리케이션 개발 단계에서의 성능 품질과 생산성 효율, 둘 다 잡기
Custom DevOps Monitoring System in MelOn (with InfluxDB + Telegraf + Grafana)
Ad

More from iFunFactory Inc. (20)

PDF
2019 아이펀팩토리 Dev Day 세션6 아이펀엔진 운영툴 연동하기 - 장수원
PDF
2019 아이펀팩토리 Dev Day 세션5 아이펀엔진으로 만든 게임 성능 분석 및 디버깅 - 남승현
PDF
2019 아이펀팩토리 Dev Day 세션4 아이펀엔진에 MO 게임 콘텐츠 채워 넣기 - 남승현
PDF
2019 아이펀팩토리 Dev Day 세션3 아이펀엔진 개발 환경 설정하기 (Windows+ VS) - 김진욱
PDF
2019 아이펀팩토리 Dev Day 세션2 아이펀엔진 개발 환경 설정하기 (Linux + VS Code) - 김진욱
PDF
2019 아이펀팩토리 Dev Day 세션1 네트워크 프로그래밍 개론 - 문대경 대표
PDF
[아이펀팩토리] 2018 데브데이 서버위더스 _03 Scalable 한 게임 서버 만들기
PDF
[아이펀팩토리] 2018 데브데이 서버위더스 _01 HTML5/WebSocket으로 Pong 게임 만들기
PDF
[아이펀팩토리] 2018 데브데이 서버위더스 _02 분산 환경을 위한 ORM 개발 경험 공유
PDF
[아이펀팩토리] 2018 데브데이 서버위더스 _04 리눅스 게임 서버 성능 분석
PDF
[아이펀팩토리] 클라이언트 개발자, 서버 개발 시작하기
PDF
[아이펀팩토리] 2017 NDCP
PPT
[아이펀팩토리]2017 NDC 강연 자료_아이펀 엔진 개발 노트
PDF
게임서버 구축 방법비교 : GBaaS vs. Self-hosting
PDF
유니티 쉐이더 단기속성
PDF
혼자서 만드는 MMO게임 서버
PDF
Python과 AWS를 이용하여 게임 테스트 환경 구축하기
PPTX
PC 와 모바일에서의 P2P 게임 구현에서의 차이점 비교
PPTX
Docker 로 Linux 없이 Linux 환경에서 개발하기
PPTX
게임 운영에 필요한 로그성 데이터들에 대하여
2019 아이펀팩토리 Dev Day 세션6 아이펀엔진 운영툴 연동하기 - 장수원
2019 아이펀팩토리 Dev Day 세션5 아이펀엔진으로 만든 게임 성능 분석 및 디버깅 - 남승현
2019 아이펀팩토리 Dev Day 세션4 아이펀엔진에 MO 게임 콘텐츠 채워 넣기 - 남승현
2019 아이펀팩토리 Dev Day 세션3 아이펀엔진 개발 환경 설정하기 (Windows+ VS) - 김진욱
2019 아이펀팩토리 Dev Day 세션2 아이펀엔진 개발 환경 설정하기 (Linux + VS Code) - 김진욱
2019 아이펀팩토리 Dev Day 세션1 네트워크 프로그래밍 개론 - 문대경 대표
[아이펀팩토리] 2018 데브데이 서버위더스 _03 Scalable 한 게임 서버 만들기
[아이펀팩토리] 2018 데브데이 서버위더스 _01 HTML5/WebSocket으로 Pong 게임 만들기
[아이펀팩토리] 2018 데브데이 서버위더스 _02 분산 환경을 위한 ORM 개발 경험 공유
[아이펀팩토리] 2018 데브데이 서버위더스 _04 리눅스 게임 서버 성능 분석
[아이펀팩토리] 클라이언트 개발자, 서버 개발 시작하기
[아이펀팩토리] 2017 NDCP
[아이펀팩토리]2017 NDC 강연 자료_아이펀 엔진 개발 노트
게임서버 구축 방법비교 : GBaaS vs. Self-hosting
유니티 쉐이더 단기속성
혼자서 만드는 MMO게임 서버
Python과 AWS를 이용하여 게임 테스트 환경 구축하기
PC 와 모바일에서의 P2P 게임 구현에서의 차이점 비교
Docker 로 Linux 없이 Linux 환경에서 개발하기
게임 운영에 필요한 로그성 데이터들에 대하여

[MGDC] 리눅스 게임 서버 성능 분석하기 - 아이펀팩토리 김진욱 CTO

  • 1. 리눅스 게임 서버 성능 분석하기 김진욱 (jinuk.kim@ifunfactory.com)
  • 3. 성능 지표 3 처리량: 얼마나 많은 일을 하고 있는가 = 이 서버 동시 접속 몇 명까지 받을 수 있나? 응답 시간: 하나의 일을 얼마나 빨리 처리하는가 = 이 서버 랙 있는지?
  • 4. 게임 서비스 4 ……게임 서버 (PvP) … Game Server Game Server Matchmaking Server Matchmaking Server 게임 서버 … 게임 서버 군 유저 세션 랭킹 캐시 … Redis / memcached 유저 데이터 랭킹 통계 데이터 Databases 로드 밸런서 Nutcracker 캐시 / 디비 미들웨어 유저 인증 서비스 GooglePlay 결제 3rd 파티 플랫폼 서비스 BI / 통계 운영 API 운영 서비스 유저 랭킹 CDN Matchmaking Server Matchmaking Server매치메이킹 실시간 PVP 서버 군
  • 5. 게임 서비스 5 전체 시스템이 여러 개의 다양한 요소로 구성 각 요소 중에 성능 문제가 있는 부분을 찾을 수 있을까?
  • 7. 원하는 것: 성능 저하가 작을 것 7 • 가장 이상적인 테스트 환경: 실 서비스 환경 • Valgrind 처럼 수 배 - 수십배의 성능 저하는 쓸 수 없다 • 프로덕션 환경에서 크래시 할 수 있다면 사용하기 힘들다
  • 8. 원하는 것: 상시 모니터링 8 • 빨리 성능 문제를 감지할 수록 좋다 • 문제 생긴 환경에서 바로 확인할 수록 문제 해결이 쉽다.
  • 9. 원하는 것: 충분한 데이터 9 • 실행 속도가 평균 얼마 (X) • 실행 속도의 분포가 이렇다 (O) 실행 시간의 분포 (백분율이나 유사한 통계) 가 있어야 파악하기 쉽다
 (예: 평균 응답시간이 1ms 라도 1,000 ms 걸려서 처리하는 요청이 있다)
  • 10. 어떻게 측정할까? 10 서버 코드에 측정하는 기능을 추가 성능 모델의 각 요소를 측정 측정 코드를 (직접) 추가 외부에서 관찰 측정 도구 이용 (프로파일러, 성능 모니터링 툴)
  • 11. 11 접근 방법 1: 측정 코드 삽입
  • 12. 성능 측정 코드 삽입 12 게임 서버 실행 방법을 (단순화해서) 모델링 필요한 코드를 서버에 추가 측정 결과를 얻고 분석 및 코드 개선
  • 14. 성능 모델 (1) 14 요청 하나를 여러 개의 이벤트로 쪼개서 측정한다 • 웹 API의 요청 - 응답 쌍 하나 vs. 1. 유저 로그인 요청을 받고 해당하는 DB 객체 가져오기 2. DB객체와 로그인 요청을 비교해서 바른 요청인지 확인 3. 인증 서비스에 유저 로그인 요청 보내기 4. 인증 서비스 결과를 클라이언트에 응답
  • 15. 성능 모델 (2) 15 이벤트 하나 → 클라이언트 메시지, 타이머 만료, DB 연산 완료, API 호출에 대한 응답 측정할 성능: 하나의 작업 (= 하나 혹은 여러 개의 이벤트) 가 얼마나 걸렸는가 각 이벤트 실행 시간을 작업 별로 모아서 합산 평균적/최소/최대로는 얼마나 빠른지 확인 (혹은 분포 확인)
  • 16. 성능 측정 코드 넣기 16 모든 분리된 이벤트 단위에서 측정한다. • 클라이언트 메시지 받고 처리 시작까지 걸린 시간 (큐 대기 시간) • DB 혹은 redis 요청 전송 / 요청 완료 콜백에 걸린 시간 • 외부 API 요청 / 완료 콜백까지 걸린 시간 • 데이터를 가지고 복잡한 연산 (예를 들어 길찾기, AI tick, …) 에 걸린 시간 • DB 에서 가져온 row 수 혹은 ORM 객체 수 • 외부 API 호출 실패에 대한 재시도 횟수
  • 17. 측정 예시 17 아이펀 엔진에서 측정하는 실행 통계
  • 18. 장점 18 개발자가 원하는 수치를 측정한다: 외부 API 성능이 궁금하다면 호출 시간 분포나, 호출 실패 분포를 측정 측정 범위를 제어하기 쉽다: 코드를 추가한 곳만 측정한다. 원하는 추상화 수준을 고를 수 있다: 시스템, 프로세스, 스레드, …
  • 19. 단점 19 측정 코드를 직접 추가 해야한다: 작업량 문제 빠진 부분이 있다면 측정 자체가 유의미하지 않을 수 있다 성능 모델을 직접 만들어야 한다 측정 방법/내용을 바꾸려면 다시 실행하거나 로드해야
  • 20. 20 접근 방법 2: 외부 도구 이용
  • 21. 리눅스 성능 측정 도구들 21 Linux perf kernel 이벤트 카운터 (v2.6 이상) Linux eBPF 프레임웍의 트레이싱 기능 (v3.18 이상; 가능하면 v4.9 이상) 그리고 이를 이용한 스크립트 / 성능 모니터링 툴 • BCC (https://github.com/iovisor/bcc): eBPF 프로그램 컴파일러 (python, lua) • PLY (https://github.com/wkz/ply): eBPF 스크립트 툴 • eBPF trace (https://github.com/iovisor/bpftrace): D-Trace 유사 기능
  • 22. eBPF + BCC 22 eBPF: 리눅스 커널 안에서 동작하는 (제한된) VM BCC: eBPF 프로그램을 생성하고,
 해당 프로그램과 통신하는 python / lua 스크립트 성능 측정이 커널 안에서 이뤄져서 빠르다 (컨텍스트 스위칭 부하 없음)
  • 23. eBPF: 할 수 있는 일 23 eBPF: 리눅스 커널 안에서 동작하는 (제한된) VM; OS 실행 중 아무때나 추가하거나 제거할 수 있다. 커널 안의 함수, 시스템 콜, 프로그램 내의 함수를 후킹 해당 함수 호출할 때 마다 특정 동작(=호출 내용 통계내기)을 하거나, 동작을 변경할 수 있다. (=패킷 필터링)
  • 24. BCC: 할 수 있는 일 24 eBPF 프로그램을 (자동으로) 생성하고 해당 프로그램을 커널에 넣어 실행 한 후, 통신한다. 시스템/프로그램의 동작을 지속적으로 추적하거나 (트레이싱) 성능 지표 등을 통계낼 수 있다. (프로파일링)
  • 25. 예: CPU 프로파일링 25 1초에 수십-수백번 정도 특정 프로세스나 스레드를 살펴본다 이때 각 CPU가 실행 중인 콜스택을 key로 map에 저장 Map에 있는 데이터를 FlameGraph 로 시각화
 (https://github.com/brendangregg/FlameGraph )
  • 27. 어떤 질문을 답할 수 있을까? 27 어떤 lock 을 제일 오래 기다리는가? Disk 읽기 요청을 어떤 크기로 주로 보내는가? CPU 수가 작업 수에 비해서 적은가? 어느 시스템 콜을 제일 많이 하는가? 어떤 Disk IO가 제일 느린가?
  • 28. Kernel - 유저 스페이스 역할 분리 28 데이터를 측정 대상의 kernel 영역 안에 안전하게 갈무리하고 (eBPF) 분석 프로그램의 유저 스페이스에서 후처리 (BCC) 성능 측정 / 디버깅 코드가 문제를 일으키기 어렵다 (측정 대상과 측정하는 툴이 서로 다른 프로그램)
  • 29. 장점 29 안전성 - OS나 측정 대상을 크래시하지 않는다 (eBPF 컴파일러가 보장) 시스템 / 프로그램 재시작 없이 측정 방식 변경 3rd 파티 라이브러리 / 프레임웍이 존재
  • 30. 단점 30 최신 커널 필요 - 일정 이상의 kernel 버전이 필요하다 (CentOS 7 은 커널 추가 설치 필요) 측정 대상이 사용하는 라이브러리 / OS 지식이 필요
 (측정할 함수나 결과로 나온 함수들을 추측해야) 컴파일러 최적화로 인해서 변경된 콜스택 분석이 어려운 경우가 있다
  • 32. flaskr 32 Python 웹 개발 프레임워크인 flask 의 튜토리얼 프로그램 해당 프로그램을 C++ 로 포팅하고, 간단한 블로깅 서버 제작 (MySQL에 데이터 + 웹서버 및 HTML 생성 부분을 C++ 로 구현)
  • 33. 33 • HTTP 요청을 처리하는 몇 개의 핸들러 • HTML 템플릿 로드 • DB 연결 • 서버 시작 (4 스레드)
  • 34. 34 • HTTP 요청 핸들러 • DB 에서 글 목록을 획득 • key, value 로 묶기 • HTML 렌더링
  • 35. 35 • DB 미들웨어
 (커넥션 풀링형식) • 초기화 • 요청마다 DB 연결 제공
  • 36. flaskr 36 다음과 같은 라이브러리 이용 Crow: C++14 기반의 고성능 HTTP 서버 CrowDB: C++14 기반의 DB API (MySQL / Mariadb 연결해서 사용) Jinja2cpp: C++14 기반의 HTML 템플릿 렌더링 라이브러리
  • 37. flaskr: 성능 목표 37 • 4 core 머신에서 초당 1000 요청 이상 처리하기 ⇨ 코어마다 초당 250건 이상 처리해야 ⇨ 요청 당 쓸 수 있는 CPU 시간 ≦ 4 ms
  • 38. 부하 테스트 38 • HTTP 부하 테스트: siege 이용 siege -c 32 -r 16384 -b http://example.com
 
 지정한 URL에 32개의 연결로,
 총 16k 개의 요청을,
 요청 사이에 쉬는 것 없이 전송
  • 39. 부하 테스트: 결과 39 개선이 필요한 수치 • 초당 110요청 • CPU 코어 당 27.5 요청 • 요청 당 처리 시간 ≒ 36 ms
  • 41. flaskr: 직접 측정 41 웹 요청에 대한 비동기 처리 없음 - DB 및 기타 처리는 직렬로 처리 하나의 HTTP 요청에 대한 처리 시간을 구분해서 기록 • 총 처리 시간 • DB 요청 - 응답 시간 • HTML 응답 생성 시간 (template -> HTML 렌더링 시간)
  • 42. 측정 코드 삽입 (1) 42 HTTP Request 에 대한 middleware 추가 • 요청 처리 시작 / 종료 시각 기록 (총 요청 처리 시간 계산) • DB 요청 시작 / 완료 시각 기록 • HTML 렌더링 시작 / 완료 시각 기록 만일, DB / HTML 렌더링 두 부분이 문제가 아니라면, 총 요청 시간 - DB 요청 시간 - HTML 렌더링 시간 값이 크게 나올 것이다. (예측)
  • 43. 측정 코드 삽입 (2) 43 각 측정 값 (총, DB, 렌더링)에 대해서 처리 시간 분포를 측정 (log 분포 이용) 시간은 마이크로 초 (us) 수준에서 측정
  • 44. 측정 결과 44 16 - 32 32 - 64 64 - 128 128 - 256 256 - 512 512 - 1024 1024 - 2048 0 750 1500 2250 3000 응답시간 (ms)
  • 45. 측정 결과: DB 처리 45 32 - 64 64 - 128 128 - 256 256 - 512 0 1000 2000 3000 4000 DB 처리 시간 (us)
  • 46. 측정 결과: HTML 렌더링 46 32 - 64 64 - 128 128 - 256 256 - 512 0 1000 2000 3000 4000 페이지 렌더링 시간 (us)
  • 47. 측정 결과 47 • 요청 당 소요 시간인 36 ms (= 36,000 us) 에 가까운 값이 없다. • 잠정 결론: DB, HTML 렌더링은 병목이 아니다. (전체 시간 중 일부) • 추가 분석을 미뤄두고, 다른 방법으로 접근 시도.
  • 49. flaskr: eBPF + BCC를 써서 측정 49 BCC 에서는 예제 및 기본 기능 제공을 위해서 몇 가지 도구를 제공한다. 해당 도구를 사용해서 성능 측정 / 병목 분석을 진행한다.
  • 50. 측정 진행 방식 50 1. 가설을 세운다 2. 가설을 확인할 적당한 도구로 측정한다 (혹은 도구를 만든다) 3. 측정 후 가설이 맞는지 확인한다
 예를 들어 CPU가 병목이라고 가정한다면,
 profile 을 실행해서 CPU 병목을 찾아보고,
 CPU 병목이 있다면 많이 실행되는 곳을 최적화한다
  • 51. 가설 #1: CPU 병목이 있는가? 51 CPU 를 많이 쓰는 코드를 어떻게 찾을까? BCC profile 명령 이용 앞서 언급한 샘플링 방식의 프로파일링 도구 예: 30초간, 매초 199번 샘플링 sudo profile -p $(pgrep -nx flaskr) -f -F 199 30
  • 53. CPU 병목이 있는가? 53 전체 프로파일링 시간 중 3% 이상 사용하는 곳: • System call (open, read, TCP send, …) • DNS 설정 읽기 (libnss)
  • 54. CPU 병목이 있는가? 54 htop 으로 확인
 
 CPU를 다 쓰고 있지 않다. (코어 1.4개 = 140% 수준)
  • 55. 가설 #2: 어딘가에서 대기하는 문제? 55 Lock 이나 메모리 같은 다른 무언가에서 대기하면 오래 걸린다 코드의 대기 시간을 측정해보자: BCC offcputime 스레드가 락을 기다리거나 / 블럭킹 시스템 콜을 해서
 CPU를 반납하는 순간의 콜 스택 분포를 구한다 sudo offcputime -p $(pgrep -nx flaskr) -f 30
  • 56. 대기하는 코드는 어디인가? 56 poll() 에서 3/4 이상의 대기가 발생 epoll() 에서 나머지가 대기
  • 57. poll() 은 누가 부르는가? 57 프로파일링 결과의 콜 스택에서 찾아보면?
  • 58. 누가 poll() 대기를 깨우는가? 58 poll() 호출한 코드가 무엇을 기다렸는지 반대방향에서 보기 깨우는 쪽에서 뭘 했는지 확인한다 offwaketime 툴 이용
 다른 스레드를 실행 가능하게 해줬을 때의 콜 스택 분포를 본다 sudo offwaketime -p $(pgrep -nx flaskr) 30
  • 59. Waker stack 확인 59 UDP 메시지를 받은 경우 깨운다
  • 60. UDP 문제 확인 60 웹 서버 어떤 부분에 UDP를 쓰는가? • Google QUIC (HTTP/2 의 비호환 이전 버전) • RTP • DNS • … DNS 호스트 주소 조회를 위해 사용하고 있다 (미들웨어)
  • 61. 가설 #3: DNS 조회 시간 문제? 61 DNS 조회 시간을 어떻게 확인하는가? 특정 함수를 후킹해서, 함수 호출/종료 사이의 시간을 잰다. BCC funclatency: DNS 주소를 조회하는 C API 를 측정한다
  • 62. DNS 응답 시간: getnameinfo 62 16 - 32 32 - 64 64 - 128 128 - 256 0 1000 2000 3000 4000 응답 시간 (ms) DNS 응답시간: 16 - 128 ms 요청 당 소요 시간 36 ms 과 비슷함
  • 63. 재확인: 직접 측정 63 16 - 32 32 - 64 64 - 128 128 - 256 0 750 1500 2250 3000 응답 시간 (ms) DNS resolver 시간을 앞서 사용한
 코드 수준의 측정 방식으로 확인
  • 64. DNS 문제 수정 64 DNS 처리가 대기하는 호출이 되지 않도록 수정한다. • Caching DNS 서버를 사용한다 (dnsmasq) • 비동기 DNS resolver 를 이용한다. (boost::asio 의 async_resolve)
  • 65. 수정 후 실행 결과 65 • 4 core 머신에서 초당 1400 요청 처리 ⇨ 코어마다 초당 350건 이상 (vs. 27.5건) ⇨ 요청 당 소요 시간 ≦ 3 ms (vs. 36 ms)
  • 66. 수정 후 실행 결과: 대기 시간 66 • 이전 부하 테스트에선 poll() 이 전체 대기시간의 3/4 이상
  • 67. 해석 67 • poll() 함수 실행 시간 개선 (대기 시간 개선) • 요청 하나를 처리하는 응답 시간 감소 ⇨ 하나의 CPU 코어가 처리하는 초당 요청 수 증가
  • 69. 어떻게 접근할까? 69 서버 코드에 측정하는 기능을 추가 모델에 대해서 한 번에 측정/확인 모델 범위를 벗어나는 경우 문제 외부에서 관찰 도구가 제공하는 범위 내에서 측정 한 번에 모두 볼 수 없으나 반복/수정
  • 70. 더 해보고 싶은 것: 클라이언트 70 • 직접 측정: Unity 나 Unreal Engine 의 자체 프로파일러 • 도구 사용: Android adeb (https://github.com/joelagnel/adeb)
 adb root 사용 가능한 arm64 / android N 이상의 기기 지원
 (사실상 커스텀 빌드 커널 필요)
  • 71. 더 해보고 싶은 것: Windows 서버 71 • 직접 측정: Linux 의 경우와 차이가 크지 않다 • 도구 사용 • Event Tracing for Windows / Xperf
 (https://randomascii.wordpress.com/2012/05/11/the-lost-xperf- documentationcpu-scheduling/ ) • Windows Performance Analyzer (WPA)
 (https://docs.microsoft.com/ko-kr/windows-hardware/test/wpt/ graphs#flame_graphs)
  • 74. 리눅스 게임 서버 성능 분석하기 김진욱 (jinuk.kim@ifunfactory.com)