서버

서버1w2

yam3u 2025. 3. 18. 00:18

250306

● 게임 서버를 위한 하드웨어와 소프트 웨어

 

MMO게임서버의 특징

- 고사양을 필요로함.. (용량 개큼)

ex) 100기가 사이즈라면?.. 근데 메모리가 100기가 전부 올라오는게 아님..

플레이어가 현재 존재한 위치의 데이터만 올라옴.. (플레이어가 스테이지 1에 위치하면 스테이지 1에 대한 메모리(데이터)만 올라온다!..

그러나 서버!!는.. 서버에 필요한 메모리가 100기가라면 100기가 전부 올라와야한다..

왜냐면... 플레이어는 혼자가 아님..(ㅈㄴ여러명) 그 사람들 다 각기 원하는 곳, 원하는 스테이지(지형)에 서있기에 모든 지형에 대한, 각자 데이터가 필요하다... 그래서 서버는 메모리가 전~부 필요한 것ㅎ

=> 어쨌든 많은 데이터를 요구하니 가능한 한 최고사양의 pc를 사용해 동접을 올린다는 것이다...

동접 올라? => 게임 재밌어져 => 사용자 많아져 => 게임 재밌어져2 => 많은 사용자 몰려와 => 많은 수익이 되는 것...

- 10개 이상의 코어를 사용할 것.. 멀티 cpu사용이 필수이다...


게임서버 하드웨어

HW를 100% 활용할 수 있는 프로그래밍이 필요하다..

ㄴㅔ트워크 프로그래밍을 잘하자! 

메모리 용량도 중요하지만.. 메모리 억세스 설계도 잘해야한다(캐시같은것?)

우리는 이를 위해 하드웨어 지식이 필요하다... 하드우 ㅔ어 성능을 제대로 끌어내려면 하드웨어가 어떻게 돌아가는지, 어떤 영향을 미치는지 알아야한다.. 성능업성능업

게임은 항상 하드웨어의 속도에 영향을 받음

우리에게 중요한 하드웨어는... CPU와 MEMORY, NETWORK 존재

 

1) CPU

- 2가지 계열 존재(AMD, INTEL)

서버용 cpu가 따로 존재... ex : 인텔( XEON ) or AMD( EPYC )

* ARM은 안드로이드...(핸드폰에 많이쓰임) -> 얘는 서버에 쓰기엔 너무 느리다

서버용 CPU와 데스크탑 CPU의 차이 존재

: 서버용 CPU는.. MultiProcessor를 지원함(캐시동기화 지원) 서로간의 데이터 전송 전용통로를 제공해줌..

└ 이런식으로... cpu간 데이터를 빠르게 주고받기위해 연결시켜줌!! 

ex ) AMD : HyperTransport(2001~) , 인텔 : QPI(2009~) 이 이런 역할...

 

- 64비트 이슈

: 우리는 64비트를 사용할 것... 기존의 32미트로는 메모리가 부족했어서.. 서버 용량에 제한이 많았음!(참고로 Linux만 long이 64비트가 됨.. 원래(windows)는 4바이트!!) -> 이것을 주의하라네요i32t , u64t를 많이 사용함.. 우리는 프로그래밍 시 int type과.. Pointer type의 크기가 달라지는 것을 주의 해야함..

(왜냐면 32비트에서는 둘다 4바이트(32비트), 이지만.... 64비트 환경에서는 포인터는 8바이트로 메모리 크기가 변경됨..)

 

- 멀티 프로세서(Multi-processor)

: 여러개의 cpu를 사용해 성능을 높임... smp사용 -> 네트워크 응답 속도와 처리속도를 개선

 

- 멀티 코어(Multi-core)

: 하나의 CPU에 여러개의 코어존재, 코어 갯수가 많아질수록 성능 향상!

 

멀티프로세서, 멀티코어 둘의 차이는...

sw적으로는 차이 x,

hw적으로는 메모리 접근 시.. 성능차이가 존재한다..! (cpu갯수만큼 메모리 대역폭이 증가)

 

CPU발전의 Trend

: 클럭속도가 증가, ( cpu가 1초동안 실행할 수 있는 사이클의 수, 단위는 Hz.. 클럭속도 증가 시 성능 굿)

  클럭당 수행되는 명령어의 갯수 증가(=IPC)

그러나......

pipeline이 4개에서 8개 된다고..속도가 2배된다? -> xxxx한계 효용의 법칙 존재.. 속도 증가에 한계가 존재한다....: Core 갯수 증가.. (2005년~) -> 이건 발열 문제 존재

 

파이프라인이란..? 

-> CPU가 명령어를 빠르게 처리하기 위해 여러 단계를 동시에 실행하는 방식..

즉 하나의 명령어가 끝날때 까지 기다리지 않고, 여러 명령어를 동시에 처리해 성능을 높이는 기술

일반적으로... IF -> ID -> EX -> MEM -> WB 의 순서로 실행됨

IF ) 명령어 패치(Instruction Fetch) : 주 기억치로부터 명령어를 읽어옴..(가져옴)

ID ) 명령어 디코드(Instruction Decode) : 명령어를 해석.. -> 필요한ㄷ ㅔ이터 구분!

EX ) 실행(Execute) : 말그대로 실행^^ (연산수행함)

MEM ) 메모리 엑세스(Memory Access) : 메모리에 접근(필요한 경우 메모리에서 데이터를 가져오거나, 저장)

WB ) 결과 쓰기(Write Back) : 결과를 저장한다.. 이때 레지스터에 저장함. 

*

 

- 파이프라인의 발전

과거에는.... 1개의 명령어를 실행할 때 몇 클럭이 필요한지가 중요했지만, 파이프라인이 발전함으로써 이 개념이 무의미해짐...(왜냐면 과거엔 cpu가 1번에 1개의 명령어만 처리했으니까)근데 파이프라인이 뭐니? 여러개의 명령어를 동시에 실행할 수 있게 해주는 것!! 

그러니까~~~ 한 명령어가 몇 사이클이 걸리는진 의미가 없음, 전체적으로 cpu가 얼마나 빠르게 실행되는지, 

cpu 너,, 초당 몇 개의 명령어를 처리 할 수 있어?! 가 중요해진 것!!!!!!

"프로그램의 실행속도는 메모리 READ에 종속됨"  

=> cpu의 실행 속도는 결국 메모리 접근 속도에 의해 제한이 된다~ 메모리 접근이 얼마나 최적화 되어있는지도 중요하다~(메모리 접근이 적으면 더 ㅃㄹ 실행할 수 있음)

 

- SIMD 명령어의 발전 

: AVX(Advanced Vector Extensions) 존재..

 

- 파이프 라인의 고도화에 따른 주의!

: 파이프라인 리셋이 발생하면.. 성능 저하됨.. ( 리셋 원인 : 시스템 콜, 분기 예측 오류, 인터럽트 및 예외 등... )

리셋 하는데? -> 파이프라인이 정상적으로 작동하려면 순차적으로 실행되어야함..

하지만 시스템 콜, 분기예측오류, 인터럽트 발생 시 ?!  파이프라인 리셋 해야함.. 이게 성능저하 일으킴ㅜ

시스템 콜의 경우 : 유저 <-> 커널 모드 오갈 때 리셋이 필요 => 근데 이게 오버헤드가 너무 큼, 몇천 쿨럭이 날아가버려...

파이프라인은... 여러 명령어를 겹쳐서(동시에) 실행하는 방식임... 예상치 못한 상황 발생 시 잘못된 명령어가 실행 될 수 있ㅇㅇ

그러니까 리셋이 필요하다~~ 요약하면 잘못된 연산을 방지하기 위해! 근데 리셋은.. 성능저하를 일으킨다..

참고로 서버에서는 delete는 안됨.. new 해놓고 계속해서 쌓아 올릴 것!

대책은....

시스템 콜을 될 수 있으면 하지 말 것, if/switch문.. 같은 분기예측오류를 일으키는 명령어 사용 x

 

- CACHE

: 캐시는.. 프로그램 실행속도에 가장 큰 영향을 미치는 요소.. 캐시가 큰 cpu일수록 속도가 빠르다!

 

- TIP 팁^^

: 같이 쓰이게 되는 데이터는 묶어놓자.. (몬스터의 x,y 위치를 따로 저장하지 않고, 같이 저장하기!!) 

-> 왜냐면 또 따로 떨어져있다..? 그럼 또 읽어야돼.. -> 이게 바로 캐시미스유발..-> 성능저하됨

루프 안에서 사용하는ㄷ ㅔ이터는 캐시에 다 올라올 수 있도록 설계하자!

int 대신 short나 char를 사용하자 ㅎㅎ (이래야 메모리 사용량이 줄어듬..) 

bool 도 사용xx(8바이트나 차지), 그리고 비트필드 사용

 

- 멀티 프로세서 프로그래밍(Multi Processor Programming)

잘하면 N배 성능 향상, 못하면 성능 하락

LOCK을 줄여라.. 락이 없도록 잘하자!! -> 락 발생 시 성능 저하..

semaphore, condition 변수는.. 시스템 콜 유발.. -> 이거는 게임서버에서 사용하지 않는다..

 

최근 CPU는 클럭 속도를 무작정 높이는 대신!!!

멀티코어, 파이프라인 최적화, 캐시 증가 등의 방식으로 성능을 향상시키고 있다...

 

2) MEMORY

- 서버가 요구하는 용량을 제공하면 된다...

그러나 종종 에러가 발생할 수 있음(불량이 아닌데도.. 방사능 때문에?) 그래서 error 수정 기능이 있는 특수 메모리를 사용한다...!

(중요한 데이터 날아가면 안되니까...) 

대역폭이 크지만 반응속도는 느리다..

 

3) NETWORK

- 서버간의 연결을 위해 한 서버에 여러개의 네트워크 카드를 꽂아 사용..

더 빠른 속도를 원한다면 infiniband 사용


게임서버 운영체제

 

서버용 운영체제의 종류 (Unix, Windows)

- UNIX 계열

└ 리눅스, freeBSD, Solaris, OSx

└ 가격이 저렴함

└ 유지 보수 관리가 어렵다.. ( 잘 아는사람이 필요함! )

 

- Windows 계열

└ 서버용 윈도우가 존재 (windows2016, 2019, 2022)

└ 비쌈, 멀티 CPU 지원

└ 유지 보수 관리가 비교적 쉽다!

 

프로그램 최적화

 

1) 꼭 필요한 일만 하기
2) 좋은 알고리즘 사용하기
3) 메모리 복사 줄이기
4) HW 영향 고려 (캐시,파이프라인)
5) 멀티스레드 프로그래밍

 

실습

 

1) 시스템콜 호출하기 -> 반응속도 차이 확인

os호출o
os호출x

this_thread::yield(); <- 이게 OS호출하는 명령어..

os호출시 733msec

os미호출시 2msec

 

2) CACHE 실습

캐시미스를 해도 느려진다...

for루프를 돌릴 때 x,y 위치 차이도 속도가 차이난다!

 

3) Pipeline 실습

 

#define abs1(x) ((x)>0) ? (x) : -(x))

VS

int abs2(int x) {

int y = x >> 31; (비트연산)

return (y ^ x) - y;

}

둘 중 누가 더 빠를지 예측... 둘다 같은 기능을 함(절댓값 찾는 거)

#define abs(x) ((x)>0) ? (x) : -(x)) 얘가 더 빠를거라 예측했음..

그러나.. 

pipeline실습 돌려보니까.. abs2가 더빨랐던 것으로... 

왜냐면 분기예측오류 때문에... ( 아까 했던..파이프라인에서 팦라인을 리셋하던 이유 중 하나인 분기예측오류가 발생함!!! )

abs1(x)에서는 cpu의 branch예측 실패로 성능저하가 발생함.. (동작 자체가 if-else로 동작해 분기가 발생함)

그러나 abs2(x)에선 branch가 없어서 cpu의 파이프라인 성능저하가 적었기에 빠를 수 있었다~! (비트연산이 빠르기도 함)

결론은 분기 없는 연산이 cpu의 파이프라인 최적화에 유리하다..!

 


정리! 

게임서버의 최적화는 HW도 고려해야 한다..

SystemCall 하지 말 것... 캐시 잘 사용하기..(가능하면 메모리가 적게)

멀티스레드프로그래밍하기ㅎㅎ

'서버' 카테고리의 다른 글

다중서버정리(iocp)  (0) 2025.04.06
일대일서버정리  (0) 2025.04.06
서버 2w1(네트워크프로그래밍)  (0) 2025.03.18
서버1w1  (0) 2025.03.14