Study/TIL(Today I Learned)

24.10.11 게임 서버

에린_1 2024. 10. 11. 23:44
728x90

게임서버

실시간 게임 최적화 전략

  • 수동적인 서버
    • 결과만 저장해주는 서버
    • 패킷 중계
  • 능동적인 서버 - 일반적인 게임서버
    • 생태를 서버의 메모리에 저장하고 있다.
    • DB는 보조 수단이다.
    • 클라이언트의 시간 차 0을 목표로 한다.(불가능 하지만)
    • 게임 루프가 있다.
  • 서버가 하는 일 in Voxel Horizon
    • 유저로부터의 패킷에 의한 이벤트처리
    • 플레이어 or NPC에 대한 이동처리

처리량과 응답성의 차이

  • 어느 부분이 문제인지 생각할 필요가 있다.

응답성 향상을 위한 주요 기법

  • 비동기 처리
    • 특정 작업 때문에 전체 응답성이 떨어지는 상황을 방지
    • 멀티 스레드 - 별도의 worker thread가 처리
    • 싱글 스레드 - 작업을 쪼개서 원래의 작업 스레드에서 여러번에 걸쳐서 처리
  • thread pool을 이용한 병렬처리
    • 서로 의존성을 가지지 않는 n개의 항목에 대해 M개의 worker thread가 병렬로 처리한다.
    • thread pool이 작업을 완료 할 때 까지 원래의 작업 스레드는 대기한다.
  • 코드 최적화 - SIMD 등
  • 서로 조합해서 사용 가능하다.

비동기 처리

  • 로그인/로그아웃
  • 급하지 않은 작업
  • 특정 작업 때문에 전체 응답성이 떨어지는 상황을 방지한다.
  • DB 종속적인 작업들
  • 저장소에 대한 I/O

비동기 처리 - 싱글 스레드

  • 별도의 worker thread를 생성하지 않는다.
  • 긴 시간이 걸리는 작업을 쪼개서 메인 스레드의 타임 라인에 분산해서 처리한다.
  • 시간 전체 처리 소요 시간은 그대로, 처리까지 걸리는 시간은 상승, 응답성은 상승
  • 스레드 간 동기화가 복잡해질 경우 선택한다.
  • 스레드간 동기화 비용 0
  • 메인 스레드의 응답성은 약간 저하 될 수 있다.

비동기 처리 - 멀티 스레드

  • 별도의 worker thread에 작업을 할당하여 완전히 백그라운드로 작업
  • 남는 CPU자원을 조금이라도 더 사용할 수 있다.
  • worker thread로 작업을 넘겼을 때 필요한 자원도 완전히 분리할 수 있을 경우(동기화 부담이 없을 경우)에 적합하다.
  • 메인 스레드 응답성에 거의 영향을 끼치지 않는다.
  • 스레드 동기화 비용이 아주 적을 경우만 사용해야 한다.

Thread Pool을 이용한 병렬처리

  • 1차선 도로를 N차선 도로로 확장
  • 처리량 때문에 지연이 발생하는 경우 사용
  • 처리해야 할 항목(원소) 각각이 서로 의존성이 거의 없어야 함
  • thread per core일 때,(이론상) 거의 선형적으로 처리량 증가
  • 비동기 처리와 병행 가능

코드 최적화 - SIMD등

  • 당장 수행되어야 할 코드
  • 컨텍스트 스위칭 없이 현재 컨텍스트 상에서 당장 빠르게 코드에 실행되어야 하는 경우
  • thread pool을 쓰는 경우 thread pool이 작동하기 시작해서 결과를 동기화할 때 까지의 비용이 더 클 수 있다.
  • 그냥 정직하게 빠른 코드를 짜야한다.
  • 수학 연산이 많다면 SIMD는 반드시 사용한다.
  • STL을 걷어내고 직접 짜게되면 보통 빨라진다.

응답성 향상을 위한 코딩

  • 범용 Heap 보다는 Stack을 사용
  • 범용 Heap보다는 고정 사이즈 memory pool(병합기능 x)
  • 적절한 동기화 객체 선택
  • SIMD 최적화의 경우 전체적으로 최대 30%까지 성능 향상을 기대할 수 있다.

실제 게임서버에 적용

VOXEL Horizon에서의 사례

기본전략

  • 응답성 최우선
  • 1개의 메인스레드 + n개의 보조 스레드 풀
    • 동기화로 인한 성능 저하 방지
    • 유지보수 용이(디버깅, 후임자 인수인계)
  • 클라이언트와 서버가 대부분의 코드를 공유할 것
    • 동일 입력 동일 결과 보장
    • 유지보수 용이
  • 멀티 스레드 남용 금지!
  • 가능하면 SIMD 사용 → 그러나 큰 기대는 금물

서버와 클라이언트의 코드 공유

  • 클라이언트와 서버는 최대한 코드를 공유한다.
  • 서버와 클라이언트가 동일한 입력에 대해 동일한 결과를 보장
  • 엔진 코드는 서버에서도 그대로 사용하므로 서버에서 필요한 기능을 지원할 것
    • 다수의 인스턴스 생성
    • 멀티 스레드를 이용한 병렬 처리
    • 비동기 처리

서버의 작업 스케쥴링 전략

  • 1개의 메인 스레드 + N개의 보조 스레드 풀
    • 동기화로 인한 성능 저하 방지
    • 유지보수 용이
  • 메인 스레드의 명령에 의해 다수의 워커 스레드가 작업
  • 작업을 마치면 워커 스레드가 메인 스레드에 통보

패킷 수신 → 메시지 처리

  • 최소 패킷 구조 = SIZE(4 bytes) + Body(N bytes)
  • I/O 워커 스레드의 메시지 수집과 메인 스레드의 경쟁 상태를 줄인다.
  • Double buffering
    • 하나 이상의 패킷이 수집되면 network 측 worker thread가 쓰기 버퍼에 수집된 패킷을 써넣는다.
    • main thread는 패킷 수신이 통보되면 쓰기 버퍼와 읽기 버퍼의 포인터를 swap한다(가벼운 lock사용)
    • main thread는 읽기 버퍼의 쌓인 패킷을 처리한다.
    • 처리가 완료되면 쓰기 버퍼와 읽기 버퍼의 포인터를 swap한다.

서버에서의 이동(충돌)처리

  • 클라이언트와 마찬가지로 30fps or 60fps로 처리
  • 키 입력에 대한 순서가 보장되어야 한다.
  • 플레이어간 입력 순서가 보장되어야 한다.
  • 따라서 완전 동기식으로 처리하되 개별 이동처리 시간을 줄이는 수밖에 없다.
  • 처리해야할 캐릭터 수에 비례해서 성능이 하락한다.
  • 처리량이 늘어서 응답성이 떨어지는 경우이므로 thread pool을 이용하여 병렬처리를 한다.

전투판정

  • 로켓의 유도 , 타격은 캐릭터 이동(충돌)처리와 동일하다.
  • gun 타입의 무기들은 총구로부터의 ray가 존재한다.
  • ray와 지형/캐릭터의 충돌처리가 사실상의 전투판정이다.
  • 다수의 삼각형(복셀지형)/타원체에 대한 수학연산이 필요하다.
  • 총을 들고 있는 플레이어/npc의 수가 증가하면 당연히 소요시간이 길어진다.
  • 이동(충돌)처리와 마찬가지로 thread pool을 이용한 병렬처리를 사용한다.

복셀 데이터 압축 전략

  • thread pool을 이용한 병렬처리
  • 코드 최적화
  • 싱글 스레드로 비동기 처리
  • 복셀 편집 이벤트가 발생한 경우
    • 무기에 의한 파괴/복셀 추가/색상 변경/ 삭제/ 정밀도 변경 등
    • 압축 대기 목록에 추가 ← lazy loading 비슷해보였다.
  • 플레이어의 위치 변경에 의해 복셀데이터를 전송해야하는 순간까지 압축처리를 하지 못한 경우, 해당 오브젝트는 그 즉시 압축

개인맵 로드/세이브

  • 플레이어가 자신의 개인맵에 들어가려고 할 경우 파일로부터 개인맵을 읽어야 한다. 처음 들어가는 경우 새로 생성해야 한다.
  • 플레이어가 개인맵에서 나갈 경우 개인맵을 저장해야한다.
  • 디스크 I/O가 발생하므로 느리다.(메모리 연산의 지연시간과 차원이 다르다)
  • 다수의 유저가 동시 다발적으로 개인맵에 들어가거나 나갈 경우 메인 스레드의 응답성이 크게 저하될 수 있다.

비동기 DB 쿼리

  • DB에 쿼리 후 응답 수신 후 메모리에 업데이트
    • 로그인, 아이템 획득 등
  • 서버의 메모리 업데이트 후 DB에 쿼리(저장)
    • 총알 소모, 캐릭터 데이터 세이브, 기타 등등
  • 어느쪽이든 비동기 처리
  • 절.대.로 DB의 게임서버의 패킷 처리 스레드가 DB의 응답을 대기해서는 안된다.

멀티 스레드가 능사가 아니다

  • 동기화 비용은 생각보다 크다
  • 초기 코딩의 어려움
  • 디버깅의 어려움
  • 유지보수의 어려움
  • 처리량의 한계로 응답성이 떨어지는 경우만 사용할 것
  • 활성화된 core 개수에 따라 turbo boost가 꺼지므로 예상보다 더 성능이 안나온다.

결론

  • 처리량에서의 병목으로 응답성이 떨어지는가?
    • thread pool을 이용한 병렬 처리
  • 당장 처리할 필요가 있는가?
    • 당장 처리할 필요가 없으면 비동기 처리
    • 메인 로직과 분리할 수 있는가? → 별도의 스레드를 사용하는 비동기 처리
  • 당장 처리해야 하고 병렬화가 어려울 경우
    • 코드 최적화
  • 읽기 전용 메모리는 공유, 쓰기가 발생하는 경우 copy-on-write 적용

참조

https://www.youtube.com/watch?v=LBo_rKN_e-I&t=3s

감사합니다!

728x90

'Study > TIL(Today I Learned)' 카테고리의 다른 글

24.10.17 CS, UE5  (3) 2024.10.18
24.10.14 게임 서버  (0) 2024.10.14
24.10.10 게임서버  (4) 2024.10.10
24.10.08 UE5, CS  (8) 2024.10.08
24.10.02 UE5, 알고리즘  (1) 2024.10.02