태그 : multicore

팀 스위니 CEDEC 2008에 대한 소고

http://blog.naver.com/saebaryo/30035653812 으로부터 트랙백

작년 CEDEC에서의 팀 스위니의 강연 내용을 잘 번역해 놓은 글이 있어 소개합니다. (작년에 네이버 재팬의 번역기로 본 내용이지만 새로 깔끔하게 번역해서 올라와서 소개할 겸 몇자 덧붙여 트랙백도 함께 보냅니다.)

강연 내용을 본 대부분의 사람들이 이야기했던 충격적인 내용은 소프트웨어 렌더러로의 회귀였고 (본문 내용에는 자세하게 나와 있지는 않습니다만 회귀가 DX와 같은 하드웨어 가속 플랫폼 레이어의 사멸을 의미하지는 않으며 아마도 공존하는 형태가 될 것이라고 덧붙인 것으로 알고 있습니다.) 개인적으로 관심 있었던 부분은 멀티 코어에 맞는 새로운 언어의 등장입니다. 강연에서 언급했는지의 여부는 알 수 없습니다만 게임 개발에 효율적인 언어의 필요성 때문에 언리얼 스크립트를 개발했던 만큼 (Haskell을 이야기하고 있긴 하지만) 멀티 코어에 맞는 새로운 언리얼 스크립트를 개발할 가능성도 있다고 봅니다.

생산성과 관련한 이슈는 이미 시작된 것이고 앞으로는 아마 더욱 심해지리라 봅니다. 이는 직접적으로는 게임 엔진이나 툴의 변화를 가져오겠지만 컴퓨팅 환경의 변화에 따른 것이므로 그래픽스와 관련한 산업 전반에 영향을 미친다고 봐야 할 것입니다. 그래서 개발 공정(아트 파이프라인 등)에도 기존과는 확연히 구분되는 새로운 혁신이 등장하는 일이 필연적이라 예상됩니다.

...그런데 이런 추세라면 개발자도 앞으로는 더욱 세분화되지 않을까요? 2000년대 초반 이후로 그래픽 하드웨어의 발전은 게임계로로 다른 산업으로부터의 인력 유입과 같은 타 산업과의 컨버전스가 가속되는 촉매 역할을 했는데 이러한 현상이 앞으로 더욱 가속화될 것 같습니다.

...음, 그러니까 요약하면 먹고 살기가 더 힘들어지지 않을까라는 생각...ㅡㅡ;


ps. 그런데 강연 장소가 쇼와 여.자. 대학이군요, 흐음~ (팀 스위니 형님, 아마 미혼이죠? 쿨럭)

by kimsama | 2009/05/04 01:05 | MultiCore | 트랙백 | 핑백(1) | 덧글(2)

멀티코어와 스트리밍 - 압축과 메모리 관리


개인적으로 게임에서 스트리밍(streaming)의 중요성과 대단함을 처음 알았던 때는 던전시즈를 끝냈을 때였다. 게임을 다시 시작하지 않는 한 로딩 없이 엔딩까지 진행할 수 있었던 게임, 그 당시의 하드웨어 환경을 사용하면 참 놀라운 일이다.

이제 멀티 코어 환경의 엔진에서 스트리밍은 지원하는 것이 당연한 기본적인 특징이 되었다. 사실 다른 처리에 비하면 스트리밍은 쉬운편에 속한다. 다른 쓰레드의 잡(job)을 방해하는 일 없이 워크 쓰레드(work thread)를 사용해서 손쉽게 처리할 수있는 경우가 대부분이기 때문이다. 이 글에서는 스트리밍 지원에 있어 고려해야할 데이터 압축 몇 가지와 또 스트리밍시 간과해서는안될 메모리 관리자 설계에 대해서 이야기한다.

스트리밍 처리에 있어서 중요한 것은 데이터의 크기이다. 품질을 희생하지 않고 데이터의 크기를 줄이는 일은 백그라운드에서 계속 로딩해야 하는 스트리밍의 특성상 매우 중요하다. 여기에 응용할 수 있는 방법이 바로 압축이다. 스트리밍이 필요한 데이터는 보통 다음의 것들이 있다

  • 메쉬 데이터
  • 애니메이션 데이터
  • 텍스쳐 데이터- 노말 텍스쳐
메쉬 데이터는 다시 정점 컴포넌트들로 구분되며 이들 정점 컴포넌트는 컴포넌트의 특성에 따라 다른 압축 방법을 사용해야 한다.[1]

www.infopub.co.kr/ebook/pdf/5674-214.pdf

최근에 사내에서 메쉬 데이터를 압축해서 사용해 보니 압축율이 무려 55%나 되었다.(100메가인 경우 55메가가 되었다는 이야기.압축한 정점 컴포넌트는 normal, color, uv, bi-normal, tangent, joint indices,weight 값들이다.) 이 정도면 왜 압축을 안하는 것이 이상할 정도. 메쉬 데이터의 압축에 대한 이야기는 Floh씨의블로그에서도 찾을 수 있다.

http://flohofwoe.blogspot.com/2008/03/vertex-component-packing.html

(※한가지 의문이 드는 점은 ShaderX1에서는 uv 값을 패킹하는 경우 피해야 할 방법에 대해서 이야기하고 있는데  위의 블로그포스트에서 이야기하는 DrakenSang에서 사용한 방법은 바로 그 피해야할 방법을 사용하고 있다는 점이다.)

또 cateu님의 블로그에도 Vertex Component Packing이라는 제목으로 좋은 내용이 있으니 읽어 보길 권한다.



두 번째로는 애니메이션 데이터의 압축이 있다.  애니메이션 데이터의 압축 방법 중 하나는 캐릭터의 바운딩 박스에서 데이터의 오프셋을 계산해서 사용하는 방법이 있다.

http://ati.amd.com/developer/shaderx/shaderx_characteranimation.pdf 
(Compression 섹션 참조)

마지막으로 텍스쳐 데이터의 압축과 관련된 이슈는 여러가지 이슈가 있는데 요즘에는 대부분 DDS 포맷을 사용하므로 기본적으로 압축된포맷을 사용하는 셈이다. 여기에 한가지 더 고려해 볼만한 것은 노말맵 텍스쳐 데이터의 압축이다. 노말값은 [0, 1]사이의 값을가지므로 이것을 이용해서 노말맵 텍스쳐 데이터의 압축을 시도해 볼만한다.

http://benjaminnitschke.com/2005/09/07/NormalMapCompressorV13AndPDCInformation.aspx


그런데 스트리밍을 지원할 때에는 반드시 함께 고려해야할 중요한 것이 한가지 있는데 바로 메모리 관리이다. 스트리밍을 통해서 (메모리로) 읽어 들인다는 것은 다른 한쪽에서는 사용하지 않는 리소스를 메모리에서 내린다는 이야기가된다. 열역학의 법칙 - 에너지의 총량은 일치해야 한다 - 은 여기에도 적용이 된다. 들어간 만큼 다시 집어 넣어려면 그만큼빼야 한다.

메모리 관리를 직접 한다고 해서 스트리밍에서 메모리와 관련해서 발생하는 모든 문제들을 해결할 수 있는 것은 아니다. 예를 들어 게임 내에서 섹터들을 백그라운드 로딩을 사용해서 읽어 들이는 경우를 생각해 보자. 각 섹터마다 정해진 메모리량에 맞추어 비슷하게 메모리를 차지하도록 배경을 디자인하는 일은 레벨 디자이너의 몫이지 프로그래머가 메모리 관리자로 해결해야 하는 일은 아니다. 하지만 정교한 메모리 관리자가 없는 경우에는 이러한 작업이 힘들 뿐만 아니라 문제가 발생해도 어디에서 어떤 문제가 발생했는지 알기가 매우 힘들다. 그러므러 스트리밍을 지원할 때에는 우선 엔진 전체에 걸친 메모리 관리 정책의 수립이 선행되어야 하고 이를 바탕으로 정교한 메모리 관리자의 작성이 필요하다.


최근의 게임들은 그 규모가 점점커지고 (콘솔 게임의 플레이 시간은 더 짧아지고 있지[2]) 복잡해지고 있다. 스트리밍은 더이상 고급 기술이 아니라 현세대 엔진이라면 당연히 지원해야 하는 기본적인 기능 중의 하나이다. 이 글에서는 스트리밍과 관련한 이슈 중 압축과 메모리 관리의 두가지 관점에서 간략하게 이야기했다. 레벨의 스트리밍과 관련해서 참고할 만한 좋은 글은 던전시즈에서 사용했던 방법에 대한 내용인"The Continuous World of Dungeon Siege"와 Saint Row의 레벨 에디팅[4]에 대한 글이있다.



[1] ShaderX1, "Vertex Decompression using Vertex Shaders "(세이더 안에서 정점 압축 풀기기), Dean Calver
[2] 개발 비용과 플레이 시간은 반비례한다는 사실에 대해서 'xxx의 법칙' 같은 것이 나올 법도 하다.
[3] Dungeon Siege, "The Continuous World of Dungeon Siege",
http://www.drizzle.com/~scottb/gdc/continuous-world.htm
[4] The Creation of Saints Row's Open World Cityscape, http://www.dev3d.net/bbs/data/pds/1188110066/saintsrow.pdf

by kimsama | 2009/05/03 23:00 | MultiCore | 트랙백 | 핑백(1) | 덧글(0)

멀티 코어와 메시징

멀티 코어는 여러 개의 쓰레드를 사용하게 된다. 이런 환경에서 가장 문제가 되는 것은 공유 데이터에 대한 접근 방법이다. 물리 시스템이 하나의 쓰레드를 사용하고 렌더링 시스템 역시 쓰레드를 사용하여 분리한 경우를 보자.

게임 루프에서 충돌이 일어난 후 물리 시스템은 충돌 이벤트에 대해서 게임 객체의 새로운 위치를 계산한다. 그리고 렌더링 시스템에서는 갱신된 새로운 위치 데이터를 사용하여 게임 객체를 렌더링한다. 그런데 이들 두 시스템이 각각의 쓰레드로 분리되어 실행되고 있다는 점이다.

일반적인 방법으로는 lock을 사용한다. 물리 시스템에서 게임 객체의 위치를 갱신하기 전에 다른 시스템에서 이 데이터에 접근하지 못하도록 lock을 건 다음 데이터를 갱신한다.

그런데 이 lock의 오버헤더가 만만치 않다. 또 이런 식으로 lock/unlock을 사용하면 전체 시스템이 복잡해지는 것은 두말할 필요도 없다. 행여 실수로 lock/unlock을 빠뜨리는 경우 시스템은 예기치 않은 오동작을 하게 된다. 더 큰 문제는이 문제가 바로 쓰레드와 관련한 문제라는 것이다. 복잡한 시스템에서 쓰레드의 문제는 해결하기가 매우 어려운 경우가 대부분인데다 심지어는 재앙을 초래하기도 한다.

그럼, 쓰레드를 사용해서 동시에 여러 개의 시스템을 실행할 때 이들 시스템 간의 데이터 공유 문제에 해결에는 어떤 방법이 좋을까?

바로 데이터를 복사하는 방법이다. 이것은 시스템이 서로 다른 시스템의 데이터에 접근하는 방법이 아니라 각 시스템이 시스템 내부에 공유 데이터의 복사본을 가지고 있고 메시지를 통해서 다른 시스템에 자기 시스템에서의 공유 데이터의 변경을 알리는 방법이다.

쓰레드 A에서 쓰레드 B의 함수를 바로 호출하는 방법도 사실은 다른 시스템의 데이터에 lock을 통해서 바로 접근하는 방법에 속한다. 메시지를 통해서 공유 데이터를 복사하는 방법은 이러한 lock을 사용할 필요가 없거나 혹은 최소한의 lock이 필요할 따름이다. 메시지는 특별한 형태가 아니라 일반적으로 알고 있는 바와 같이 다음과 유사한 형태로 처리할 수 있다.

...
//메시지 생성
Message::MoveMsg moveMsg = Message::MoveMsg::Create();

//메시지 값의 설정
moveMsg.Set(x, y, z);

// 메시지의 전송
moveMsg.SendAsync();
...

일전[1]에 망갈로 게임 프레임워크의 메시지 시스템의 장점 중에 하나로 멀티 코어 시스템에 편리하다고 이야기한적이 있었는데 바로 이런 이유에서다. 또 이렇게 메시지를 사용하게 되면 각 시스템을 서로 완전히 분리(decoupling) 시킬 수가 있는데 이것 역시 멀티 코어를 사용하여 각 시스템들을 동시에 실행시킬 때에는 매우 중요한 것 중의 하나이다.

컴포넌트 방식의 게임 프레임워크[2]에서 컴포넌트 간의 통신에 메시지를 사용하게 되면 이처럼 멀티 코어 환경에서도 잘 작동하는 유용하고 견고한 시스템의 설계가 가능하다.


[1] KGC 2008, Nebula2 Mangalore Game Framework에서
[2] GPG6, Component based Game Framework Library


...이렇게 메시지를 사용하게 되면 이제 메시지 전달의 순서에 대한 문제가 대두됩니다. 무슨 말이냐 하면 물리 시스템의 위치 메시지는 렌더링 시스템의 위치 메시지보다 먼저 처리되어야 합니다. 바꾸어 말하면 물리 시스템에서 계산된 위치 데이터를 계산한 다음 이 메시지가 렌더링 시스템에 전달되어야 순서에 맞습니다다. 이 부분은 또 다음에... ^^

by kimsama | 2009/05/03 18:17 | MultiCore | 트랙백 | 덧글(7)

멀티 코어란 무엇인가?

멀티 코어란 무엇인가?


멀티 코어(multi-core)는 멀티 코어 CPU를 이야기하는 것인데 이것은 두 개 이상의 독립 코어를 단일 집적회로로 이루어진 하나의 패키지로 통합한 것으로 듀얼 코어(dual-core) 프로세서는 두 개의 코어를 포함하고 있으며, 쿼드 코어(quad-core)는 네 개의 코어를 포함하고 있다.

현재 칩 제조사에서 출시하는 신형 CPU들은 이렇게 두 개 이상의 CPU를 내장한 멀티 코어 CPU가 주종을 이루고 있다. 이는 단일 코어에서 더 이상 코어 클럭을 이용해서 성능을 올리는 방법으로는 한계에 도달했기 때문이다. 그런데 이러한 하드웨어 환경에서 게임의 성능을 최대한 끌어 올리도록 만들기 위해서는 하드웨어에서 제공하는 여러 개의 코어들을 이용해서 병렬 처리(parallel processing)가 가능한 구조로 게임을 구성을 해야 한다. 왜냐하면 단일 쓰레드를 사용하도록 제작된 프로그램은 여러 개의 코어를 가진 프로그램 내에서 실행이 되더라도 속도가 빨라지지 않기 때문이다. 멀티 코어가 주는 성능을 최대한 이용하기 위해서는 응용 프로그램이 멀티 코어를 지원하도록 작성해야 하는데 문제는 이것이 쉽지 않다는 것이다.


왜 멀티 코어 프로그래밍이 어려운가?


우선 언어적 특성에 기인한 원인이 있다. 일반적으로 게임에 사용하는 C와 C++은 본질적으로 순차적 언어이기 때문에 병렬 처리가 당연한 문제의 경우에도 구현이 쉽지 않는 경우가 많다.

그런데 이런 언어 상의 문제점만 있는 것이 아니라 게임이라는 애플리케이션의 특징에 기인한 문제점도 있는데 이것은 더욱 복잡하고 까다롭다. 첫 번째로 다른 응용 프로그램과 비교할 때 게임은 인공지능, 물리 시뮬레이션, 네트워킹, 애니메이션, 렌더링 시스템 등과 같은 다양한 서브 시스템으로 구성이 되어 있는 것이 보통이다. 그런데 이들 서브 시스템은 서로 결합되어 작동되는 것이 보통이므로 단순한 서브 시스템의 조합으로 구성된 응용 프로그램과 비교할 때 훨씬 더 복잡한 구조를 가지고 있다. 즉, 서브 시스템 간의 의존도가 높기 때문에 병렬 처리하는 것이 어렵다. 두 번째는 게임이 이벤트 중심의 애플리케이션이라는 점이다. 게임은 사용자의 입력이나 네트워크를 통해 전송되는 패킷, 게임 내에 정의된 시나리오, 물리 시뮬레이션 등에 의해서 수시로 변경되며 서로 상호작용해서 작동되는 것이 보통이다. 그리고 이런 이벤트들을 전체 시스템의 관점에서 보면 정해진 규칙은 있지만 순서는 없다. 이러한 이벤트들은 여러 개의 서브 시스템에서 처리하여 그 결과를 이용하게 되는데 게임에서는 이렇게 각 서브 시스템간에 데이터 정보를 공유해야 하므로 서브 시스템들을 독립적으로 병렬 처리하는 것이 어렵다.

그리고 세 번째로는 게임의 실시간성에 따른 프레임에 관한 문제이다. 게임은 실시간으로 작동되어야 하기 때문에 게임은 매 프레임을 순차적으로 처리해서 렌더링 하게 되는데 현재의 프레임은 이전 프레임에 의존적이기 때문에 프레임들을 병렬 처리할 수가 없다. 이것도 게임을 병렬 처리 하기 어려운 이유 중의 하나이다.


그러면 멀티 코어 프로그래밍은 어떻게 접근해야 할까?


멀티 코어 시스템은 여러 개의 프로세서를 처리하므로 프로세서 간 통신을 위한 효율적이고 유연한 소프트웨어 아키텍쳐를 필요로 한다. 이 소프트웨어 아키텍쳐란 각 서브 시스템 간의 교환되는 데이터의 동기화가 용이한 모델에 초점을 맞춘 것을 말하는 것으로 데이터의 동기화 뿐만 아니라 코어의 개수가 서로 다른 환경에서도 안정적으로 작동될 수 있어야 한다. 이러한 병렬 처리 데이터 동기화의 모델 중에서 게임에 적합한 모델로는 비동기화 함수 병렬 모델(Asynchronous parallel model)이라는 것이 있다. 게임에서는 보통 게임 로직, 물리 연산, 렌더링의 순서로 작업이 행해 것이 보통인데 비동기화 함수 병렬 모델에서는 이 세 가지를 각각의 코어로 실행하고 이들 코어간의 동기화는 최신 결과를 기다리지 않고 이전 결과를 가지고 작업을 수행하는 것이다. 예를 들어 렌더링에서는 이번 프레임에서의 물리 연산의 결과를 기다려서 렌더링 작업을 수행하는 것이 아니라 이전 물리 연산 결과를 이용하여 렌더링하게 된다. 이것은 게임이라는 응용 프로그램이 적당한 응답성만을 보장하면 되는 특성에 기인한 것으로 이 방법을 사용하면 각 서브 시스템간의 분리 뿐만 아니라 병렬 처리도 용이해 진다는 장점이 있다.


디버깅에서도 차이가 있다. 일반적으로 멀티코어 시스템의 디버깅은 단일 시스템의 디버깅보다 더 복잡하고 어플리케이션에 영향을 미칠 수 있다. 하나의 코어 또는 코어의 하위 집합만이 정지되는 경우에는 다른 코어가 정지된 코어로 데이터를 전송하거나 수신할 수 있기 때문에 단일 쓰레드에서의 디버깅과는 다를 수 있다.

이상 언급한 것은 소프트웨어적인 측면인데 이런 소프트웨어적인 측면 뿐만 아니라 하드웨어적인 측면에서도 고려해야 하는 점이 있다. 예를 들어 메모리나 버스의 경우에도 사용 가능한 대역폭을 코어 간에 공유해야 한다는 점에 주의해야 한다. 소프트웨어 아키텍처가 아무리 잘 설계가 되어 있다고 하더라도 이러한 부분에서 병목 현상이 발생하게 되면 당연히 퍼포먼스에 영향을 미치게 되기 때문이다.

실제로는 이보다 더 많은 내용들이 있겠지만 여기까지만 살펴 보더라도 멀티 코어로의 패러다임의 전환은 그 어느때보다 소프트웨어 특히 병렬 처리와 관련한 소프트웨어를 잘 다룰 줄 아는 프로그래머의 역할이 중요해졌다는 것을 알 수 있다.

멀티 코어가 대세가 되는 시대에는 더 이상 하드웨어의 업그레이드 만으로는 프로그램의 빠른 실행을 기대할 수가 없게 되었다. 이제는 기술 진보의 의미가 클러 수의 증가로 하드웨어가 빨라 지는 것을 의미하는 것이 아니라 병렬 처리가 가능한 코어의 증가로 동시 처리의 증가를 의미하는 시대가 도래 되었다. 이것은 주어진 문제를 여러 개로 분리해서 가장 효율적으로 처리할 , 수 있는 방법이 중요한 이슈로 부상했다는 것을 의미한다.



참조

[1] 위키피디아 - 멀티코어, http://ko.wikipedia.org/

[2] Multithreaded Game Engine Architectures, Gamasutra,

http://www.gamasutra.com/view/feature/1830/multithreaded_game_engine_.php?print=1

by kimsama | 2008/05/29 19:40 | MultiCore | 트랙백 | 덧글(3)

◀ 이전 페이지다음 페이지 ▶