꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자


안녕하세요. 처음 이렇게 글을 올리게 됩니다.
저는 네오플에 다니고 있는 프로그래머 denoil 입니다.
다들 실력이 쟁쟁하신 분들이라서 제가 이런 글을 올려도 되나 하는 망설여지는데 개발자들끼리 공유하는 것은 좋다고 생각하여 이렇게 글을 올려봅니다.^^

 제가 오늘 발표할 내용은 홀펀칭 입니다. 현재 P2P 게임을 만드시는 분들께서는 모두들 아는 내용이시겠지만 처음 P2P를 접하거나 만드시게 되는 분들께는 도움이 되지 않을까 하여 이렇게 남겨 봅니다. 제가 지금부터 하는 말들은 실전에서 사용 했던 방법을 근거로 이야기 하는 것이지 네트워크 이론을 막 신경써서 하는것이 아님을 미리 말씀드립니다.

 일단 P2P를 사용하는 가장 큰 이유는 역시 빠른 속도 겠지요. 패킷이 서버를 거치지 않고 Peer에게 바로 전송이 되니 그만큼 빠른 것도 없겠지요.
방법에는 TCP를 사용할 수도 있고 UDP를 사용할 수도 있겠습니다. TCP를 사용한다고 P2P가 아닌건 아니지 않습니까? Peer to Peer이기 때문에 서로 직접 연결만 되어 있으면 되는 것 입니다.
허나! TCP를 사용하지 않는 이유는 UDP가 TCP보다 빠르기 때문 입니다. TCP는 패킷을 보장하기 위해 헤더가 더 붙고 뭐 순서를 보장하기 위해 block 하기도 하고 이것 저것 한다고 합니다.(자세한 내용은 책을 참고 하세요) 
UDP는 패킷을 보장하진 않지만 빠르다는 장점이 있습니다. 패킷 손실률이 있어 이부분은 직접 프로그래머가 제어를 해줘야 하는 단점이 있지만 어찌됐든 일반적으로 UDP는 TCP보다 빠르다고 합니다. (중국에서는 UDP보다 TCP가 더 빠르다는 이야기가 있습니다.) 
그래서 최신 P2P게임에서는 UDP를 많이 사용 합니다.

 Peer들 간의 통신을 하기 위해선 서로의 IP, PORT에 패킷을 전송하면 그만 입니다. 그러나 우리는 하나의 고정 IP를 가지고 공유기나 방화벽의 NAT( Network Adresss Translation )라는 장애물을 만나게 됩니다. NAT를 거치게 되면 사설 IP( 192.168.0.2 이런식의 IP )가 지급? 되게 됩니다.


 실제 공인IP 가 아니지요. 사설망에서만 서로간의 통신이 가능한 가상 IP라고 생각 하시면 됩니다. 그래서 NAT단에서 방화벽으로 IP, PORT등이나 인터넷을 막아버리면 아예 인터넷이 불가능 한 그런 상황도 만들어낼 수도 있고 인터넷 연결 없이 사설망에서만 돌아가게 인트라넷을 만들기도 합니다. 사설 IP로 서로 통신을 하게 되면 같은 NAT안에서의 통신은 가능 하지만 서로 다른 NAT에서는 당연히 통신이 되지 않습니다. NAT안에서만 존재하는 가상 IP이기 때문 입니다.
NAT의 종류에도 여러가지가 있습니다.
1. Full Cone NAT
2. Restricted Cone NAT
3. Port Restricted Cone NAT
4. Symmetric NAT
이런것들이 있다고 합니다. 어떤 NAT는 먼저 패킷을 쏴서 포트를 뚫어놔야 하고 어떤 NAT는 먼저 패킷이 와야 포트가 뚤리고 뭐 어떤 NAT는 어쩌구 저쩌구해서 저쩌구 하고 제가 오늘 여기서 설명 드릴 것은 저런 NAT 타입에 대해서는 전혀 모르셔도 됩니다. 왜냐하면 저도 모르니깐요. :D
모로 가도 서울만 가면 된다고 합니다. 이제부터 실전에서 배워보는 홀펀칭에 대해 알아보기로 하겠습니다. 이전 회사에서나 현재 회사에서나 모두 보니 홀펀칭의 방법은 동일 했습니다.

 지금까지 설명드리지 않은 홀펀칭! A4 용지 파일을 정리할때 구멍을 뚫는 펀칭도구를 홀펀칭이라고 부르지요? 말그대로 NAT로 인해 막혀있는 홀( 포트? )를 네트워크 패킷이 지나갈 수 있게 뚫는 것입니다.
방법에는 여러가지가 있습니다. 위에 설명드린 것처럼 누가 먼저 보내고 받고도 있고 포트 번호의 구간을 정해 놓은 후에 하나씩 시도 해보는 방법( 예전 기억엔 그렇습니다. -_- )도 있습니다.
하지만 여기서 포트번호는 한개로 고정 하겠습니다. 포트번호는 임의로 정하되 방화벽으로 막혀 있지 않는 포트 번호를 선택 합니다.
현재 저희 게임에서 사용하고 있는 방법이고 문제 없이 잘 사용 하고 있는 방법 입니다.
그리고 포트 번호 비교를 통해서 현재 어떤 NAT환경인지 체크할 수 있는 내용도 있지만 현재 주제가 길어 질 수 있으므로 생략 합니다. 

간단하게 3단계로 정리 해 보겠습니다.

1. 사설(private) IP로 패킷을 전송해 본다.
2. 안되면 공인(public) IP로 패킷을 전송해 본다.
3. 그래도 안되면 어쩔수 없이 Relay Server로 패킷을 전송해 대신좀 전송해 달라고 한다.

( 여기서 릴레이 서버는 패킷을 중계 해주는 서버입니다. 클라이언트로 부터 받은 패킷을 다른 클라이언트에서 전송만 해주는 역할을 담당하고 다른 일은 하지 않습니다. 지금까지 만난 프로젝트에서는 TCP를 사용했는데 전 회사의 다른 팀 게임에서는 UDP를 사용해서도 했다고 합니다. )

위의 세가지 입니다. 현재 나와있는 넷텐션의 ProudNet 같은 경우 다양한 테스트와 다양한 방법을 사용하여 홀펀칭을 하여 99%의 성공률 보인다고 합니다. ( 끼로님한테 들은 이야기 입니다. -_-)a 문제가 된다면 내용을 삭제 하겠습니다. ) 하지만 여기서 다루는 내용은 홀펀칭을 이용해서 99%의 성공율을 해주지는 못할 것입니다. 릴레이서버의 도움으로 99%까지는 가능 합니다. 기본적인 내용이고 현재 상용게임에서 사용하는 내용임을 거듭 강조 드립니다.

다시 돌아와서 천천히 방식을 전개해 보겠습니다. 위의 3단계를 시행하기 위해선 패킷을 보내려는 상대방의 사설, 공인 릴레이서버의 IP를 알아야 합니다. 그러려면 누군가는 나에게 상대방의 IP 정보를 알려 주어야 겠지요. 그것은 게임 서버가 역할을 하게 됩니다. ( 다른 서버가 해도 되는데 일단 게임 서버라고 하겠습니다. ) 게임 서버에게 나의 IP 정보를 통보하여 서버에서는 정보를 저장하고 있다가 해당 정보를 다른 유저에게 알리는 방식이 되겠습니다.

 이제 서버에게 통보를 할 정보를 수집해 보도록 하겠습니다. 처음 해야 할일은 자신의 IP를 알아내야 합니다. gethostname 함수를 이용하게 되면 자신의 IP를 알 수 있게 됩니다. 그러나 NAT환경에 있게 된다면 현재 나의 IP가 사설 IP 인지 공인 IP 인지 알 수 없게 됩니다. 그러면 공인 IP를 어떻게 알 수 있을까요? NAT환경 밖에 있는 서버는 알 수 있습니다. NAT환경 밖에 있게 되는 서버는 패킷을 받을때 어떤 IP로 왔는지 알 수 있기 때문입니다. 서버와의 통신을 한번 해야 합니다. UDP로 하도록 하지요. TCP로 하면 바인딩하고 커넥션하고 귀찮고 UDP로 간단하게 쏘고 받기만 하면 되는 일이니까요.

자 그러면 공인 IP를 알기 위해서 1바이트짜리의 빈 패킷을 서버로 보냅니다. ( 예를 들어서 빈 패킷일 뿐 제작하시는 분 마음대로 패킷에 내용을 채우셔도 상관 없습니다. )
그리고 서버는 패킷을 받았다면 받은 IP, PORT를 패킷내용에 실어서 패킷을 보내온 클라이언트에게 다시 전송을 해 줍니다. ( 이 과정에서 아마 일단 PORT가 열리는 것으로 알고 있습니다. 자세한 내용은..NAT 관련 문서를 찾아보아야 할 것 같습니다. )

만약 클라이언트가 일정 시간동안 서버로 부터 패킷을 받지 못했다면 다시 시도를 합니다. UDP는 패킷을 보장해주지 않기 때문에 손실이 되었을 수도 있기 때문입니다.
클라이언트는 패킷을 받게 되면 정보수집은 끝 입니다. 
사설IP, 공인IP 모두 알게 되었습니다. 릴레이서버IP는 처음 로그인할때 게임서버에서 받기로 하지요.
이제 수집한 정보를 게임서버에 로그인 한 후에 서버에게 보내주면 준비 끝 입니다.

위의 과정과 앞으로의 과정을 한눈으로 알아볼 수 있게 그림으로 표현 합니다.



내정보 보냈으니 서버에서는 데이터를 가지고 있겠고 각각의 Peer들은 상대방의 IP 정보를 알 수 있게 되었습니다. 사설 IP, 공인 IP, 릴레이서버 IP 모두 알 수 있게 되었습니다. 
이제 파티가 이뤄지기 시작하면 위의 정보를 가지고 처음 위에서 설명 했던 3가지를 시도 합니다.

 

일정 시간 동안 일정한 간격으로 Peer A는  Peer B에게 더미 패킷을 사설 IP로 전송 해 봅니다. 만약 Peer B가 해당 패킷을 받게 되면 상대방에게 받았다고 알려 줍니다. Peer A는  패킷을 받았다면 앞으로 사설 IP로 패킷을 보내면 됩니다.
그러나 일정 시간 동안 받지 못했다면 이번에는 공인 IP로 패킷을 보내 봅니다. 공인 IP로 쐈을때 다시 패킷을 돌려 받았다면 앞으로 공인 IP로 쏘면 됩니다.
일정 시간 동안 또 받지 못했다면 서로 NAT환경에서는 통신이 불가능 합니다. 결국엔 릴레이 서버를 이용해야 하는 상황이 발생하게 됩니다. ( 최대한 릴레이 서버와의 연결은 안붙는게 좋습니다. 한단계를 더 거치기 때문에 느려지기 때문이죠. 그리고 여러명이 붙게 되면 그만큼 릴레이서버에 부담을 주기 때문입니다. )

처음부터 간단하게 다시 정리 해 드리겠습니다.

1. 클라이언트는 gethostname으로 사설 or 공인 IP의 주소를 저장함.
2. NAT환경이 아닌 게임서버 혹은 스턴서버에게 dummy Packet을 보냄. 
3. 서버는 받은 패킷의 IP, PORT로 IP, PORT를 패킷 내용에 담아 클라이언트에게 보냄.
4. 클라이언트는 사설 IP, 공인 IP 모두 저장.
5. 클라이언트는 수집된 사설 IP, 공인 IP를 서버에게 보냄.
6. 파티가 이루어질 경우 서버는 각각의 Peer에게 상대방의 사설,공인 IP를 전송.
7. 각각의 Peer들은 일정시간동안 사설 IP로 패킷 전송.
8. 사설 IP로 패킷 전송 실패시 공인 IP로 패킷 전송.
9. 공인 IP로 패킷 전송 실패시 최후의 보루 릴레이서버에게 맡김.

참 쉽죠잉?

여기서 주의 할점은 서로 패킷통신이 가능해지더라도 일정 시간동안 패킷을 주고 받지 않으면 포트가 막힐 수 있다고 합니다. 그러니 일정 시간동안 패킷이 오가지 않았다면 더미 패킷 혹은 핑 패킷이라도 쏴주면서 포트를 유지 시켜주면 좋겠습니다.

현재 상용게임에서 쓰고 있는 방식입니다. 얼마전 해외 서비스 통계상으로 봤을때 80%이상의 P2P성공율을 보였고 나머지 안되는 NAT 환경은 릴레이 서버를 함께 사용할 경우 98~99%의 P2P성공율을 보이고 있습니다.

많은 P2P 게임을 만드신 분들은 모두들 아는 내용일 것이라고 생각합니다. 각각의 회사마다 방식은 모두 조금씩 다르겠지만 일반적으로 저 방식을 통해서 홀펀칭을 하는 것으로 알고 있습니다. 저는 전문적으로 네트워크를 공부한 입장이 아닌 클라이언트 프로그래머 입니다. 잘못된 내용이 있다면 따끔히 지적해주세요. 저도 소스만 보고 익히고 공부한 내용이라서 틀린 부분도 분명 있을 것입니다. ^^
아무쪼록 여러분들께 좋은 자료가 되었으면 하며 이만 물러 나겠습니다.
긴글 읽어주셔서 감사합니다.

-denoil-

반응형
,
Posted by 알 수 없는 사용자

아무리 재미있고 뛰어난 그래픽의 게임을 만들어도 너무 고사양의 PC를 요구하거나 고사양의 PC로 돌리고 있음에도 최적화 되지 않은 문제로 유저가 제대로 게임을 즐길수 없다면 시장의 외면을 받기 쉽습니다.

하지만 어떻게 최적화가 잘 되어있는지 판단하는 것도 문제입니다.

여러가지 사양의 PC를 마구잡이로 전산팀에 요청해 받은 뒤 게임 클라이언트를 실제로 돌려보며 확인하는 방법도 있겠지만 좀 더 스마트한 방법을 찾아보도록 하겠습니다.(정말 스마트한 방법은 프로그래머가 클라이언트 내부에 메모리 사용량과 Frame을 기록해주도록 한 뒤 Bot이 돌아다니며 데이터를 수집하는 방식을 사용하더군요. -_-b)

아마도 이 방법은 개발팀의 지원이 어려울 경우 Test팀이 사용 할 수 있는 최선이지 않을까 싶습니다;

준비물

Fraps –  Frame 측정 및 기록하는데 꼭 필요합니다.

perfmon – 윈도우 성능 모니터링 도구. XP부터 7까지 모두 윈도우에 기본으로 탑재되어 있습니다.

Excel – 최고의 테스트 Tool 이며 친구입니다. 테스트를 한다면 Excel은 꼭 배워둡시다.

Test 진행

a. Fraps 준비

1. Fraps에는 동영상 녹화로 많이 쓰이지만 그 외에 스크린샷 촬영, Frame 기록 기능이 붙어 있습니다. Test 시에 사용되는 것은 Frame 기록 기능과 일정 시간마다 스크린샷을 촬영하는 기능입니다.
그러나 Fraps에서 2가지 기능을 도시에 사용은 못하기 때문에 Frame 기록 기능만 사용하도록 합니다.
2. FPS 탭페이지에서 FPS 체크해 줍니다.
4. 참고로 fraps는 구매해야 위 기능을 모두 쓸수있습니다. 가지고 있음 유용한 프로그램이니 떼를 써서라도 회사에 구매해 달라고 해봅시다.
5. 주의사항 단축키로 주로 쓰이는 F11, F10 같은 펑션키는 게임 클라이언트내에서도 무언가의 단축키로 쓰일 수 있습니다. 최대한 게임 내 단축키와 겹치지 않도록 설정합니다.6. 일정 시간마다 스크린샷을 찍어주는 기능이 따로 필요합니다. Fraps와 비슷한 프로그램중에 반디캠이라는 프로그램이 있습니다.  반디캠의 옵션에 가면 이미지 탭의 캡쳐에 이미지 캡쳐 반복 옵션이 있습니다. 10초 정도의 시간을 줍니다. 단축키 설정도 있는데 역시 Fraps와 겹치지 않게 잘 설정합니다.

B. perfmon 준비

1. 우선 게임 클라이언트를 실행시켜 둡니다. 그 이유는 나중에 나옵니다. 그 다음에 Win – R 단축키를 누르면 실행 창이 뜹니다. perfmon 이라 쓰고 확인을 눌러주면 성능 모니터 도구가 실행됩니다.

2. 우선은 Win7 기준으로 설명 드리겠습니다. 메뉴 이름은 XP나 Vista나 비슷비슷하니 찾아보시면 금방 아실 수 있습니다. “데이터 수집기 집합 > 사용자 정의” 를 들어가신 뒤 오른쪽 공백페이지에서 오른 클릭을 하시면 “새로 만들기 > 데이타 수집기 집합”을 선택 하실 수 있습니다.

3. 선택하시면 새 데이터 수집기 집합을 만들기 위한 실행창이 뜹니다. 우선 이름을 정해줍니다. 이름은 직관적으로 적습니다. “게임명_메모리사용량_확인” 정도면 될 것 같습니다. 템플릿과 수동을 선택 할 수 있습니다만 수동을 선택합니다.

4. 다음을 선택하시면 “데이터 로그 만들기” 에서 “성능 카운터”를 체크하고 다음으로 넘어갑니다.

5. 성능 카운터를 추가 하는 페이지입니다. 추가 버튼을 누르면 윈도우에서 사용하는 리소스들의 리스트를 볼 수 있습니다. 이중에서 선택할 것은  ”Process > Private Bytes” 입니다. 맨처음 화면에는 Processor 만 보이는데 그 바로 위에 Process 가 있습니다. 헤메지 마세요.

6. Private Bytes를 선택했으면 그 바로 아래 개체 인스턴스를 선택하는 곳이 있습니다. perfmon을 실행시키기 전에 클라이언트를 먼저 실행시키는 이유는 그래야 이곳에 client의 인스턴스가 나오기 때문입니다. 클라이언트 인스턴스를 선택한 후 추가 버튼을 누르면 오른쪽에 클라이언트의 메모리 사용량 카운터가 추가 됩니다.

7. 확인을 누르면 다시한번 카운터가 추가된 것을 확인 할 수 있습니다. 하단에 샘플간격 단위를 정할 수 있는데 1초로 합니다.

8. 다음을 누르면 로그 데이터를 남길 폴더를 선택합니다. 적당히 좋아하는 폴더를 설정해주고 마침을 누릅니다.

9. perfmon 의 준비도 완료되었습니다.

C. 시나리오 준비
● 테스트를 시작하기전 시나리오를 준비하는 것이 좋습니다. 시나리오에는 다음과 같은 요소를 정리합니다.

a. 테스트 PC Spec을 정리합니다. 가능하다면 고사양/중사양/저사양 PC와 게임 내에서 설정 가능한 그래픽 옵션을 맞춰서 정리해주는 것이 좋습니다. PC Spec의 고려는 다나와 사이트의 PC방 PC 스펙과 STEAM의 통계 페이지를 참고합니다. (http://store.steampowered.com/hwsurvey )b. 테스팅 시간 : 30분에서 1시간 정도의 진행 시간을 잡습니다. 다른 요소에 맞춰 정하는게 가장 좋습니다만 1시간 이상 넘어가면 데이터 가공 및 확인에 어려움이 생기니 최대 1시간 정도가 좋습니다. 그 이상 해야될 경우에는 1시간 단위로 테스트를 끊어서 중간중간 데이터를 취합해가며 진행합니다. (왠만하면 쉬면서 합시다…)

c. 진행 루트 : 게임 내에서 이동할 필드의 루트를 정합니다. 오브젝트가 많은 곳, NPC가 많은 곳등 특징적인 장소를 정하거나 사전 프로그램팀의 의견을 얻어 확인되면 좋을 장소를 선정합니다.

d. 시나리오 : 테스팅 동안 수행할 액션들을 시간별로 정리합니다. 비쥬얼이 화려한 스킬을 사용한다던지 각자 따로 움직이던 테스터들을 정해진 시간에는 모여서 PK를 한다던지 클라이언트에 부하가 될만한 액션들을 놓으면 됩니다.

e. 짝짓기 : 게임 내에서 같이 이동할 테스트 파트너를 정합니다. 최소한 2명 이상은 같은 지역을 이동하며 시나리오를 진행해야 서로 다른 PC에서의 데이터 비교가 가능합니다. 같은 지역을 다니더라도 누구는 과부하가 생길 수 있고 누구는 안생길 수 있는 법입니다. 비교 대상이 있어야만 이와 같은 문제 상황을 찾을 수 있습니다.

D. 테스팅 진행
1. 이제 테스트 준비가 되었다면 시작합니다. 시나리오 시작 전 프랩스의 FPS 기록과 반디캠의 스크린샷 기록 기능을 단축키로 활성화 해준 뒤 perfmon 도 데이터 수집기 집합에서 만들어 놓은 카운터를 상단의 실행 버튼으로 활성화 시켜 줍니다.
 
2. 자 이제 테스트가 시작되었습니다. 준비해 놓은 시나리오대로 잘 진행하면 됩니다.3. 몇 가지 주의사항
a. 테스트란게 만사형통으로 잘되었으면 좋겠지만 그게 쉽지만은 않죠. 분명 테스트 진행 중 클라이언트 혹은 서버 crash 가 발생할 수 있습니다. 진행된 곳까지의 기록은 남으니 해당 기록물들을 한폴더 모아 압축해 정리해 놓고 집착하게 다시 테스트를 진행합니다.b. 서버 crash나 클라이언트 crash 상황이 특정한 재발생 스텝이 있을 경우 최적화 테스트를 하는 동안에는 해당 액션을 피하도록 합니다. 물론 해당 문제는 따로 보고가 되야 합니다.
4. 테스트가 완료되면 각 테스터들은 perfmon, fps, 스크린샷 파일들을 모아 압축 파일로 만들어 진행자에게 전달합니다. 압축 파일명은 사전에 통일 시켜주는 것이 좋습니다. “홍길동_시나리오1.zip” 혹은 crash가 있던 결과물은 “홍길동_시나리오1_crasf.zip” 이런식으로 말이죠.
E. 결과물 정리하기
1. 드디어 결과물들을 정리할 시간이 되었군요. 테스터들은 모두 가버리고 진행자인 당신만 남았습니다. 그럼 기분을 가라 앉히고 파일들을 열어 봅시다. 우선 perfmon 입니다. 설정에서 선택한 폴더에 가면 DataCollector01.blg 파일이 있을 겁니다. 실행시키면 자동으로 모니터링 도구가 실행되며 보고서 그래프를 볼 수 있습니다. 엑셀에서도 이 그래프를 볼 수 있을 것입니다. 그래프 화면에서 오른 클릭 후 “데이터를 다른 이름으로 저장”을 선택 합니다. 그리고 파일형식을 쉼표 구분의 csv 파일로 선택하여 저장 합니다.2. 저장된 csv 파일을 열어봅시다.A열이 시간이고 B열은 사용된 메모리 값입니다. 표만봐도 시간이 지날 수록 메모리 사용량이 늘었음을 알 수 있군요.자 이제는 Frame 입니다.

3. fraps의 저장 폴더를 따로 설정하지 않았다면 fraps 설치 폴더 내에 Benchmarks 라는 폴더가 생겼을 겁니다. 그 폴더 내에 역시 csv 확장자 파일이 있습니다. 열어보도록 하겠습니다.

떨렁 A열에 숫자만 잔뜩 있는 문서가 열릴 것입니다. 저 숫자들이 초당 Frame 값입니다. A열 선택 후 복사한 뒤 perfmon 결과 문서에 합칩니다.

엑셀 내용을 좀 종리했습니다. 시간은 년월일은 제거하고 시분초만 남겼고 FPS를 C열에 붙여넣었습니다. 이제 그래프로 만들겠습니다.

4. Excel 2007 부터 그래프 만들기가 참 쉬워졌습니다. 위의 A,B,C 열을 모두 선택한 뒤에 리본 메뉴의 삽입에서 그래프 “꺾은선형”을 선택하면 바로 그래프가 만들어 집니다.

예제의 Excel은 2010 같기도 하지만 어차피 리본메뉴는 같습니다. 리본메뉴 없는 Excel을 쓰신다면 네이버에서 그래프 만드는 법을 검색해보시기 바랍니다. 아니면 회사에 Excel 2007 이상은 사달라고 쫄라봅시다.

5. 그래프는 만들어 졌는데 메모리 사용량은 나오는데 FPS는 그래프에 보이지 않습니다. 메모리 사용량과 FPS의 값 단위가 너무 나서 그렇습니다. 조정을 좀 해주겠습니다.

축 옵션에 들어가서 최대값과 주 단위를 좀 조정해 준 뒤 표시단위는 백만으로 선택합니다. 그러면 단위값이 변경되며 하단에 FPS 그래프가 바닥에 붙어서 나오게 됩니다.

바닥의 껌딱지를 오른 클릭한 뒤 데이터 계열 서식을 선택 합니다. 계열옵션에 보면 보조 축을 선택할 수 있는데 선택화면 오른쪽에 FPS 축이 따로 생기면 껌딱지가 발딱 일어납니다. 기운차군요(…..)

6. 너무 자세하게 쓰느라 슬슬 힘들기 시작하므로 그래프의 미세한 조정은 엑셀책을 보고 알아보시길 권하고 이제 문제점 찾기로 가보겠습니다.

F. 문제점 찾아보기
좀 정리된 그래프를 보도록 하겠습니다.나름 깔끔한 그래프입니다. 메모리 사용량에서 특출나게 많이 잡아 먹는 경우도 없고 중간중간 누수없이 반환도 잘 되고 있습니다. Frame은 굴곡이 너무 심해보입니다. 잦은 Frame 변화는 유저의 시각적 피로도를 증가시키는 요소 입니다. 수직 동기화 옵션을 제공해서 최대한 60fps 정도를 유지해주는 것이 좋습니다.갑작스럽게 Frame이 떨어지는 곳이 보입니다. 왜 그런지 유추해야겠죠. 먼저 집에간 테스터들을 괴롭혀 봅시다. 문제가 있는 그래프의 테스터에게 전화를 걸어 테스트 할 때 몇시 몇분에 무슨 상황이었냐며 물어봅시다. 기억을 못하면 그런것도 기억을 못하냐고 해봅시다. 자신의 쿨가이임을 느끼며 전화를 끊습니다. 는 농담이고.. 이제 스크린샷을 사용합니다. 스크린샷은 10초 단위로 찍도록 설정했기 때문에 해당 시간에 맞는 스크린샷을 열어서 확인해봅니다.스크린샷을 통해 Frame이 떨어지는 곳의 장소와 상황을 알 수 있습니다. 어떤 스킬을 사용했더니 파티클로 인해 프레임이 떨어 질 수도 있고 해당 장소에 유저들이 잔뜩 모여 렉이 발생했을 수도 있습니다. 아니면 오브젝트가 잔뜩있는 장소일 수도 있구요.Frame 뿐 아니라 메모리 사용량에 있어서도 그래프에 극심한 변화를 볼 수 있을 경우 스크린샷으로 해당 상황을 유추할 수 있습니다.

문제의 확인 시에는 다른 테스터들의 결과물도 도움이 됩니다. 꼭 비교 참조해서 전체적으로 발생하는 현상인지 하나의 PC에서만 나오는 현상인지도 확인하도록 합니다.

어떤 류의 문제가 생기는가에 대해 모든 상황을 언급해주기는 어렵습니다. 문제라 생각하고 얘기했는데 실은 아니더라라는 경우도 충분히 있을 수 있거든요. 가장 좋은건 그래프와 스크린샷을 통해 알아낸 결과물들을 정리 한 뒤 개발팀과 같이 확인 하는 것이라고 봅니다. 좀 더 확실한 문제의 이유를 찾을 수도 있고 자신의 경험 축적에도 도움이 될수 있을 것입니다.

p.s 오랫만에 긴 글을 작성할려니 힘들군요. 나눠쓰고 싶은 욕구가 강하였으나 테스트 진행 과정이 중간에 뚝 끊겨있으면 욕먹을것 같아서 그러지 않았습니다; 하지만 내용량 대비 시간을 들이지 못해서 부족하거나 이해 안가는 부분이 있을 수도 있을것 같습니다(......) 그런 부분은 질문 주시면 답변을 드리도록 하겠습니다. 감사합니다.

반응형
,
Posted by 알 수 없는 사용자
안녕하세요. rihwan (유인환) 입니다. 사실 오늘은 제가 글 쓰는 날은 아니지만, 가볍게 개발에 도움이 될만한 내용을 올려볼까 합니다.

저는 주로 Windows 개발만을 해왔기 때문에 Windows를 기반으로 하여 글을 써보도록 하겠습니다. 하지만 원하신다면 언제든지 이외의 환경으로 바꿀 수 있습니다.

한 프로그램에서 전세계의 글자를 동시에 표현하고 싶을 때. 채팅, 게임의 해외 퍼블리싱등을 생각할 때 Unicode 표현을 쓰게 되면 상당히 이익을 보게 됩니다.

약간의 역사적인 흐름을 보자면...

초기 컴퓨터를 생각해보면 당시에는 주로 영미권에서 컴퓨터를 개발해왔기 때문에 알파벳만 표시하면 되었습니다. 알파벳이 26자인가 그러고 그 외 기호들을 포함해도 모두 합쳐도 256개면 충분하던 시기이죠. 그래서 그때 당시에 1 byte 문자 체계인 ANSI를 사용하기 시작합니다 (1 byte는 8 bits이고, 8 bits는 0 - 255까지 256개를 표현할 수 있습니다). 하지만 이 문자 체계는 중국, 일본, 한국등에서 컴퓨터를 사용하기 시작하면서 1개의 문자를 표현하는데 훨씬 더 많은 byte가 필요하게 됩니다. 한글의 경우는 초성, 중성, 종성을 합쳐서 모두 약 11,000개에 달하는 문자가 존재했으며, 중국과 일본의 경우는 저것보다 더 필요하게 된 것이죠. 우선 다른 나라는 잘 모르겠지만 한글의 경우는 ANSI를 그대로 두면서 2 bytes로 확장된 문자 체계를 표준으로 정하여 사용하기 시작했지요. 방법은 ANSI의 경우 최상위 1 bit가 항상 0 (양수)로 표현했지만, 한글은 최상위 1 bit를 1로 두고 (음수) 2 bytes로 확장해서 한글을 표현하기 시작한 것이죠. 불행히도 당시 2 bytes의 한글 표준은 한글의 초성, 중성, 종성의 조합 형태를 제대로 표현하지 않고, 그냥 마구잡이로 순서를 붙여서 사용했었습니다. 물론 표준이 아닌 조합형 표현이 있기는 했습니다만, 표준이 아니었기 때문에 널리 사용되지 못합니다. 이러한 표현 방법을 MBCS (Multi Byte Character Set인가...)라고 부릅니다. MBCS 하에서는 ANSI와 한글이 동시에 표현되면서 가변길이로 문자가 표현되게 됩니다. 즉 한개의 바이트를 읽었을 때 양수이면 1 byte만 읽으면 되고, 음수이면 그 다음 바이트도 빼내어야지 문자를 한개씩 읽어나갈 수 있게 되는 거죠.

문제점 중 하나는 이러한 음수 표현을 이용한 확장이 한국만이 아니라 중국, 일본 및 해외에서 빈번하게 사용되면서 같은 값이 다른 문자를 표현하게 된 것이죠. 즉 한글로 작성된 문서를 중국으로 가지고 갔을 때 그 문서가 한글이라는 사실을 알지 못하면, 뭔지 전혀 알 수 없게 된 것입니다. 또한 세계의 언어를 하나의 어플리케이션에서 동시에 표현하는 것도 거의 불가능에 가까웠습니다.

그래서 나온 것이 세계의 문자를 표준으로 만들자이고, 그로 인해서 Unicode가 만들어지게 됩니다. 초기에는 2 bytes (0 - 65535까지 65536개)면 충분할 것으로 보고 2 bytes Unicode 표준안이 만들어지게 됩니다. 자랑스럽게도 어떤 분께서 2 bytes 표준안에 한글의 코드값 거의 12,000개에 달하는 영역을 얻어내는데 성공합니다. 즉 전체의 거의 1/5을 한글 코드 값이 차지하고 있다고 할까요... 중국 글자는 한글에 비하면 거의 수십배 많은데도 한글이 저 정도를 차지하고 있습니다. -_-b. 또한 2 bytes Unicode 체계에서 한글은 조합형이 아니지만, 조합형처럼 사용할 수 있게끔 잘 구분되어 있어서 초성, 중성, 종성이 분리가 가능합니다!!!! 이것에 대해 궁금하신 분은 제가 오래~~ 전에 쓴 글인

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=7022&ref=7022

위 링크에 가시면 초성, 중성, 종성 분리에 대한 글을 보실 수 있습니다. 어쨋든 다시 유니코드로 들어와보면 2 bytes 체계를 제외하고도 3, 4 bytes, 가변길이 체계등 여러가지 표준안이 있습니다. 그 중에서 2 bytes 체계는 한글, 중국어 (거의 대부분), 일본, 라틴, 기타등등을 거의 다 포함하고 있어서 자주 사용되고 있지요. 그리고 Visual Studio로 프로젝트를 새롭게 만들게 되면 2 bytes 유니코드 체계로 항상 프로젝트를 만들게 됩니다. 그러므로 3, 4 바이트 체계는 우선 가볍게 무시하고 2 bytes만을 대상으로 이 글을 써봅니다요.

정리하자면 유니코드는 다음과 같은 장점과 단점을 가지게 됩니다.

장점.
1. 전세계 문자를 동시에 표현할 수 있다.
2. 문자 처리하는 속도가 빨라진다.
3. 표준이다.

단점
1. 차지하는 메모리 양이 늘어난다.
2. 유니코드와 ANSI, MBCS와의 호환성에 대해서 고민을 해줘야 한다. 오래된 라이브러리는 오직 MBCS나 ANSI만을 지원하기 때문

따라서 장점이 단점을 넘게 되면 사용해볼만 합니다요!

자 이제부터 사용법에 대해서 다뤄보겠습니다.

유니코드는 일반적으로 아예 다른 문자 값을 표현하는 방법이기 때문에 문자열과 문자를 표현할 때 조금은 다르게 쓰게 됩니다.

지금까지 일반 문자열을 "abc", 일반 문자를 'a'와 같이 표현하고, "a아아아"와 같은 표현에서는 자연스럽게 MBCS로 표현하고 있습니다만, Unicode를 쓰기 위해서는 다르게 써야 합니다.

"abc" -> L"abc"
'a' -> L'a'

위와 같이 앞에 L을 붙여주게 되면 유니코드 표현이라는 것을 컴파일러에 알려주는 것이죠. 그래서 컴파일러는 해당하는 문자나 문자열을 유니코드로 인식하게 됩니다. 유니코드를 위한 데이터 타입은 char가 아닙니다.

wchar_t가 공식적인 유니코드 문자와 문자열을 위한 데이터 타입이며, 일반적으로 2 bytes입니다. 음. 이 부분은 향후에 어떻게 바뀔지는 좀 모르겠습니다만, 현재까지 Visual Studio 2008과 2010에서는 2 bytes입니다.

또한 문자열을 화면에 출력하고 싶다면 기존에 printf 또는 cout를 많이 사용하셨겠지만, 이 부분도 달라져야 합니다. 타입이 다르니까요.

wprintf(L"유니코드 출력!\n");
std::wcout << L"유니코드 출력" << std::endl;

흐음... 물론 저렇게 프로그램을 처음부터 만들어가면 아예 유니코드 기반으로 만들어갈 수 있습니다. 하지만 기존의 많은 프로그램 코드들이 유니코드 기반이 아니기 때문에 호환성이 많은 문제가 됩니다.

가장 단순한 해결책이자 조금은 무식한 해결책은 변환이 필요한 곳에서 변환을 해서 값을 전달하고 또 받아올 때 다시 유니코드로 변환하는 방법입니다. 윈도우즈도 리눅스나 맥도 모두 유니코드와 ANSI, MBCS는 지원할터이니 모두 변환하는 함수는 갖추고 있습니다. 표준 함수도 존재하구요. 따라서 표준 변환 함수를 사용하셔서 필요할 때 바꾸면 됩니다.

윈도우즈 기반 제 코드를 좀 보여드리자면 헤더 파일에는...
const char* ToMbcs(const char* psz);
const char* ToMbcs(const wchar_t* psz);
const wchar_t* ToUnicode(const char* psz);
const wchar_t* ToUnicode(const wchar_t* psz);
const TCHAR* ToTString(const char* psz);
const TCHAR* ToTString(const wchar_t* psz);

소스 코드에는
__declspec(thread) static const int    BUFFER_SIZE = 4096;
__declspec(thread) static char        g_MbcsBuffer[BUFFER_SIZE];
__declspec(thread) static wchar_t    g_UnicodeBuffer[BUFFER_SIZE];

const char* ToMbcs(const char* psz)
{
    return psz;
}

const char* ToMbcs(const wchar_t* psz)
{
    g_MbcsBuffer[0] = '\0';
    WideCharToMultiByte(CP_ACP, 0, psz, -1, g_MbcsBuffer, BUFFER_SIZE, 0, 0);
    return g_MbcsBuffer;
}

const wchar_t* ToUnicode(const char* psz)
{
    g_UnicodeBuffer[0] = L'\0';
    MultiByteToWideChar(CP_ACP, 0, psz, -1, g_UnicodeBuffer, BUFFER_SIZE);
    return g_UnicodeBuffer;
}

const wchar_t* ToUnicode(const wchar_t* psz)
{
    return psz;
}

const TCHAR* ToTString(const char* psz)
{
#ifdef _UNICODE
    return ToUnicode(psz);
#else
    return ToMbcs(psz);
#endif
}

const TCHAR* ToTString(const wchar_t* psz)
{
#ifdef _UNICODE
    return ToUnicode(psz);
#else
    return ToMbcs(psz);
#endif
}

위에서 TCHAR에 대해서는 글 조금 더 읽으시면 나올 예정이며, __declspec(thread)가 뭔가라고 궁금하시면 이것은 Thread Local Storage (TLS)입니다. 이 부분은 또 다른 영역이니 다른 글에서 다뤄보지요.

제가 사용한 함수는 MultiByteToWideChar와 WideCharToMultiByte입니다. 윈도우즈 기반 함수이니 필요하시면 표준 변환 함수로 바꾸어 쓰시면 됩니다.

기존의 코드를 Unicode와 MBCS(ANSI)와 호환시키기!

Windows의 경우는 아래의 헤더 파일을 포함시키는 것이 좋습니다. 왜냐하면 MBCS와 Unicode의 변환을 매우 편리하게 해줄 수 있는 다양한 매크로가 존재하기 때문이죠.
#include <tchar.h>

위 헤더 파일 안을 들어가보면 다양한 define들이 들어가 있습니다. 너무 길어서 보기 힘들기에 제가 간단히 정리해보겠습니다.
#ifdef _UNICODE
#define _tprintf wprintf
#define _sntprintf _snwprintf
#define _ttoi atoi

#define TCHAR wchar_t
#define _T( x ) L##x

#else
#define _tprintf printf
#define _sntprintf _snprintf
#define _ttoi _wtoi

#define TCHAR char
#define _T( x ) x

#endif

즉 만약 _UNICODE라는 매크로가 정의가 되어 있다면 _tprintf는 컴파일 하기 전에 wprintf로 해석되게 됩니다. 만약 정의되어 있지 않으면 일반 printf로 해석되게 되지요. 이러한 정의는 단지 printf에 대해서만이 아니라 문자열 관련 거의 모든 함수를 _snprintf의 경우는 _sntprintf로, atoi는 _ttoi로 등등 매크로로 변환을 정의해놨습니다.

위에서 보는 바와 같이 _tprintf 등을 사용하면 Unicode건 MBCS(ANSI)건 신경쓰지 않고 사용할 수 있다는 장점이 있습니다. 문자열 표현에 대해서도 마찬가지로 아래와 같이 쓰면 신경쓰지 않을 수 있지요.

"A" -> _T("A")
L"A" -> _T("A")

위와 같이 쓰면 _UNICODE 매크로에 따라서 유니코드 문자열이 되었다가 다시 MBCS(ANSI) 문자열로 되었다가 변환되게 됩니다.

TCHAR ch = _T('a');

그럼 _UNICODE 매크로는 어디에 선언되어 있을까요? Visual Studio의 경우는 직접 선언하는 것이 아니라 프로젝트에서 마우스 오른쪽 클릭 후 나오는 Property에 가면



위와 같이 보시면 Unicode를 사용할지 MBCS를 사용할지 아님 ANSI를 사용할지에 대해서 선택할 수 있게 되어 있습니다. 리눅스나 유닉스의 경우는 제가 모르겠지만 가능하다는 사실은 알고 있습니다.

One more thing...

그럼 우리라고 위와 같은 확장을 하지 말란 법이 없습니다. 그래서 저는 아래와 같이 주로 사용하고 있지요.
#ifdef _UNICODE
namespace std
{
    typedef basic_string<wchar_t>            tstring;
    typedef basic_istream<wchar_t>            tistream;
    typedef basic_ostream<wchar_t>            tostream;
    typedef basic_fstream<wchar_t>            tfstream;
    typedef basic_ifstream<wchar_t>            tifstream;
    typedef basic_ofstream<wchar_t>            tofstream;
    typedef basic_stringstream<wchar_t>        tstringstream;
    typedef basic_istringstream<wchar_t>    tistringstream;
    typedef basic_ostringstream<wchar_t>    tostringstream;
}
#else
namespace std
{
    typedef basic_string<char>                tstring;
    typedef basic_istream<char>                tistream;
    typedef basic_ostream<char>                tostream;
    typedef basic_fstream<char>                tfstream;
    typedef basic_ifstream<char>            tifstream;
    typedef basic_ofstream<char>            tofstream;
    typedef basic_stringstream<char>        tstringstream;
    typedef basic_istringstream<char>        tistringstream;
    typedef basic_ostringstream<char>        tostringstream;
}
#endif

std::tstring으로 사용하면 string과 wstring가 매크로에 맞춰서 정의되게 되지요. 위와 같은 것을 제외하고서도 많은 유니코드 관련 중요한 점들이 존재합니다. 그리고 현재 프로젝트에 해외 퍼블리싱이 필요하다라고 생각되신다면 과감히 유니코드에 도전해볼 수 있겠지요.
반응형
,
Posted by ozlael
들어가며

 현재 전 세계적으로 3D 입체 영상의 붐이 일고 있고, 게임업계도 이에 편승하여 입체 영상을 지원하는 게임들을 발매하고 있습니다. UBI 소프트는 2012년에는 절반 이상의 타이틀이 정식으로 입체 3D를 지원할 것이라고 발표하기도 하였습니다. 실제로도 스타크래프트 2, 크라이시스 2, 베틀필드 3 등 최근 출시작들은 거의 다 입체 3D를 지원하고 있지요. 곧 있으면 나올 디아블로 3도 입체 영상을 지원하는 것으로 알려져 있습니다. PC 뿐안 아니라 엑박,플스 등 콘솔 게임기도 3D 입체 영상을 지원하지요. 닌텐도는 3DTV 보급률이 20%를 넘었을 시 콘솔에서 입체 3D를 지원할 예정이라고 발표하였지만, 휴대기기에서는 닌텐도 3DS를 발표할 만큼 적극적인 관심을 보이고 있지요. 또한 게임기 뿐만 아니라 LG,HTC,샤프 등에서 입체 3D를 지원하는 스마트폰을 출시하며 각종 게임들과 연계하고 있지요. 노트북도 지원 모델들이 본격적으로 출시되기 시작했습니다. 이토록, 입체 3D 게임은 거실뿐만아니라 모바일까지 그 영역을 확대해 나가고 있습니다.


하지만 이에 비해서 모든 게이머가 입체 3D ( 이하 Stereo 3D 혹은 S3D라 일컫겠습니다) 게임을 선호하지는 않지요. KOCCA의 보고서를 보면 29%만이 S3D 게임을 선호하고 있습니다. S3D가 전혀 필요 없어보이는 TV쇼보다 그나마 높은 수준정도밖에 되지 않지요. 

장르 별 Stereo 3D 선호도


왜 그런 것일까요? 안경을 끼고 시청해야 하는 불편함도 있겠지만은 멀미나 피로감 등에 그 주된 원인이 있다 볼 수 있겠습니다. 영화는 길어봐야 2시간의 러닝타임을 가지고 있고 어느정도 그러한 피로감을 감수할만 하겠지만, 게임은 그렇지가 않지요. (원래 권장은 2시간입니다만;;) 장시간동안 피로감과 멀미 등의 휴먼 팩터에 노출되기 때문에 사용자들은 거부감이 들 수 밖에 없게 되는 것입니다. 이러한 휴먼 팩터들은 아직도 활발히 연구가 되고 있고, 게임 개발자들 또한 이를 이해하고 다루어야 할 것입니다.


동요병

 흔히 멀미라 불리우는 동요병(motion sickness)은 흔들림이나 회전에 의해 불쾌감, 구토, 식은땀 등을 동반합니다. 이 동요병은 S3D 영상의 휴먼 팩터 중 하나지요.
 우리는 그동안 학교에서 배운 멀미의 원리는 내이과잉자극설(over stimulation theory)이라고 합니다. 전정 감각이 신체의 평형감각에 중요한데, 열차 및 자동차의 진동에 의한 가속도가 달팽이관이 있는 내이를 무리하게 자극한다는 것이죠. 


감각불일치설
.
 하지만, 가만히 앉아서 게임을 하는데도 멀미를 느끼게 됩니다. 이러한 영상 멀미는 감각불일치설(sensory conflict theory)로 설명이 되고 있습니다. 신체는 평소에 자신의 위치나 움직임 등을 다양한 감각 정보로 취득합니다. 하지만, 영상만 보면 신체정보와 눈으로 입력되는 패턴이 일치하지 않게 되지요. 이러한 정보들을 짜집기하면서 동요병이 발생을 하게 되는 것입니다. 특히 FPS 등 움직임이 급격한 게임이 감각불일치로 인한 영상 멀미가 쉽게 일어나지요. 이러한 영상 멀미가 있는 상태에서 S3D로 게임을 즐기게 되면 안정피로(asthenopia)가 더해져서 그 부작용은 더더욱 커지게 됩니다.


안정피로

지속적인 시각 작업이나 집중을 하게되면 피로해지고 눈이 침침해지며 통증, 두통, 어깨결림을 수반하게 되고 심하면 구토 증세까지 보이기도 합니다. 이러한 현상을 안정피로라고 합니다. 극도로 정신이 긴장하여 발생하는 것은 신경성 안정피로(nervous asthenopia)로 분류됩니다. 게임에 너무 몰입해도 발생할 수 있는 현상이지요. 수정체의 과부하나 초점 조절로 인하여 생기는 것은 조절성 안정피로(accommidative asthenopia)로 분류됩니다. 기본적으로 신경성 안정피로가 발생하는 게임을 S3D로 영상을 보게되면 조절성 안정피로가 더해지는 것이지요.


폭주와 초첨의 불일치

사람이 사물을 바라볼 때는 두 눈의 주시선이 무한히 평행히 뻗어나가는 것이 아니라 한 지점에서 교차를 하게 됩니다. 그 교차점이 주시의 대상이 되는 것이고, 이러한 능력을 폭주 혹은 수렴이라 부릅니다. 그리고 눈의 수정체를 조절하여 초점을 맞추는 대상이 폭주 대상과 일치하게 됩니다.
 하지만 S3D 영상을 보게되면 이 폭주 대상과 초점 대상이 어긋나게 됩니다. 사람의 물리적인 초점은 모니터 스크린에 맺히게 되지만 폭주 대상은 모니터의 위치가 아닌 입체 공간의 어딘가가 되는 것이죠. 이러한 증상을 폭주와 초점의 불일치(Vergence-accommodation mismatch)라 부르는데, 이러한 불일치를 조절하게 되면서 조절성 안정피로가 일어나게 되는 것입니다.



마치며

이러한 영상멀미나 안정피로등은 개인차가 큽니다. FPS를 해도 어떤 사람은 멀미를 일으키지만 어떤 사람은 하루 종일 해도 멀미가 일어나지 않기도 하지요. S3D 영상은 그 개인차가 더 심하게 나타납니다. 영상 왜곡까지 더해지면 그 후폭풍은 감당하기 힘든 수준이 되지요. 이러한 휴먼 팩터들은 게임 뿐 아니라 모든 장르의 S3D 시장 발전의 저해 요소가 되고 있습니다. 그렇기때문에 앞서 말씀드렸다시피 활발한 연구가 이루어지고 있고 표준 규약 또한 만들어지고 있지요. 특히 영화나 에니메이션 등 영상 컨텐츠는 영상의 깊이감을 많은 사람들이 평균적으로 수용 가능한 값으로 설정하고 있습니다.
 하지만 게임은 이러한 점이 다소 자유롭습니다. 사용자가 영상의 깊이감을 조절을 할 수 있기 때문이지요. 사용자는 자신의 팩터에 맞게 깊이감을 조절하여 멀미나 피로를 최소화 하여 게임을 즐길 수 있습니다. 그렇기때문에 개발자는 이러한 사용자의 조절을 반드시 지원 해 주어야 할 것입니다.


예고 : 다음 시간에는 S3D의 영상 왜곡에 대해 다루도록 하겠습니다.
반응형
,
Posted by 밥을먹는선비
2강이라고 하기엔...준비가 좀 부족했습니다.
요즘에 갑자기 학교 특강을 맡아버리는 바람에 아무정신없이 어느덧 2주가 훌쩍 지나 버렸네요.  ㅜ.ㅜ;

매월 6일과 21일에 글을 올리기로 되었는데 처음부터 쉽지않네요. 다소 준비가 부족한 면이 있어도 양해 부탁드립니다. 다음부터는 꼭 스캐쥴을 배분을 잘해 두도록하겠습니다.

전체 구조요약

일리히트엔진구조를 전체적으로 요약하자면 일단 네임스페이스별로 irr 을 루트로 하여 그밑으로 천하는 중원의 조조가 다스리는 core, 서촉의 유비가 다스리는 video, 강동의 손책이 다스리는 scene, 북방의 원소가 있는 gui, 남만맹획 의 io  5개의 나라로 나누어져있습니다.(그냥 농담으로 비유한것이니 넘 심려치 마시길...)

암튼 중요한것은 크게 5개로 나누어진다는것입니다.  

irr 
|
+--core
+--video
+--scene
+--gui
+--io


irr 루트에는 가장 중요한 디바이스(

irr::IrrlichtDevice

) 객체가 있습니다.

일리히트엔진의 디바이스란 랜더링디바이스를 추상화 한것이 아니라 시스템 전체(os 포함)를 추상화 한것입니다. 랜더링 디바이스(비디오카드 같은...)는 video 네임스페이스로 따로 나뉘어져있습니다.
device는 전체 시스템을 추상화 한것입니다, 달리 말하면 가상의 기계라고 보셔도 됩니다. 이것을 보고 예전에 어떤분이 프레임웍이 내장된 엔진이라고 평을 하던데 전 개인적으로는 플랫폼이 내장된엔진이라는 표현이 더 맞을거같다는 생각이 들더군요.

디바이스에 종속적인 부분은 바로 이부분에서 모두 다뤄지게됩니다. 예를 들어 안드로이드 상에서 일리히트엔진을 돌리고싶다면 안드로이드용 디바이스를 irr::IrrlichtDevice 에서 상속받아 만들어주면됩니다. 나머지 네임스페이스로 나누어진 부분들은 모두 코드 수정할 필요가 전혀없습니다.


이름 그대로 중심의 core에 나머지 네개가 모두 종속 되어져있습니다. 반데로 말하면 core는 나머지 4개가 어떻게 바뀌던 상관없지만 core가 수정이 되면 나머지 네게 모두 영향을 받게 됩니다.(사실 그런 부분만 따로 떼어 놓은것이 core 입니다.)
core의 내용물은 벡터,행렬등의 수학관련 클래스와 array,map,string등의 컨네이너클래스들 입니다. 예전에 이 부분만 따로 떼어서 겜브리오 엔진에 붙여서 사용한적도 있을 정도로 정리가 아주 잘되어있습니다. 
만약 일리히트엔진을 다른언어등으로 포팅하려할때도 이 부분을 먼저 포팅하는것이 순서일것입니다.

io 는 코어의 컨테이너 부분을 사용합니다. 주로하는 역활은 파일처리와 더불어 항상성 유지 관리 기능을합니다. 엔진의 현재 모든 상태(씬트리, 메트리얼)를 그대로 xml로 직렬화해서 내보낼수있으며 반대로 읽어 들일수도있습니다.

video 는 core 의 수학클래스와 컨테이너를 사용하며 또 io의 파일처리 부분을 사용합니다. video국의 주력 군대는 dx, opengl 같은 랜더러를 추상화 합니다. 

scene,gui는 둘다 video에 종속되어있습니다. 자세한 내용은 두나라를 정벌할때 다시 이야기 하도록 하겠습니다.


이번에는 여기서 마칠까합니다.


ps.
다음회 부터는 본격적으로 5나라를 하나씩 정복하겠습니다.

새해 복 많이 받으시고 모두다 천하통일의 대업을 이루시길기원합니다.

모두들 천하통일하는 동안 그럼 소는 제가 키우겠습니다.




 
반응형
,
Posted by 알 수 없는 사용자

안녕하세요.  라오그람이란 필명을 사용하는 김효진 입니다. 저는 서버 개발을 하고있구요, 원래는 게임 서버 개발자이지만 현재는 게임쪽이 아닌 잠시 다른 서버 분야를 개발하고 있습니다.  없는 실력에 네임드 분들 사이에서 글을 쓴다는 것이 영광스럽기 그지 없네요ㅠㅠ 앞으로 더욱 열심히 공부해서 좋은 글을 쓰도록 하겠습니다.
 

 

 제가 이번에 다루어 보고자 하는 주제는 구글에서 만든 TCMalloc입니다



 보통 멀티 스레드 환경의 서버를 만들다 보면 메모리 풀을 사용하게 됩니다. 메모리 풀의 이점은 다음과 같습니다.


첫째, 빠른 메모리 할당.

둘째, 메모리 단편화 감소.

하나의 커다란 메모리 풀을 사용하면 단순하게 malloc을 호출해서 메모리를 할당하는 것 보다 속도가 빠릅니다. 메모리 풀에 따라 다르겠지만 일반적으로 볼 때 처음 프로그램이 실행될 때 필요한 메모리를 통째로 잡아놓고 메모리가 필요한 경우에는 잡아놓은 메모리 안에서 잘라서 주기 때문이죠. 하지만 멀티 스레드 환경에서는 메모리 풀도 속도가 저하됩니다. 왜냐하면 여러 스레드에서 하나의 메모리 풀에 접근을 하게 되면 lock에 대한 cost가 발생합니다. 동시에 접근하는 스레드가 많을 수록 lock을 기다리는 시간이 많아지고 그러다보면 메모리를 얻기 위하여 기다리는 스레드들은 일을 할 수 없게 됩니다.

 lock cost로 인한 속도 저하는 또 다른 방법으로 해결 할 수 있습니다.

thread별로 따로 메모리 풀을 두고 관리하고 
다른 thread에서 접근 해야 하는 메모리는 전역 메모리 풀을 두어 관리하고… 
뭐 이런 식으로 메모리 풀을 설계하면 되죠. 


하.지.만 

귀찮잖아요? 


그래서 친절하신 구글에서 우리들을 위하여 TCmalloc(Thread Caching malloc)이란 이름으로 메모리 할당하는 라이브러리를 내어 놓았습니다.

 

 이름도 거창하게 Thread Caching을 달고 나오신 TCmalloc은 메모리를 Thread Local Cache와 Central Heap으로 나누어서 관리합니다. Thread Local Cache는 32K이하의 작은 오브젝트 들을 담당하며 메모리가 부족할 시에는 Central Heap에서 메모리를 얻어와서 할당합니다. 그리고 32K가 넘어가는 큰 오브젝트들은 Central Heap에다 4K의 페이지 단위로 나누어서 메모리 맵을 이용하여 할당합니다. 사실 단순한 Central Heap은 일반적으로 사용하는 메모리 풀과 다를 바 없습니다. 하지만 Thread Local Cache가 있음으로 해서 메모리 할당시 불필요한 동기화가 줄어들게 되고 그에 따른 lock cost가 꽤 많이 감소되어 성능이 향상됩니다.

 특히 리눅스 환경에서는 pthread_mutex_t가 거지같이(?) 느리기 때문에 개인적으로는 꽤 많은 향상을 얻을 수 있었습니다. 자체적으로 만든 memory pool과 비교해보고 나서 '나는 무엇을 하고 있었나' 라는 심한 좌절감을 느꼈었죠Orz...


 TCmalloc의 사용법은 정말 간단합니다. 다운로드를 받고 설치를 한 후 라이브러리를 링크 해주면 끝납니다. 헤더파일 Include이런 것 전혀 필요 없습니다. 단지 TCmalloc 라이브러리를 링크해주는 것 만으로 malloc이 TCmalloc으로 바뀌는 마법 같은 효과를 얻을 수 있습니다.

 

구글에서는 이렇게 말하고 있습니다. 

- TCmalloc을 사용하려면 Linker flag에 "-ltcmalloc"을 넣으면 된다. 

- 따로 컴파일 할 필요 없이 $ LD_PRELOAD="/usr/lib/libtcmalloc.so"를 해주는 것으로도 효과를 볼 수 있다. 하지만 이것은 편법이기 때문에 추천하지는 않는다.'


단순하게 -ltcmalloc을 넣는 것 만으로 메모리 할당에서 20%이상의 성능향상을 얻을 수 있다니 참 쉽죠?
(윈도우에서는 
Project Dependencies에 추가해 주시면 됩니다) 

더불어 TCmalloc을 사용하면 구글에서 제공하는 heap checkerheap profiler까지 덤으로 사용할 수 있게 됩니다. 


< 스레드 수와 메모리 용량에 따른 TCMalloc과 PTMalloc이 CPU초당 처리할 수 있는 횟수 비교>
Mops : Millions operations per second 
 



출처 : Google TCmalloc Document에서 발췌
* 자세한 내용은 링크를 따라가시면 보실 수 있습니다.


위의 표를 보면 Single Thread에서는 크게 효과를 보지 못합니다. 하지만 Thread의 수가 늘어날 수록, 메모리 단위가 작을 수록 TCmalloc이 효율이 더 뛰어난 것을 보실 수 있습니다.

위의 내용이 길고 두서없어서 읽기 귀찮으신 분들을 위하여 간단하게 요약을 하자면...

1. Google Performance Tools Home에서 Google Performance Tools를 다운 받는다.
2. INSTALL파일을 읽어보고 자신의 환경에 맞게 설치한다. 
3. Library를 링크시키고 사용한다.
4. 성능 향상이 일어난다!!!!



 아직 게임 서버에는 적용을 해 볼 수 없었지만 현재 하고 있는 프로젝트에서는 memory allocator를 TCmalloc으로 대체하고 난 이후 평균적으로 20%정도의 성능 향상이 일어났고 메모리 할당에 대한 속도 저하 걱정을 많이 덜 수 있었습니다.  요즘은 유저들의 PC가 좋아짐에 따라 클라이언트들도 꽤 많이 멀티스레드를 지향하고 있다고 들었습니다.

몇 분의 투자로 20%의 성능 향상. 
같이 누려보시는 것은 어떨까요? 
반응형
,