블로그 이미지
차세대 개발 플랫폼인 .NET Framework 4.0 과 Visual Studio 2010 의 정보와 아티클을 제공하는 공식 팀 블로그 입니다. 엄준일(땡초)
« 2010/03 »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      


 
 

[JumpToDX11-11] DirectCompute 를 위한 한걸음!

DirectX 11 | 2010/02/11 09:00 | Posted by 조진현


앞선 시간을 통해서 GPGPU 를 위해서 마이크로소프트가 제공하는 플랫폼이
DirectCompute 라는 것이라고 말씀드렸습니다.
앞으로 DirectX11 을 지원하는 모든 그래픽카드들은 이 DirectCompute 를 지원할 것입니다.
그 이외에도 일부 DirectX10 을 지원하는 그래픽카드들도 지원을 하고 있습니다.


GPGPU 를 위해서 가장 기본적이고 핵심이 되는 기능은 무엇일까요?
저는 GPU 에서 처리된 메모리를 CPU 쪽의 메모리로 보내는 것이라고 생각합니다.
( 이는 개인 의견입니다.^^ )
즉, 그래픽카드에 있는 메모리를 메인메모리로 보내는 작업입니다.
DirectX9 세대까지는 이 작업이 불가능 했습니다.
예를 들면, 그래픽스 파이프라인 중간에 처리된 결과를 다시 가공할 수 있는 방법은
VertexShader 나 PixelShader 같은 쉐이더 스테이지 정도 뿐이였습니다.

하지만 DirectX10 부터는 이들에 대한 중간 결과를 메인메모리로 보내는 기능이 추가되어지면서,
GPGPU 의 시작을 알렸다고 생각합니다.
이 단순한 Copy 작업이 앞으로도 얼마나 유용하게 사용될 수 있을지는 기대가 상당합니다.



< DirectCompute 를 위한 ComputeShader >

DirectCompute 를 위해서 개발자가 할 일은 ComputeShader 를 작성하는 일입니다.
ComputeShader 는 HLSL 이라는 기존 DirectX 의 쉐이더 문법 구조로 작성을 합니다.




HLSL 코드는 DirectX 쉐이더 컴파일러인 FXC 나 API 를 통해서 컴파일 됩니다.
HLSL 은 결국 최적화된 IL 코드를 생성하게 되고,
이 IL 코드를 기반으로 런타임에 각각의 하드웨어에 최적화된 명령어들로 변환
되어져서 실행됩니다.


< GPGPU 에게 실행이란? >

GPGPU 를 활용해서 실행한다는 것은 하드웨어 내부적으로 어떻게 동작하도록 할까요?
앞선 시간에 GPU 는 병렬 처리에 최적화된 많은 SIMD 형태로 구성되어져 있다고 언급했었습니다.
결국 이들은 스레드들의 그룹으로써 실행합니다.
스레드들을 얼마나 많이 생성할 것인지를 개발자가 정해주면, 그에 맞게 연산을 수행합니다.

API 에서는 이들을 큰 그룹으로 나누어 줍니다.
큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 입니다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에
ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정합니다.

[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}




결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 됩니다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 합니다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘오오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 됩니다.

아래에 간단한 예가 있습니다. 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있습니다.

일단 먼저 어떻게 DirectCompute 가 실행되어지는지에 대해서 살펴보았습니다.
실행까지 가기 위해서는 일련의 절차를 거쳐야 합니다.
이들에 대해서는 앞으로 차근차근 살펴보겠습니다.



참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

VC++ 10 C++0x나 병렬 프로그래밍 라이브러리 이외에도 툴적인 측면에서도 여러 좋은 기능들이 추가 되었습니다. 알고 있으면 작업할 때 편리한데 시간이 부족하여 제가 아직 자세하게 찾아보지 못해서 소개하지 못한 것이 많이 아쉽습니다. 그래서 짥은 것이라도 틈틈이 시간나면 소개하려고 합니다.

 

 

VC++ 10에서는 디버깅 모드에서도 역어셈블리 코드를 볼 수 있습니다.

 

메뉴에서 “Debug” -> “Windows” -> “Disassembly”를 선택합니다.



아래와 같이 역어셈블리 코드 창이 나타납니다.



그러나 위 화면을 보면 코드 바이트는 표시되지 않고 있습니다.

코드 바이트를 보고 싶다면 위 화면 왼쪽 상단의 “Viewing Option”을 클릭합니다.



위와 같이 옵션을 선택할 수 있습니다. 이 중 “Show code bytes”를 선택합니다.

그러면 아래와 같이 코드 바이트가 표시됩니다.



 

 

 

참고

http://d.hatena.ne.jp/kkamegawa/20100130/p1

 

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

'Language Development > Visual C++ 10' 카테고리의 다른 글

디버깅 모드에서 역어셈블리 코드 보기  (1) 2010/02/04
About Visual C++ 10  (0) 2009/10/15
 

안녕하세요.^^ 오늘은 IDE 4번째 시간으로 C# 개발자분들은 위한 IDE 소개하곘습니다.

이미 PDC 09 사이트에서 IDE관련한 동영상이 있고 C#, VB.NET 있습니다. 오늘은 C#&VB.NET 으로 개발하시는 분들을 위한 설명을 할까 합니다. 그럼 다음은?? ㅎㅎ VB.NET 입니다. 그리고 다음은 .. Web, C++ Project Management 마지막이 General 마무리를 지으려 합니다. 사실 PDC에서는 별도의 섹션으로 되어 있으나 제가 그냥 하나로 합쳐서 글을 씁니다. 이유는 글을 끝까지 읽어보시면 됩니다.

 

그럼 첫번째 C# 하기 전에 PDC 09 DJ Park 이란 분의 동영상과 자료는 이곳에서 다운로드 받을 있습니다.

 

http://microsoftpdc.com/Sessions/FT35

 

마지막에 분은 1분안에 코딩을 완료하는.. 멋진 모습(?) 보실 있습니다.(저도 해보고 싶지만.. ㅋㅋ 실력이 딸려서 . 그렇지만 언제는 해보고 싶습니다.^^ 세미나에서 1 코딩 완성 ㅎㅎㅎㅎ)

여기서는 동영상에서 나온것을 일단 정리하면서 C# 개발자 분들에게 도움이 될만한 IDE 환경에 대하여 한번 써보겠습니다.

 

 

화면을 아시는지요??(.. 뭐냥. 이건.. 아는 건뎅. .) 화면은 모두 아시겠지만 여러분들이 개발하는 언어를 선택하면 언어에 맞는 환경 구성을 한다는 것입니다. 여기서 환경이라고 하면.. 당근 개발 환경이겠지요. General Development Settings 으로 합니다. 일반적인 개발 환경으로는 개발 속도가 조금 다를것입니다. 일단 C#이므로 C#으로 선택합니다. 물론 중간에 설정 변경을 있습니다. 중간 변경은 Tool 에서 Import and Export Settings 에서 변경할 있습니다.

중간 변경화면 입니다.

 

 

처음 설정을 C# 개발자 하여 환경설정을 해보죠^^(중간에 변경 가능 아시죠?)

 

이렇게 경우 C# 개발 환경으로 변경이 되는데 변경되는 것은 키보드의 단축키와 IDE 환경이 변화게 됩니다.

IDE 환경에서 개발 언어 또는 관리자에 맞게 IDE 환경을 변경하여 최적의 개별 환경을 꾸미는 것입니다. 그럼 C# 최적은 무엇일가? 단축키?( 쓰지 않습니다 .) VS 시작할 시자화면? 모두 개발의 생산성이나 편리성에 맞추어 개발자가 바로 개발을 있다는 것입니다.

 

이렇게 C#으로 선택하면 초기에는 왼쪽은 박스, 오른쪽에는 솔루션탐색기와, 탁색기, 속성만 일단 표시됩니다. 다음 여러분들이 추가/변경 하실 있습니다. 다음은 바로 단축키 입니다. 단축키 부분이 변경이 되는데 소스 코드 한줄 할줄 생성할 여러분들이 단축키를 이용하면 오타를 많이 줄일 있습니다.( 사실 오타 땜시 오타쟁이라고 소문이 .)

 

첫번째는 Modernize the IDE라고 하는 부분입니다.

짧은 영어 실력으로 번역을 해보면 현대적인 IDE 환경을 이야기 합니다. 현대적인? 현대화 라고 하는데.. 정확히는 IDE환경을 조금 현대적으로 또는 우리가 마음대로 바꿀 있도록 했다는 것이며, 요즘 모두 모니터가 2 이상을 사용하는 추세이므로(HDMI까지 하면 노트북에서도 3개까지 가능합니다.) 멀티 모니터의 지원입니다. 사실 멀티 모니터는 개발자들에게 매우 많은 도움이 것이라는 것은 믿어 의심치 않습니다. ^^. 그럼. 현대적인 개발환경에서 첫번재 시작화면을 이야기를 하겠습니다.

 

시작 화면은 이미 변경 가능하다는 것으로, 일단 부분은 다른 블로그에서 소개 했습니다. 시작 페이지의 변경이 없으면 화면에서 왼쪽은 새로운 프로젝트와 시스템 연결선택 메뉴가 있고 바로 밑에 Recent Projects 메뉴가 있습니다. 사실 메뉴는 프로젝트 목록을 불러오는 것인데, 개발자에 따라 사용도 하고 그렇지 않은 경우도 있습니다.(사실 느린 시스템이나 인터넷이 연결 안된 상황에서는 시작페이지를 뛰우지 않습니다 . 가끔 그런상황이 있죠?? ㅎㅎ ) Recent Projects에서 해당 프로젝트의 목록을 이제부터는 Pin 형식으로 고정 사라지게 있다는 것입니다.

 

 

 

  여기서 보시면 제가 빨간색으로 체크한 부분입니다. 부분이 추가됐구요..

 

 

이제 위의 화면은 바로 두번째 메뉴입니다. 바로 해당하는 폴더를 바로 열어 있습니다.(사실 TFS 연결시 실제 폴더를 찾기 위해 소스제어에서 폴더 위치를 가끔 확인하곤 합니다 ^.^ 역시 바부팅 .) 그리고 하나씩 삭제도 가능하죠. ^^ 다음이 바로 밑에 있는 두개의 체크 박스입니다.


 

부분은 시작페이지의 표시 여부와 프로젝트 로드 시에 작업을 체크하는 것입니다. 이것은 그냥 Pass VS 2008에도 있었던 것이므로, 그렇지만. 여기서는 시작페이지에 표시되었다는 것이 조금 다르지요 옛날에는 메뉴에서 환경 설정에서 변경 했는데 편하게 변경되었습니다. 그것이 조금 눈에 들어오고, PDC PPT에서는 첫번째 체크 항목에 대하여 나왔는데 바로 프로젝트를 로드하고 페이지를 닫을 것인지에 대한 체크입니다.

 

다음이 뉴스 부분입니다. 부분은 조금 쉽게 변경되었다고 있습니다. Microsoft 에서 동안 너무 일방적인(?) 부분으로 개발관련 자료는 웹이나 로컬에 MSDN 설치해서 봐야하고 특정 목차가 초급자가 쉽게 접근할 없었습니다. 그런데 ~ 처음에는 Welcome 으로 초급자에게 쉽게 VS 사용법을 접근할 있도록 표시두었다는 것입니다. 전에는? 최신정보도 좋았지만 초급자가 원하는 정보는 찾기가 힘들었다는 것입니다. 그렇다면 고급자는 뉴스 메뉴에 Guidance and Resources 선택하면 조금 고급으로 넘어갑니다.

 

정리하면,  초급자에게 접근하기 좋은 화면 Get Started

              중급자 이상이 보기에 좋은 화면 Guidance and Resources

 

 

 

 

 이렇게 정리할 있습니다.

물론 RSS feed 수저할 있거나 URL 변경, 최신정보로 가져올 있습니다. 변경은 Latest News 에서 수정 또는 갱신이 가능합니다.


 

이제 다음으로 넘어가서 초기 기본으로 제공하는 시작화면에 대하여는 여기서 끝입니다. ^^

그럼 이제 프로젝트 부분인데 이것은 다음에 다시.^^ 글을 씁니다.


크리에이티브 커먼즈 라이선스
Creative Commons License

nullptr

Language Development/C++0x | 2010/01/28 09:00 | Posted by 흥배

오랜만에 팀 블로그에 C++0x 관련 글을 올립니다.

이미 알고 계시겠지만 Visual Stuido 2010 Beta2에 새로운 C++0x 기능이 추가 되었습니다.

추가된 것은 nullptr 이라는 키워드 입니다.

nullptr C++0x에서 추가된 키워드로 널 포인터(Null Pointer)를 나타냅니다.

 

 

null_ptr이 필요한 이유

 

C++03까지는 널 포인터를 나타내기 위해서는 NULL 매크로나 상수 0을 사용하였습니다.

그러나 NULL 매크로나 상수 0을 사용하여 함수에 인자로 넘기는 경우 int 타입으로 추론되어 버리는 문제가 발생 합니다.

 

< List 1 >

#include <iostream>

 

using namespace std;

 

void func( int a )

{

cout << "func - int " << endl;

}

 

void func( double *p )

{

cout << "func - double * " << endl;

}

 

int main()

{

func( static_cast<double*>(0) );

                 

func( 0 );

  func( NULL );

                 

getchar();

return 0;

}

 

< 결과 >

 


첫 번째 func 호출에서는 double* 로 캐스팅을 해서 의도하는 func이 호출 되었습니다. 그러나 두 번째와 세 번째 func 호출의 경우 func( doube* p ) 함수에 널 포인터로 파라미터로 넘기려고 했는데 의도하지 않게 컴파일러는 int로 추론하여 func( int a )가 호출 되었습니다.

 

바로 이와 같은 문제를 해결하기 위해서 nullptr 이라는 키워드가 생겼습니다.

 

 

 

nullptr 구현안

 

C++0x에서 nullptr의 드래프트 문서를 보면 nullptr은 아래와 같은 형태로 구현 되어 있습니다.

 

const class {

public:

    template <class T>

    operator T*() const

    {

        return 0;

    }

 

    template <class C, class T>

    operator T C::*() const

    {

        return 0;

    }

 

private:

    void operator&() const;

 

} nullptr = {};

 

 

 

nullptr 사용 방법

 

사용방법은 너무 너무 간단합니다. ^^

그냥 예전에 널 포인터로 0 이나 NULL을 사용하던 것을 그대로 대처하면 됩니다.

 

char* p = nullptr;

 

<List1>에서 널 포인트를 파라미터로 넘겨서 func( double* p )가 호출하게 하기 위해서는

func( nullptr );

로 호출하면 됩니다.

 



nullptr의 올바른 사용과 틀린 사용 예

 

 

올바른 사용

char* ch = nullptr; // ch에 널 포인터 대입.

sizeof( nullptr ); // 사용 할 수 있습니다. 참고로 크기는 4 입니다.

typeid( nullptr ); // 사용할 수 있습니다.

throw nullptr; // 사용할 수 있습니다.

 

 

틀린 사용

int n = nullptr; // int에는 숫자만 대입가능한데 nullptr은 클래스이므로 안됩니다.

 

Int n2 = 0

if( n2 == nullptr ); // 에러

 

if( nullptr ); // 에러

 

if( nullptr == 0 ); // 에러

 

nullptr = 0; // 에러

 

nullptr + 2; // 에러

 

 

 

nullptr 너무 간단하죠? ^^

VC++ 10에서는 예전처럼 널 포인터를 나타내기 위해서 0 이나 NULL 매크로를 사용하지 말고 꼭 nullptr을 사용하여 함수나 템플릿에서 널 포인터 추론이 올바르게 되어 C++을 더 효율적으로 사용하기 바랍니다.^^

 

 

 

짜투리 이야기...... ^^


왜 nullptr 이라고 이름을 지었을까?

nullptr을 만들 때 기존의 라이브러리들과 이름 충돌을 최대한 피하기 위해서 구글로 검색을 해보니 nullptr로 검색 결과가 나오는 것이 별로 없어서 nullptr로 했다고 합니다.

제안자 중 한 명인 Herb Sutter은 현재 Microsoft에서 근무하고 있는데 그래서인지 C++/CLI에서는 이미 nullptr 키워드를 지원하고 있습니다.

 

 

C++0x 이야기

근래에 Boost 라이브러리의 thread 라이브러리가 C++0x에 채택 되었다고 합니다. Boost에 있는 많은 라이브러리가 C++0x에 채택되고 있으므로 컴파일러에서 아직 지원하지 않는 C++0x의 기능을 먼저 사용해 보고 싶다면 꼭 Boost 라이브러리를 사용해 보기 바랍니다.

 


 

참고

http://d.hatena.ne.jp/faith_and_brave/20071002/1191322319

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf

http://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/nullptr

http://d.hatena.ne.jp/KZR/20080328/p1

 

 

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

'Language Development > C++0x' 카테고리의 다른 글

nullptr  (0) 2010/01/28
대용량 파일 조작을 위한 C++0x의 변화  (0) 2009/09/07
[VC++] 14. decltype  (0) 2009/06/30
[VC++] 13. Lambda - 네 번째  (1) 2009/06/23
[VC++] 12. Lambda - 세 번째  (0) 2009/06/16
[VC++] 11. Lambda - 두 번째  (0) 2009/06/09

[JumpToDX11-10] GPGPU 를 위한 DirectCompute.

DirectX 11 | 2010/01/27 09:00 | Posted by 조진현


아주 오래 전 컴퓨터에는 GPU 라는 개념이 특별히 존재하지 않았습니다.
그저 화면에 얼마나 많은 픽셀을 나타낼 수 있는가 정도가 그래픽 카드의 성능을 나타내는 기준이였습니다.
그랬던 상황이 오늘 날에 이르게 된 것입니다.( 굳이 자세히 언급할 필요가 없을 것 같습니다.^^ )

오늘날의 GPU 의 성능은 가히 놀라울 정도입니다.
하지만 이런 놀라운 성능을 가진 GPU의 processing unit 들이 대부분의 시간을 놀면서 있다는 것이
우리의 신경에 거슬렸던 것입니다.
그래서 이들에게 일감을 분배시키기 위한 방안을 생각하게 되었고,
이를 배경으로 등장한 것이 바로 GPGPU 입니다.

GPU 를 활용한 일반적인 처리 방식을
GPGPU( General-purpose computing on graphics processing uints ) 라고 합니다.
범용성 있게 GPU 를 활용해서 처리하겠다는 것이지만,
사실 CPU 와 GPU 의 목적은 엄연히 다릅니다.

CPU 는 광범위한 영역에서도 효율적으로 이용될 수 있도록 설계를 된 것이지만,
GPU 는 그래픽 처리를 위한 산술 연산에 특화된 processing unit 입니다.
오늘 날 PC 는 멀티코어 형식이 많아지고 있는 추세인데,
하나의 CPU 는 기본적으로 특정 시간에 하나의 연산만 수행할 수 있습니다.
GPU 의 경우에는 병렬처리 형식에 완전히 특화된 형태입니다.
오늘날 GPU의 코어는 32개라고 합니다.
즉 32개가 연산이 동시에 실행될 수 있다는 얘기입니다.
아래 그림을 한번 보실까요?




GPU 에는 SIMD 라는 것이 굉장히 많은 것을 볼 수 있습니다.
SIMD( Single Instruction Multiple Data ) 라는 것은 병렬 프로세서의 한 종류입니다.
벡터 기반의 프로세서에서 주로 사용되는데,
하나의 명령어를 통해서 여러 개의 값을 동시에 계산할 수 있도록 해줍니다.
( http://ko.wikipedia.org/wiki/SIMD  --> 여기서 참고 했습니다^^ )

벡터 기반이라는 사실에 우리는 주목할 필요가 있습니다.
GPU 는 광범위한 목적으로 설계된 processing unit 이 아닙니다.
즉, GPGPU 를 활용하는 목적은 주로 수치 연산에만 국한된 이야기 입니다.
일반적인 로직으로 GPGPU 를 활용하는 것은 그리 좋은 선택이 아니라는 것입니다.
현재 GPGPU 가 활용되고 있는 영역은 이미지 프로세싱, 비디오 프로세싱, 시뮬레이션 등과 같이
많은 수학 연산이 필요한 영역입니다.
분명한 것은 이들 수치 연산에 국한된 모델이라 할지라도, 그 성능이 무척 매력적이라는 것입니다.

이런 GPGPU 활용을 위해서 마이크로소프트는 어떤 준비물을 가지고 등장했을까요?
그것이 바로 'DirectCompute' 라는 것입니다.^^
아래 그림을 한번 보실까요?



DirectCompute 외에도 친숙한 이름이 보이시나요?
개인적으로 현재 GPGPU 분야에서 가장 앞서 있다고 보여지는 CUDA 가 있습니다.
이것들에 대한 우열을 가리기는 어려운 문제입니다.
여러분이 처한 상황에서 최선의 선택을 하면 되는 것입니다.
그 중에 DirectCompute 도 하나의 선택지일 뿐입니다.
CUDA 도 굉장히 훌륭한 GPGPU 모델입니다.
( 사실 저도 CUDA 를 공부하면서 GPGPU 의 개념을 잡았습니다.^^ )
CUDA 는 제가 지금 언급하지 않아도 될 정도로 많은 정보들이 공개되어 있습니다.

DirectCompute 는 마이크로소프트에서 가지고 나온 GPGPU 모델입니다.
앞으로 OS 의 강력한 지원을 가지고 등장하게 될 것입니다.

사실 GPGPU 와 DirectCompute 는 매우 혼란스럽게 사용될 수 용어들입니다.
그래서 오늘은 이들 두 용어를 확실히 구분하는 것으로 마무리 하겠습니다.^^
다음 시간부터는 DirectCompute 에 대해서 조금씩 살펴보겠습니다.


참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.
크리에이티브 커먼즈 라이선스
Creative Commons License

concurrent_queue는 사용 용도가 concurrent_vector 보다 더 많을 것 같아서 좀 더 자세하게 설명하겠습니다.

 

온라인 서버 애플리케이션의 경우 Producer-Consumer 모델이나 이와 비슷한 모델로 네트웍을 통해서 받은 패킷을 처리합니다. 즉 스레드 A는 네트웍을 통해서 패킷을 받으면 Queue에 넣습니다. 그리고 스레드 B Queue에서 패킷을 꺼내와서 처리합니다. 이 때 Queue는 스레드 A B가 같이 사용하므로 공유 객체입니다. 공유 객체이므로 패킷을 넣고 뺄 때 크리티컬섹션과 같은 동기 객체로 동기화를 해야 합니다. 이런 곳에 concurrent_queue를 사용하면 아주 좋습니다.

 

 

concurrent_queue를 사용하기 위한 준비 단계

 

너무 당연하듯이 헤더 파일과 네임스페이스를 선언해야 합니다.

 

헤더파일

#include <concurrent_queue.h>

 

네임스페이스

using namespace Concurrency;

을 선언합니다.

 

이제 사전 준비는 끝났습니다. concurrent_queue를 선언한 후 사용하면 됩니다.

concurrent_queue< int > queue1;

 

 


concurrent_queue에 데이터 추가

 

concurrent_queue에 새로운 데이터를 넣을 때는 push 라는 멤버를 사용합니다.

 

원형

void push( const _Ty& _Src );

 

STL deque push_back과 같은 사용 방법과 기능도 같습니다. 다만 스레스 세이프 하다는 것이 다릅니다. concurrent_queue는 앞 회에서 이야기 했듯이 스레드 세이프한 컨테이너이므로 제약이 있습니다. 그래서 deque 와 다르게 제일 뒤로만 새로운 데이터를 넣을 수 있습니다.

 

concurrent_queue< int > queue1;

queue1.push( 11 );

 

 

 

concurrent_queue에서 데이터 가져오기

 

데이터를 가져올 때는 try_pop 멤버를 사용합니다. 앞의 push의 경우는 STL deque와 비슷했지만 try_pop은 꽤 다릅니다.

 

원형

bool try_pop( _Ty& _Dest );

 

try_pop을 호출 했을 때 concurrent_queue에 데이터가 있다면 true를 반환하고 _Dest에 데이터가 담기며 concurrent_queue에 있는 해당 데이터는 삭제됩니다. 그러나 concurrent_queue에 데이터가 없다면 false를 즉시 반환하고 _Dest에는 호출했을 때의 그대로 됩니다.

 

concurrent_queue< int > queue1;

 

queue1.push( 12 );

queue1.push( 14 );

 

int Value = 0;

 

if( queue1.try_pop( Value ) )

{

           // queue1에서 데이터를 가져왔음

}

else

{

           // queue1은 비어 있었음.

}

 

 

 

concurrent_queue가 비어 있는지 검사

 

concurrent_queue가 비어 있는지 알고 싶을 때는 empty()를 사용합니다. 이것은 STL deque와 같습니다.

 

원형

bool empty() const;

 

비어 있을 때는 true를 반환하고 비어 있지 않을 때는 false를 반환합니다. 다만 empty를 호출할 때 비어 있는지 검사하므로 100% 정확하지 않습니다. 100% 정확하지 않다라는 것은 empty push, try_pop 이 셋은 스레드 세이프하여 동시에 사용될 수 있으므로 empty를 호출할 시점에는 데이터가 있어서 false를 반환했지만 바로 직후에 다른 스레드에서 try_pop으로 삭제를 해버렸다면 empty 호출 후 false를 반환했어 try_pop을 호출했는데 false가 반환 될 수 있습니다.

 

 

 

concurrent_queue에 있는 데이터의 개수를 알고 싶을 때

 

concurrent_queue에 있는 데이터의 개수를 알고 싶을 때는 unsafe_size 멤버를 사용합니다.

 

원형

size_type unsafe_size() const;

 

이것은 이름에서도 알 수 있듯이 스레드 세이프 하지 않습니다. 그래서 unsafe_size를 호출할 때 push try_pop이 호출되면 unsafe_size를 통해서 얻은 결과는 올바르지 않습니다.

 

 


concurrent_queue에 있는 데이터 순차 접근

 

concurrent_queue에 있는 데이터를 모두 순차적으로 접근하고 싶을 때는 unsafe_begin unsafe_end를 사용합니다.

 

원형

iterator unsafe_begin();

const_iterator unsafe_begin() const;

 

iterator unsafe_end();

const_iterator unsafe_end() const;

 

unsafe_begin을 사용하여 선두 위치를 얻고, unsafe_end를 사용하여 마지막 다음 위치(미 사용 영역)를 얻을 수 있습니다. 이것도 이름에 나와 있듯이 스레드 세이프 하지 않습니다.

 

 

 

모든 데이터 삭제


모든 데이터를 삭제할 때는 clear를 사용합니다. 이것은 이름에 unsafe라는 것이 없지만 스레드 세이프 하지 않습니다.

 

원형

template< typename _Ty, class _Ax >

void concurrent_queue<_Ty,_Ax>::clear();

 

 

 

제 글을 보는 분들은 C++을 알고 있다는 가정하고 있기 때문에 STL을 알고 있다고 생각하여 아주 간단하게 concurrent_queue를 설명 하였습니다.

 

concurrent_queue 정말 간단하지 않습니까? 전체적으로 STL deque와 비슷해서 어렵지 않을 것입니다. 다만 스레드 세이프 하지 않은 것들이 있기 때문에 이것들을 사용할 때는 조심해야 된다는 것만 유의하면 됩니다.

 

이것으로 Concurrency Runtime PPL에 대한 설명은 일단락 되었습니다.

이후에는 Concurrency Runtime의 다른 부분을 설명할지 아니면 Beta2에서 새로 추가된 C++0x의 기능이나 또는 이전에 설명한 것들을 더 깊게 설명할지 고민을 한 후 다시 찾아 뵙겠습니다.^^

 

 


참고

Producer-Consumer 모델 : 자바워크님의 http://javawork.egloos.com/2397148

MSDN concurrent_queue :

http://msdn.microsoft.com/en-us/library/dd504906(VS.100).aspx#queue

 

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

concurrent_queuequeue 자료구조와 같이 앞과 뒤에서 접근할 수 있습니다.

concurrent_queue는 스레드 세이프하게 enqueue와 dequeue(queue에 데이터를 넣고 빼는) 조작을 할 수 있습니다.

또 concurrent_queue반복자를 지원하지만 이것은 스레드 세이프 하지 않습니다.

 



concurrent_queuequeue의 차이점


concurrent_queuequeue는 서로 아주 비슷하지만 다음과 같은 다른 점이 있습니다.

( 정확하게는 concurrent_queue와 STL의 deque와의 차이점 이라고 할수 있습니다. )


- concurrent_queue enqueue dequeue 조작이 스레드 세이프 하다.


- concurrent_queue는 반복자를 지원하지만 이것은 스레드 세이프 하지 않다.


- concurrent_queue front pop 함수를 지원하지 않는다.

  대신에 try_pop 함수를 대신해서 사용한다.


- concurrent_queue back 함수를 지원하지 않는다.

  그러므로 마지막 요소를 참조하는 것은 불가능하다.


- concurrent_queue size 메소드 대신 unsafe_size 함수를 지원한다.

  unsafe_size는 이름 그대로 스레드 세이프 하지 않다.


 

 

스레드 세이프한 concurrent_queue의 함수


concurrent_queue에 enqueue 또는 dequeue 하는 모든 조작에 대해서는 스레드 세이프합니다.

 

- empty

- push

- get_allocator

- try_pop

 

empty는 스레드 세이프하지만 empty 호출 후 반환되기 전에 다른 스레드에 의해서 queue가 작아지던가 커지는 경우 이 동작들이 끝난 후에 empty의 결과가 반환됩니다.

 



스레드 세이프 하지 않은 concurrent_queue의 함수

 

- clear

- unsafe_end

- unsafe_begin

- unsafe_size

 

 


반복자 지원

 

앞서 이야기 했듯이 concurrent_queue는 반복자를 지원하지만 이것은 스레드 세이프 하지 않습니다. 그래서 이것은 디버깅 할 때만 사용할 것을 추천합니다.

또 concurrent_queue의 반복자는 오직 앞으로만 순회할 수 있습니다.


concurrent_queue는 아래의 반복자를 지원합니다.

 

- operator++

- operator*

- operator->

 

 

concurrent_queue는 앞서 설명한 concurrent_vector와 같이 스레드 세이프한 컨테이너지만 STL vector deque에는 없는 제약 사항도 있습니다. 우리들이 Vector deque를 스레드 세이프하게 래핑하는 것보다는 Concurrency Runtime에서 제공하는 컨테이너가 성능적으로 더 좋지만 모든 동작이 스레드 세이프하지 않고 지원하지 않는 것도 있으니 조심해서 사용해야 합니다.

 

 

다음에는 일반적인 queue에는 없고 concurrent_queue에서만 새로 생긴 함수에 대해서 좀 더 자세하게 설명하겠습니다.


ps : 앞 주에 Intel의 TBB에 대한 책을 보았습니다. 전체적으로 Concurrency Runtime과 비슷한 부분이 많아서 책을 생각 외로 빨리 볼 수 있었습니다. 제 생각에 TBB나
Concurrency Runtime를 공부하면 다른 하나도 아주 빠르고 쉽게 습득할 수 있을 것 같습니다.
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License


concurrent_vector의 주요 멤버

 

자주 사용하는 것들과 STL vector에 없는 것들을 중심으로 추려 보았습니다.

멤버

스레드 세이프

 

at

O

 

begin

O

 

back

O

 

capacity

O

 

empty

O

 

end

O

 

front

O

 

grow_by

O

new

grow_to_at_least

O

new

max_size

O

 

operator[]

O

 

push_back

O

 

rbegin

O

 

rend

O

 

size

O

 

assign

X

 

clear

X

 

reserve

X

 

resize

X

 

shink_to_fit

X

new

 

concurrent_vector는 기존 요소의 값을 변경할 때는 스레드 세이프하지 않습니다. 기존 요소의 값을 변경할 때는 동기화 객체를 사용하여 lock을 걸어야 합니다.

 

 

concurrent_vector 사용 방법

 

concurrent_vector를 사용하기 위해서 먼저 헤더 파일을 포함해야 합니다.

concurrent_vector의 헤더 파일은 “concurrent_vector.h” 입니다.

 

concurrent_vector의 사용 방법은 STL vector를 사용하는 방법과 거의 같습니다. 그러니 STL vector에 없는 것들만 제외하고는 vector를 사용하는 방법을 아는 분들은 따로 공부해야 할 것이 거의 없습니다.

STL vector에 대해서 잘 모르시는 분들은 About STL : C++ STL 프로그래밍(4)-벡터 글을 참고해 주세요.

 


 

concurrent_vector 초 간단 사용 예


concurrent_vector를 사용한 아주 아주 간단한 예제입니다.^^

 

#include <ppl.h>

#include <concurrent_vector.h>

#include <iostream>

 

using namespace Concurrency;

using namespace std;

 

 

int main()

{

           concurrent_vector< int > v1;

           v1.push_back( 11 );

           return 0;

}

 

 


STL vector에는 없는 grow_by, grow_to_at_least 사용 법

 

grow_by vector의 크기를 확장해 줍니다.

예를 들어 현재 vector의 크기가(size()에 의한) 10인데 이것을 20으로 키우고 싶을 때 사용합니다.

 

원형은 아래와 같습니다.

iterator grow_by( size_type _Delta );

iterator grow_by( size_type _Delta, const_reference _Item );

 

grow_to_at_least는 현재 vector의 크기가 10인데 이것이 20보다 작을 때만 20으로 증가시키고 싶을 때 사용합니다.

원형은 아래와 같습니다.

iterator grow_to_at_least( size_type _N );

 

grow_bygrow_to_at_least의 반환 값은 추가된 처음 요소의 위치가 반복자입니다.

 

grow_by의 예제 코드입니다.

void Append ( concurrent_vector<char>& vector, const char* string) {

    size_t n = strlen(string) + 1;

    memcpy( &vector[vector_grow_by(n)], string, n+1 );

}

위 예제는 http://japan.internet.com/developer/20070306/27.html 에서 참고했습니다.

 

 


shink_to_fit


shink_to_fit는 메모리 사용량과 단편화를 최적화 시켜줍니다. 이것은 메모리 재할당을 하기 때문에 요소에 접근하는 모든 반복자가 무효화됩니다.


 

Intel TBB


CPU로 유명한 Intel에서는 멀티코어 CPU를 만들면서 병렬 프로그래밍을 좀 더 쉽고, 안전화고, 확장성 높은 프로그램을 만들 수 있도록 툴과 라이브러리를 만들었습니다.

라이브러리 중 TBB라는 병렬 프로그래밍 용 라이브러리가 있습니다. 아마 TBB를 아시는 분이라면 Concurrent Runtime PPL에 있는 것들이 TBB에 있는 것들과 비슷한 부분이 많다라는 것을 아실 것입니다.

VSTS 2010 Beta2가 나온지 얼마 되지 않아서 병렬 컨테이너에 대한 문서가 거의 없습니다. 그러나 TBB에 관한 문서는 검색을 해보면 적지 않게 찾을 수 있습니다. concurrent_vector에 대해서 좀 더 알고 싶은 분들은 Intel TBB에 대해서 알아보시면 좋을 것 같습니다.

( 참고로 TBB 관련 서적이 한국어로 근래에 출간되었습니다.  http://kangcom.com/sub/view.asp?sku=200911100001 )

 


다음에는 concurrent_queue에 대해서 알아 보겠습니다.

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

[JumpToDX11-8] Deferred Contexts

DirectX 11 | 2009/12/02 09:00 | Posted by 조진현

멀티스레드 기반의 렌더링을 위해서 DirectX11 에서는 세가지를 중점에 두었습니다.

- 비동기적으로 자유스러운 리소스 로딩과 관리.
  ( 이들은 렌더링과 동시에 수행될 수 있어야 한다. )

- 멀티스레드 형식으로 렌더링 커맨드의 생성.
  ( 여러 스레드로 나누어서 렌더링 작업을 할 수 있어야 한다. )

- 디스플레이 리스트( Display lists )의 지원.


첫번째 리소스와 관련한 것을 지난 시간에 알아보았습니다.
이제는 실제 렌더링 작업에 대해 알아보아야 합니다.
그 실제 렌더링 작업을 위해서 우리는 새롭게 등장한 Deferred Context 라는 것을 살펴볼 것입니다.
Deferred Context 가 멀티스레드 기반의 렌더링에서 가장 중요한 키워드입니다.

< Device 의 분리 >
지난 세대의 DirectX는 모든 GPU 관련 내용의 처리는 Device 인터페이스를 통해서 수행했습니다.
즉 지난 회들에서 꾸준히 언급했던 것처럼,
Device 인터페이스를 통해서만 커맨드( Command ) 를 생성할 수 있었습니다.



오직 싱글코어 기반으로 설계된 지난 세대의 DirectX 였기 때문에,
위의 그림과 같은 상황이 연출되었습니다.
이들에 대한 내용은 지난 시간을 통해서 꾸준히 언급되었기 때문에, 더 자세한 언급은 하지 않겠습니다.

Device 인터페이스에 모든 작업이 집중되어 있었기 때문에,
이를 분리할 방법이 필요했습니다.
그 기준은 앞서 언급했듯이, 
그래픽카드에 보내는 작업이 Free threaded 한지였습니다.
결론적으로 얘기 드리면 DirectX11 에서는 기존의 Device 가 분리에 분리를 거듭했습니다.
그래서 아래와 같은 구조로 되었습니다.





DirectX11 에서 이제 개발자가 다루어야하는 커맨드 생성 인터페이스는 총 3가지입니다.
Device 는 Free threaded 한 API 만 사용하는 인터페이스입니다.
주로 리소스들이 포함됩니다.( 버퍼나 텍스쳐, 쉐이더 등 )

Device Context 는 실제로 렌더링과 관련된 인터페이스입니다.
렌더스테이트의 교체나 Draw 명령을 내리기 위해서는
반드시 Device Context 를 통해서 커맨드를 생성해야 합니다.
더 자세한 사항은
http://vsts2010.net/115 여기를 보시기 바랍니다.^^

Device Context 는 다시 두개로 분리될 수 있습니다.
Immediate Context 와 Deferred Context 가 바로 그것들입니다.

만약 멀티스레드 기반으로 렌더링 하고 싶지 않다면,
Deferred Context 는 사용하지 않으셔도 됩니다.
Deferred Context 의 유무가 바로 멀티스레드 기반의 렌더링이냐,
아니면 일반적인 렌더링이냐를 결정
합니다.
반면에 Immediate Context 는 반드시 한개만 존재해야 합니다.
이 인터페이스는 실제로 렌더링과 관련된 커맨드를 생성하기도 하지만,
생성된 커맨드를 그래픽 카드로 보내는 일
도 합니다.

< Deferred Context >

Deferred Context는 애플리케이션에서 여러개 생성될 수 있습니다.
하나의 스레드에 하나씩 Deferred Context 가 사용될 수 있으며, 이들은 Thread unsafe 합니다.
이렇게 하나의 스레드에 할당되어진 Deferred Context는 Display List 를 생성합니다.
이들은 GPU 가 바로 처리 가능한 커맨드들을 모아둔 버퍼라고 할 수 있습니다.
이렇게 Display List 를 미리 만들어둠으로써 성능을 크게 개선 시킬 수 있습니다.
일반적으로, CPU 가 커맨드를 생성시키는 시간이 꽤 오래 걸리기 때문입니다.
( 생성될 커맨드가 변화가 없다면, 이렇게 미리 만들어 두면 크게 도움이 되겠죠? ^^ )


사실 위의 그림은 개념적인 것입니다.
실제 소스레벨에서 Deferred Context 를 가리키는 인터페이스는 별도로 존재하지 않습니다.
Immediate Context 를 가리키는 인터페이스는 ID3D11DeviceContext 입니다.
Deferred Context 를 가리키는 인터페이스도 ID3D11DeviceContext 입니다.
즉, 둘 다 동일한 인터페이스를 통해서 처리되고 있는 것입니다.
실제 멤버 변수 선언들을 조금 나열해 보면 다음과 같습니다.

ID3D11Device*      m_ipGPU;
ID3D11DeviceContext*    m_ipImmediateContext;
ID3D11DeviceContext**  m_ippDeferredContextArray;

동일한 인터페이스 선언을 확인하셨습니까?
하지만 인터페이스가 동일하다고 해서, 이를 동일하게 생각해서는 안됩니다.
동일한 인터페이스를 사용하는 이유는
Deferred Context 는 Immediate Context 의 모든 기능을 지원한다는 의미로 받아들여야 합니다.
결과적으로 Device Context 는 아래의 그림과 같은 구조로 확장될 수 있습니다.




멀티스레드 기반으로 렌더링을 한다는 것은 엄밀히 말해서는 지원하지 않습니다.
정확히 말하자면, 멀티스레드 기반으로 커맨드를 생성해서
이들을 순차적으로 그래픽 카드로 보냅니다.

위의 그림이 이를 잘 표현하고 있습니다.

Deferred Context 는 각각의 스레드에 의해서 DisplayList 를 생성하고,
이들을 그래픽 카드에 보내기 위해 버퍼에 저장합니다.
그리고 실제로 그래픽카드에 커맨드를 보내기 위해서는
반드시 Immediate Context 를 통해야 합니다.
이때 Immediate Context 를 통해서 직접적으로 커맨드를 생성시킬 수 있습니다.

아무래도 렌더링이라는 작업은 순간순간의 렌더링 상태들에 의해서
결과가 변하기 때문에, 최종적으로 전송하는 작업만큼은 순차적으로 설계한 듯 합니다.
이로써 우리는 Deferred Context 를 사용할 준비가 되었습니다.^^



크리에이티브 커먼즈 라이선스
Creative Commons License

Visual Stuido 2010 Beta2가 나오면서 제가 기대하고 있었던 병렬 컨테이너가 드디어 구현되었습니다.

 

Concurrency Runtime(이하 ConRT)에는 총 3개의 병렬 컨테이너를 제공합니다. Beta2에서는 모두 다 구현되지는 못하고 concurrent_vector concurrent_queue 두 개가 구현되었습니다. 아직 구현되지 않은 것은 concurrent_hash_map 입니다.

 

세 개의 컨테이너들은 C++ STL의 컨테이너 중에서 가장 자주 사용하는 것으로 vector, deque, hash_map 컨테이너의 병렬 버전이라는 것을 이름을 보면 쉽게 알 수 있을 것입니다.

 

STL에 있는 컨테이너와 비슷한 이름을 가진 것처럼 사용 방법도 기존의 컨테이너와 비슷합니다. 다만 병렬 컨테이너 답게 스레드 세이프하며, 기존의 컨테이너에서 제공하는 일부 기능을 지원하지 못하는 제한도 있습니다.

 

 

몇 회에 나누어서 concurrent_vector concurrent_queue에 대해서 설명해 나가겠습니다.

이번에는 첫 번째로 concurrent_vector에 대한 것입니다.

 

 

 

concurrent_vector란?

 

STL vector와 같이 임의 접근이 가능한 시퀀스 컨테이너입니다. concurrent_vector는 멀티 스레드에서 요소를 추가하던가 특정 요소에 접근해도 안전합니다. 반복자의 접근과 순회는 언제나 멀티 스레드에서 안전해야 하므로 요소를 추가할 때는 기존의 인덱스와 반복자를 무효화 시키면 안됩니다.

 

 

concurrent_vector vector의 차이점


기능

vctor

Concurrent_vector

추가

스레드에 안전하지 않음

스레드에 안전

요소에 접근

스레드에 안전하지 않음

스레드에 안전

반복자 접근 및 순회

스레드에 안전하지 않음

스레드에 안전

push_back

가능

가능

insert

가능

불가능

clear

모두 삭제

모두 삭제

erase

가능

불가능

pop_back

가능

불가능

배열식 접근 예. &v[0]+2

가능

불가능

 

 

grow_by, grow_to_at_least (vector resize와 비슷)는 스레드에 안전하지 않음

 

 

추가 또는 resize 때 기존 인덱스나 반복자의 위치가 바뀌지 않음

 

 

bool 형은 정의 되지 않았음

 


concurrent_vector에 대한 설명을 이번에는 소개 정도로 끝내고 다음부터는 본격적으로 Concurrent_vector을 어떻게 사용하면 되는지 상세하게 설명해 나가겠습니다.^^

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License