[JumpToDX11-10] GPGPU 를 위한 DirectCompute.

DirectX 11 2010. 1. 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 를 참고해서 만들었습니다.

SharePoint 2010 Visual Web Part

SharePoint 2010 2010. 1. 21. 09:00 Posted by 알 수 없는 사용자

Visual Studio 2010에서 새롭게 제공되는 Visual Web Part를 생성해서 배포, 테스트하는 예를 알아보도록 하겠습니다.

이전버전의 웹 파트는 디자인하기가 시간이 걸리는 작업이며 AJAX 기능을 구현하려고 해도 설정과 코딩이 필요합니다. 하지만 Visual Studio 2010에서 Visual Web Part를 이용하면 얼마나 쉽게 생성할 수 있는지 알아보겠습니다.

Visual Studio 2010에서 프로젝트를 생성하면서 Empty SharePoint Project에서 새 항목을 추가하면서 Visual Web Part를 추가해도 되고 별도로 Visual Web Part 프로젝트를 통해서 생성해 됩니다.

 

Empty SharePoint Project는 해보았으니 새롭게 Visual Web Part 를 구성해보겠습니다.

프로젝트 이름은 VisualWPDemo 라고 하고 OK을 선택합니다.


SharePoint Customization Wizard에서는 OK를 선택합니다.

생성된 프로젝트의 솔루션 탐색기를 보면 새 항목으로 Visual Web Part 항목이 추가된 것을 알 수 있습니다. Visual Web Part에서는 Web Part와 항목이 동일한데 ascx ascx.cs가 들어가 있는 것을 확인할 수 있습니다.


그리고 VisualWebPart1.cs에서는 ascx 컨트롤을 CreateChildControls에서 로딩하는 것을 아래 그림에서 알 수 있습니다.


개발자들이 ascx에서 데이터바인딩과 디자인, AJAX 등에 대한 구현을 하면 일반 웹 파트와 동일하게 사용자 정의 컨트롤(ascx) Controls에 추가되어 처리됩니다.

 

자 그럼 데이터 바인딩과 AJAX를 가미해서 배포해보도록 하겠습니다.

AJAX 웹 파트를 위한 복잡한 web.config 구성 작업은 필요 없습니다.

디자인은 아래 그림에서처럼 AJAX UpdatePanel 안에 DropDownList GridView를 배치하고 GridView는 디자인을 적용했습니다.



ascx.cs
코딩은 생략하겠습니다. 일반적인 ADO.NET 이라..,

 

결과는 아래와 같습니다. 간단하게 AJAX 기능과 컨트롤의 디자인을 적용된 것을 확인 할 수 있습니다.


SharePoint 2010 개발 중에서 Visual Web Part에 대한 내용을 알아보았습니다.

일반 웹 파트 개발과 병행해서 사용하면 좋을 듯 합니다.

Welcome to Dynamic C#(13) - 아직도 가야할 길.

C# 2010. 1. 20. 09:00 Posted by 알 수 없는 사용자
- 제목이 표절인거 같은데...?

넵. 존경해 마지 않는 스캇 펙의 아직도 가야할 길을 요즘 감명깊게 읽고 있기 때문만은 아니구열. dynamic키워드로 아직도 써야 할 내용이 남아 있기 때문에, 한번 써봤습니당. 역시, 프로그래밍 언어의 현대적인 패러다임을 따라잡는 건, 단순히 사용하는 패턴만 익히는 게 아니라는 걸 다시한번 깨닫게 되네요. 그럼그럼~ 계속해서 한번 가보시져!


- 프로퍼티

d.Foo를 예로들면, d는 dynamic객체이고, Foo는 d속에 살고 있는 멤버 변수나 프로퍼티입니다. 컴파일러가 이런 구문을 만나면, 우선 Foo라는 이름을 payload속에다가 기록합니다. 그리고 런타임에게 d의 실제 타입을 찾아서 연결(바인드)해달라고 요청합니다.
payload : 캡슐화를 통해서 제공되는 컴퓨터 프로그램이나, 데이터 스트림속에서 사용자의 정보등을 나타내는 부분(출처 : http://en.wikipedia.org/wiki/Payload). 여기서는 C# 런타임 바인더가 해당 구문을 제대로 바인드하기 위해서 필요한 정보를 기록해 놓는 데이터 구조를 뜻합니당.

그리고 이런 프로퍼티는 항상 3가지경우 중 한가지경우에서 쓰이는데요. 값을 읽어오거나, 값을 대입하거나, 둘다 하거나(+=같이). 컴파일러가 사용된 모양을 보고, 어떻게 사용하려고 하는지도 payload에 같이 기록합니다. 즉, 읽기만 하는 경우에는 해당 프로퍼티는 읽기전용으로 기록을 하는 식으로 말이죠.

그리고 컴파일러는 이런 접근이 필드에 접근하는건지, 프로퍼티에 접근하는건지 딱히 구분하지 않습니다. 그건 나중에, 런타임이 구분을 하게됩니다. 그리고 컴파일할때, 이런 구문의 리턴 타입은 dynamic으로 설정됩니다.


- 인덱서

인덱서는 두가지로 생각해볼 수 있습니다. 첫번째는 매개변수가 있는 프로퍼티, 두번째는 배열이나 리스트같은 집합의 이름을 통한 메서드 호출. dynamic과 연관지어서 생각할때는 후자가 훨씬 도움이 됩니다. 메서드의 경우와 같이 인덱서도 정적으로 바운드 될 수 있지만, dynamic타입의 매개변수가 주어지고, 그 매개변수가 dynamic타입을 받는 인덱서로 정적 바운드가 되지 않는 경우, 오버로드 판별 과정에 유령이 끼어들게 됩니다. 그래서 인덱서의 수신자(receiver)는 정적타입이지만, 매개변수가 dynamic타입이라서 런타임에 늦은 바인딩이 일어나게 됩니다. 말로 설명하니깐, 깝깝하시죠? 실력부족으로 더 이상 말로는 깔끔하게 설명을 못드리겠네요-_-;; 예제로 설명을 드리면요.

public class C
{
    public int this[int i]
    {
        get
        {
            return i;
        }
    }

    public int this[dynamic d]
    {
        get
        {
            return d;
        }
    }
   
    static void Main(string[] args)
    {
        C c = new C();
        Console.WriteLine(c[5]);
        dynamic d = 7;
        Console.WriteLine(c[d]);
    }
}


위와 같은 코드를 보시면, C에 인덱서가 두개가 있습니다. 하나는 int를 매개변수로, 하나는 dynamic을 매개변수로 받죠. 그리고 Main메서드 안에서 하나는 int를, 하나는 dynamic타입의 매개변수를 넘겨주고 있습니다. 이 경우에 두번째 인덱서는 언제 어떻게 바인드될까요? 이경우는 비록 d가 타입이 dynamic이지만, 인덱서의 오버로드중에, 매개변수를 dynamic타입으로 받는 인덱서가 있습니다. 그래서 컴파일하는 시점에 "c[d]"이 인덱서 호출은 "public int this[dynamic d]"이 인덱서로 바인드 됩니다. 정적바인드가 되는거죠.

그런데, 만약에 dynamic을 받는 오버로드가 없다고 한다면 어떻게 될까요? 인덱서 호출을 받는 수신자는 c이고 c의 타입은 정적 타입인 C입니다. 하지만, 매개변수가 dynamic이죠. 그런데, dynamic과 일치하는 오버로드가 없습니다. 그래서 이때, 지지난 포스트에서 설명드렸던, 유령이 끼어들게 되는거죠.

메서드 처럼 생각하는게 편하다는 말씀은 드렸지만, 사실 프로퍼티와 유사한 면도 있습니다. 인덱서호출 역시 payload에다가 읽기, 쓰기등을 어떻게 하는지 기록합니다. 그래서 C# 런타임 바인더가 그 정보를 바탕으로 바인드할 수 있도록 말이죠. 그리고 인덱서의 리턴타입 역시 컴파일하는 시점에서는 dynamic으로 간주됩니다.


- 형변환

지지난 포스트에서 설명을 드릴때, dynamic은 다른 타입으로 암시적 형변환은 안되지만 되는 경우가 있다고 설명을 드렸었습니다. 그리고 지난 포스트에서 사실 그런 형변환이 대입 형변환이라는 설명도 드렸구요~. 형변환의 경우는 payload가 매우 단순해집니다. 왜냐면, 컴파일러는 이미 어떤 타입으로 형변환을 하려고 하는지 알고 있기 때문이죠. 그래서 컴파일러는 그냥 payload에 형변환 하려고 하는 타입을 기록하고, 런타임 바인더에게 가능한 모든 대입 형변환(형변환 연산자를 쓰는 경우에는 명시적 형변환도 같이)을 시도해보라고 이야기 해줍니다. 물론, dynamic타입이 아니라, 런타임에 결정될 실제 타입에서 목표 타입으로 시도해보겠죠.

형변환의 경우는 다른 모든 경우와 다르게 컴파일하는 시점에서 dynamic이 아닌 형변환의 목표타입을 리턴합니다. 위에서 말씀드렸듯이 이미 어떤 타입으로 형변환하려고 하는지 알 수 있게 때문이죠.


- 연산자

연산자는 초큼 특이합니다. 그냥 아무생각없이 훑어보면, 동적인 뭔가가 일어난다고 느끼기 힘들기 때문이죠. 그런데, d+1 같은 간단한 구문도 런타임에 바인드 되어야 합니다. 그 이유는 사용자정의 연산자가 끼어들 수 있기 때문입니다. 그래서, dynamic 매개변수를 갖는 모든 연산은 런타임에 바인드됩니다. +=나 -=같은 연산자도 포함해서 말이죠.

컴파일러는 연산자를 보면, d.Foo += 10 같이 멤버에 대입하는 연산이 있는지 혹은, d += 10 같이 변수에 대입하는 연산이 있는지 확인합니다. 그리고 그 과정에서 d를 ref를 통해 넘겨서 변경된 값이 유지되어야 하는지 확인합니다.

그리고 마지막으로 d.Foo += x 같은 구문이 있을 때, d.Foo가 바인드결과 delegate나 event타입이라면, 앞의 구문은 이벤트 수신자 추가 같은 적절한 메서드를 호출하도록 컴파일러가 연결해줍니다.


- 델리게이트 호출

데일게이트 호출은 메서드와 굉장히 유사합니다. 딱 한가지 틀린 점이 있다면, 호출되는 메서드의 이름이 명시되지 않는다는 것 뿐이죠. 그래서, 아래 예제의 두 호출은 모두 런타임에 바인드됩니다.

public class C
{
    static void Main(string[] args)
    {
        MyDel c = new MyDel();
        dynamic d = new MyDel();

        d();
        c(d);
    }
}


첫번째 호출은 매개변수가 없는 호출을 런타임에 바인드하게 됩니다. 런타임 바인더가 런타임에 호출의 수신자가 델리게이트 타입이 맞는지 확인하고 해당 델리게이트 시그니처와 일치하는 호출이 있는지 오버로드 판별을 통해서 찾게 됩니다.

두번째 호출은 매개변수가 dynamic타입이기 때문에, 런타임에 바인드됩니다. 컴파일러가 컴파일시점에서 c의 타입이 델리게이트라는 걸 확인할 수 있지만, 실제 오버로드 판별은 런타임에 가서 끝나게 됩니다.


- 마치면서

이제야 저는 dynamic에 대한 내용들이 머리속에서 아주 조금 자리를 잡은 듯한 느낌이네요. 저도 이런데 혼란스러웠던 지난 포스트를 보신 분들은 더 하시겠죠-_- 최대한! 최대한! 앞으로도 열심히 적겠습니다. 그럼 다음포스트에서 뵙죠~.

- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/12/11/dynamic-in-c-v-indexers-operators-and-more.aspx

SharePoint 2010 Web Part 생성

SharePoint 2010 2010. 1. 19. 09:00 Posted by 알 수 없는 사용자

이전 블로그에서는 개발 환경 테스트 개념으로 간단한 Web Part를 생성해보았는데

여기서는 데이터베이스 연동과 여러 기능을 붙여 좀더 구체적으로 Web Part를 생성해보겠습니다.

 

말씀드릴 내용은 MOSS 2007에서 했던 내용과 동일한 내용입니다. Hello World는 너무 간단하여 데이터베이스 연동과 속성 노출 등을 구성해봅니다.

다음 블로그에서는 VS 2010에서의 SharePoint 지원 내용으로 Visual Web Part AJAX를 가미해서 보다 더 쉽고 강력하게 개발하는 내용을 다뤄보겠습니다.

 

완성된 모습은 아래와 같습니다. 텍스트 상자에 카테고리 ID를 입력하고 버튼을 누르면 해당 제품 이름과 가격 리스트가 표시되며 속성을 노출해서 카테고리 ID 기본값을 입력할 수 있습니다.


새로운 프로젝트를 생성합니다. SharePoint Empty 프로젝트를 선택하고 이름은 SP10WP 라고 지정합니다.



SharePoint Customization Wizard에서는 http://sp10:8090으로 선택합니다.



솔루션 탐색기에서 추가 > 새 항목> 웹 파트를 새로 추가합니다. 이름은 ProductsWP라고 합니다.


생성된 웹 파트 항목의 솔루션 탐색기 구조는 아래처럼 구성되어 있습니다.


Feature Package가 포함되어 있으며 디자이너를 통해 편집이 가능하며 향후 구체적으로 다루어 보겠습니다.

ProductsWP 폴더는 Elements.xml ProductsWP.cs, ProductsWP.webpart로 구성되어 있습니다.

Elements.xml Feature의 구체적인 파일로서 웹 파트 위치나 URL 등을 지정할 수 있습니다.

ProductsWP.webpart는 웹 파트의 메타데이터를 나타내는 파일입니다.

ProductsWP.cs 는 코딩으로 컨트롤과 데이터바인딩, 속성 등을 정의하는 파일입니다.

 

코드에서는 구체적으로 보지 말고 RenderContents, OnLoad 메서드만 보도록 하겠습니다.

해당 메서드에서는 화면에 입력 폼을 구성하고 데이터베이스 연동하여 데이터 바인딩을 해주는 내용입니다.


OnLoad 에서 컨트롤을 생성해주는 내용입니다. Web Part 항목에서는 디자이너를 제공하지 않아 직접 아래와 같이 생성해야 합니다.

속성 노출에 대한 내용은 아래와 같습니다.



위의 실제 결과는 아래 그림과 같습니다.



Hello World
보다 좀 더 발전된 웹 파트를 VS 2010 환경하에서 생성하고 테스트해보았습니다. 코드가 상당히 손이 많이 가고 디자인하기 좀 곤란한 측면이 있습니다. 하지만 어떤 경우에서는 위와 같이 생성해야 할 듯 합니다. 다음에 다룰 내용은 Visual Web Part AJAX로 더 손쉽고 강력하며 디자인이 지원되는 모습을 알아보도록 하겠습니다.

Visual Studio 2010 출시 일정

Visual Studio 2010 2010. 1. 18. 09:30 Posted by POWERUMC

오늘 Visual Studio 2010 출시 일정이 나왔습니다. Rob Caron 말에 의하면 2010년 4월 12일에 출시가 된다고 합니다. Microsoft 직원이 전하는 이야기이니 아마도 이 날에 출시하는 것을 신뢰하시면 될 것 같습니다.

Rob Caron 은 Microsoft 에서 마케팅 커뮤니케이션 매니저로 일하고 있으며, Developer, User Experience Runtime, Tools 과 관련된 일을 하고 있는 분입니다. Microsoft 에 입사 하기 전에 개발자 출신인데, Microsoft 에서 이전에는 개발자 마케팅과 MSDN 개발자 센터, Visual Studio 마케팅 사이트, 개발자 이벤트, MSDN Library 에 콘텐트를 게시하는 일도 하셨다고 합니다.

  

좀 아쉬운 것은 현재 문제나 개선되고 있는 부분을 좀 더 자세히 알고 싶은데, 출시 일정 외에 다른 언급은 전혀 없네요.   

Visual Studio 2010 의 처음 출시 일정은 2010년 1월에 RC 버전, 3월에 정식 버전을 출시하기로 예정하였습니다. 그러나 가상 메모리 관련, 성능 최적화 문제로 정확한 출시 일정을 공개하지 않고 출시 기간을 연장하였습니다. 일정이 연기된 것은 개인적으로 아쉽지만, 보다 충실한 플랫폼을 만들기 위해서라면 충분히 기다릴만 하다고 생각합니다. 써본 후에 투덜댈봐엔 좀 더 완성도 높은 플랫픔을 위한 것이라면 말이죠^^ 

언제나 언급했던 이야기 이지만, 예전의 Visual Studio 2008 까지는 기능에 충실했던 강력한 개발 도구임이 틀림이 없습니다. 하지만 Visual Studio 2010 과 관련된 플랫폼은 기능은 기대 이상 완벽히 충실하고 있으며, 편의성까지 갖추게 될 그야말로 차세대 플랫폼입니다. 앞으로 다가올 4월이 정말 기대가 됩니다.

Welcome to Dynamic C#(12) - dynamic은 외로운 아이.

C# 2010. 1. 18. 09:00 Posted by 알 수 없는 사용자

- 뭐시 외로운 아이여? 스타아닌겨?

네. 확실히 C# 4.0의 가장 큰 키워드는 Dynamic이기 때문에, dynamic은 스타일지도 모르겠습니다. 하지만, 화려한 모습뒤에 감쳐진 그들의 일상사는 때때로 자살같은 비극적인 사건을 통해서 세간에 알려지곤 하죠. 그럴때마다 세삼스럽게 사람들은 화려한 일상뒤의 모습은 변비때문에 우울해하는 것 같이, 보통사람과 전혀 다르지 않음을 재확인 합니다..... 왜 이런 헛소리를 또 하고 있을까요-_-;;; 아무튼. dynamic은 초큼 외로운 아이입니다. 증거를 제시해드리죠.



그림1. 출처 : http://blogs.msdn.com/cburrows/archive/2008/11/06/c-dynamic-part-iv.aspx

네. 다른 타입들은 System.Object로 부터 아주 사이좋게 이리저리 연결되어 있습니다만, dynamic은 천상천하유아독존입니다. 그저 혼자 있을 뿐이지요. 어린이집에서도 유별난 애들은 꼭 걔네들 기분에 잘 맞춰줘야 해서 선생님들이 고생을 하기도 하는데요. dynamic역시 독특한 면을 갖고 있습니다. 지난 포스트에 이어서 dynamic의 형변환 룰에 대해서 알아보면 아래와 같습니다.

1. dynamic에서 dynamic으로 동일한 형변환이 가능
2. 모든 참조형 타입에서 dynamic으로 암시적인 참조형변환이 가능
3. 모든 값형 타입에서 dynamic으로 암시적인 박싱형변환이 가능
4. dynamic에서 모든 참조형 타입으로 명시적인 참조형변환이 가능
5. dynamic에서 모든 값형 타입으로 명시적인 언박싱 형변환이 가능
리스트1. dynamic에서 다른 타입으로 형변환 가능여부.

  아래부분에 줄로 그어 버린 부분은 정식버전이 출시되면서 개념이 바뀐 부분들입니다. 그리고 밑에서 설명드리는 '대입형변환'이라는 용어도 여전히 의미가 있는지 불분명합니다. 다만, C# 4.0 명세서에 보면 '대입형변환'이라는 개념이 없는 걸로 봐서는 설명을 위해서 도입한 개념이 아닌가 싶은데요, 이에 관해서 Chris Burrows에게 질문을 남겨놨는데요, 답이 오면 바로 업데이트 하겠습니다. 바뀐 내용에 대해서는 이 글을 참조하시기 바랍니다.

매우 직관적으로 보이긴 하지만, 좀 생각해보면 이상한 점들이 발견됩니다. 그 첫번째가 바로 dynamic에서 object로 암시적인 형변환이 없다는 사실인데요. 위에서 1, 4번에서 언급했듯이 dynamic에서 dynamic을 제외한 모든 참조형타입으로 암시적인 형변환이 없다고 하고 있습니다. 그 이유는 연산을 하는 도중에 둘을 구분해내기가 매우 어렵기 때문이라고 합니다. 근데, 아래와 같은 코드가 컴파일 되고 실행되는 걸 확인할 수 있습니다.

dynamic d = null;
object o = d;

이건 분명히 암시적 형변환 처럼 보이는데, 왜 이게 컴파일이 되는 걸까요? 사실, 두번째줄은 암시적 형변환이 아니라 대입 형변환입니다. 대입 형변환은 또 뭘까요?

그림 2. 출처 : http://blogs.msdn.com/cburrows/archive/2008/11/11/c-dynamic-part-v.aspx

위 그림을 보시면, 형변환이 총 3가지로 분류가 되는 걸 확인하실 수 있습니다. 대입 형변환은 명시적 형변환과 암시적 형변환의 사이에 위치해 있는데요. 모든 대입 형변환은 사실 명시적 형변환이며, 모든 암시적 형변환은 대입 형변환 인거죠. 왜 이런 걸 새로 도입했어야 할까요? 사실 C# 4.0작업을 하면서 dynamic에서 다른 타입으로 암시적 형변환 도입을 검토했었다고 합니다. 그런데, 이렇게 하게 되면 문제가 생기는데요. 바로, dynamic을 통해서 아무타입에서 아무타입으로 형변환이 가능하기 때문입니다. 이 문제를 오버로드 판별을 예로 들어서 설명해보겠습니다.

public class C
{
    public static void M(int i){}

    public static void M(string s){}
   
    static void Main(string[] args)
    {
        dynamic d = GetSomeDynamic();
        C.M(d);
    }
}

코드1. 만약 암시적 형변환이 가능하다면?

위와 같은 코드가 있다고 할때요, 과연 어떤 메서드가 실행되어야 할까요? dynamic에서 모든 타입으로 암시적 형변환이 있다면, dynamic에서 int도 dynamic에서 string도 가능한 상황이 됩니다. 물론, dynamic에서 object같이 dynamic에서 int보다 더 나은 걸 찾을 수도 있겠지만, 그렇지 않은 경우가 훨씬 많이 발생하게 됩니다. 이런 모호함 때문에 dynamic에서 다른 타입으로 형변환을 할때는 명시적으로 선언을 하게 제한을 둔 거죠.

그렇다면, 대입 형변환은 또 뭘까요?

1. dynamic에서 모든 참조형 타입으로 대입 참조 형변환이 가능
2. dynamic에서 모든 값형 타입으로 대입 언박싱 형변환이 가능
리스트2. 대입 형변환의 설명

[리스트1]에서 4,5번을 보면 명시적 형변환에 대해서 이야기 하고 있죠? 사실은 그 명시적 형변환이 바로 이 대입 형변환을 말하는 겁니다. 모든 대입 형변환은 명시적 형변환이라고 말씀드렸던 걸 떠올리시면 고개가 절로 끄덕끄덕....교회 다니시는 분들은 교회로 끄덕끄덕 하실겁니다. 그리고 리스트2의 모든 형변환과 암시적 형변환을 모두 합하면 바로 대입 형변환이 되는 거죠.


- 그래그래 어디 계속 해봐.

대입이 일어나는 곳이 바로 컴파일러가 대입 형변환을 시전하는 곳입니다.

dynamic d = GetSomeDynamic();
Worksheet ws = d; //대입 형변환

이거 말고도, 대입 비스무리한 것들은 모두 대입 형변환을 사용합니다. return과 yield 그리고 프로퍼티와 인덱서, 배열 초기화구문, 그리고 foreach나 using같은 구문말이죠.

return d; //return할 타입으로 대입 형변환
yield return d; //반복자의 타입으로 대입 형변환
foo.Prop; //Prop의 타입으로 대입 형변환
foo[1] = d; //인덱서의 타입으로 대입 형변환
bool[] ba = new bool[] { true, d }; //bool로 대입 형변환
foreach(var x in d) {} //IEnumerable로 대입 형변환
using (d) {} //IDisposable로 대입 형변환
리스트3. 대입 형변환이 어디어디서 끼어드는지!

하지만, 이런 대입 형변환을 사용하지 않는 곳 중에 하나가 오버로드 판별입니다. [코드1]에서 보셨듯이 만약에 메서드를 호출하는데 대입 형변환을 적용하게 되면, 매번 메서드를 호출할때마다 모호함때문에 캐고생을 하게 될겁니다. 하지만, 대입 형변환을 적용하지 않는다고 해도, [코드1]은 제대로 컴파일 되지 않을거 같습니다. 왜냐면, d의 타입인 dynamic에서 C의 두 오버로드가 받는 파라미터 타입인 int와 string으로 형변환이 불가능 하기 때문입니다. 하지만, 이런 코드가 컴파일 되고 잘 돌아가야만 하니깐, 바로 지난 포스트에서 언급했던 유령 메서드가 끼어들게 되는거죠.


- 마치면서

어찌 퍼즐 조각이 좀 맞아 들어가시나요? 저도 글을 쓰면서 다시한번 자세히 읽다보니 퍼즐조각이 조금씩 맞아들어가는 느낌이 드는데요. 꼭 무슨 그것이 알고싶다에서 사건 조사하는 거 같은 기분이네요-_-. 그럼 다음 포스트에서 뵙져!!!


- 참고자료

1. http://blogs.msdn.com/cburrows/archive/2008/11/06/c-dynamic-part-iv.aspx
2. http://blogs.msdn.com/cburrows/archive/2008/11/11/c-dynamic-part-v.aspx

Parallel Patterns Library(PPL) - concurrent_queue - 2

VC++ 10 Concurrency Runtime 2010. 1. 16. 09:00 Posted by 알 수 없는 사용자

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

 

안녕하세요. 아직 떨린 마음을 감추지 못하는 팀블로그 새내기 박세식입니다. 너무나 오랜만에 포스팅을 하게되네요. 제 개인사까지 다 말씀드릴 필요는 없겠지만, 여러분 모두 새해에는 하시는일 모두 잘되었으면 좋겠습니다. 진심으로요ㅠㅠ 늦었지만 새해 복 많이 받으세요^^

이번 시간은 ASP.NET MVC와 인사를 나눠보는 시간을 갖도록 하겠습니다. 반갑게 만나보도록 하죠^^

M, V, C의 각방생활

먼저 프로젝트를 생성합니다. 새 프로젝트 열기에서


ASP.NET MVC 2 Web Applicatoin 을 선택하고, 이름은 HelloMVC 로 하겠습니다. OK를 클릭하면 다음과 같이 유닛 테스트 프로젝트를 생성할 것인지 묻는 창이 뜹니다. (이게 ASP.NET MVC의 장점이라는 겁니다. 프로젝트 자체에서 유닛 테스트를 지원해주고 있습니다. 이 창에서 Yes 를 선택하면 간단하게 유닛테스트 프로젝트를 생성할 수 있습니다.) 이 시간은 유닛테스트와는 전혀 상관이 없는 관계로 No를 선택하도록 하겠습니다.


그러면, 다음과 같은 구조의 프로젝트가 생성된 것을 확인하실 수 있습니다.


Controllers, Models, Views 폴더가 각각 분리되어 생성되었습니다. 지나가는 얘기로, 저의 한가지 꿈이 여러개의 방이 있는 집을 갖는건데요.^^; 하나는 서재실, 또 하나는 음악실, 나머지 하나는 침실 뭐 이런거죠. 정말 각각의 방 안에서의 할일이란 명확합니다. 각각의 방에서 할일들을 한 방에서 하게 된다면... 잠시 상상해보겠습니다. 한쪽에서는 드럼과 피아노와 일렉기타의 절묘한 하모니의 시끄러움(?)이, 다른 한쪽에서는 책과 씨름하며, 또 다른 한쪽에서는 코를 곯며 자는 모습... 상상이 가십니까? 음악실에서는 음악을 연주하며 맘껏 소리높여 노래도 부르고, 서재실에서는 조용한 가운데 책도 보며 교양을 쌓고, 침실에서는 자면되는거죠. 모델, 뷰 그리고 컨트롤러 또한 각자의 할 일이 뚜렷하기 때문에 한 방에 같이 있을 수가 없습니다. 각방을 써야하는거죠^^ 

프로젝트가 생성될때 샘플페이지도 같이 생성이 됩니다. F5를 눌러서 확인해보겠습니다. 클릭하시면 디버깅을 할 수 있도록 Web.config 파일을 수정한다는 팝업창이 뜨는데요, OK하고 넘어가도록 합니다.


자, Welcome to ASP.NET MVC! 가 출력되는 것을 확인하실 수 있습니다. 우리를 먼저 반갑게 맞이해주는데요.^^ 기분좋네요~ 가만히 있을 순 없죠. 저도 인사를 해보도록 하겠습니다.

Hello! ASP.NET MVC!!!

먼저, 컨트롤러 폴더의 HomeController 클래스의 Index() 메쏘드를 다음과 같이 수정하겠습니다.

public ActionResult Index()
{
    ViewData["Message"] = "Hello! ASP.NET MVC!!!";
    return View();
}

소스 1 - /Controllers/HomeController.cs

컨트롤러는 ViewData 를 통해서 뷰에 데이터를 전달할 수 있습니다. 뷰에서는 인라인 코드로 ViewData에 접근할 수 있습니다. (자세한건 여기에서 'ViewData로 뷰에 전달하기'를 봐주세요) 수정한 내용은 F5 로 실행하여 확인할 수 있습니다.


그런데 주소를 보니 http://localhost:1589/로 되어있는데도 원하는 결과를 확인할 수 있습니다. ASP.NET MVC는 기존 웹폼에서처럼 직접적인 페이지 호출이 아닌 라우팅 룰에 의해 호출이 됩니다. ASP.NET MVC 애플리케이션이 처음 시작되면 Global.asax.cs 에 있는 Application_Start() 메쏘드가 호출되고 이는 라우트 테이블을 생성합니다.


소스 2 - Global.asax.cs

라우트 테이블은 들어오는 웹 요청을 3부분으로 나눕니다. 처음은 컨트롤러 이름과 매핑이되고, 두번째는 액션메쏘드와 매핑이 되고, 세번째는 그 메쏘드에 전달될 파라미터와 매핑이 됩니다. 예를들어, /Product/Detail/3 과 같이 요청이 들어오면 다음과 같이 나뉩니다.

Controller  = ProductController
Acton = Detail
Id = 3

예제에서 주소가 http://localhost:1589/  이렇게 되어도 라우트 테이블의 디폴트값인 HomeController의 Index 액션메쏘드를 호출하기 때문에 우리가 원하는 결과를 볼수 있었던 거죠^^

이제 인사는 나누었는데 처음 만남에 인사만 나누기는 섭섭하죠? ^^; 커피라도 한잔하며 얘기 나눠보
는게 좋겠죠? 흠.. 이번시간은 간단히 인사만 나누고 끝내려고 했는데요. 너무 금방 헤어지면 정말 아쉬울것 같아서 더 진행하는 것이니 간단하게 해보도록 하겠습니다. 아무쪼록 저의 허접함을 탓하지 마옵소서...(저 스스로 분발하도록 하겠습니다^^;)

우리 차라도 한잔?

먼저 차한잔 마실거니까요 Index.aspx 를 수정해서 차 마시러 가보도록 하죠^^

<h2><%= Html.Encode(ViewData["Message"]) %></h2>
<br />
차라도 한잔 하실래요?
<%= Html.ActionLink("네", "../Menu/MenuForm") %>

소스 3 - /Views/Home/Index.aspx

Html.ActionLink() 메쏘드는 A 태그를 만듭니다. 링크를 MenuForm으로 하네요. 물론 실행해 보면 에러가 나겠죠. 메뉴컨트롤러와 해당 액션메쏘드가 없기 때문이죠. 그러면 컨트롤러를 생성해보도록 하겠습니다.

using HelloMVC.Models;

namespace HelloMVC.Controllers
{
    public class MenuController : Controller
    {
        //
        // GET: /Menu/
        public ActionResult MenuForm()
        {
            List<Menu> MenuList = new List<Menu>();

            MenuList.Add(new Menu { Id = 1, Name = "커피", Price = "4000" });
            MenuList.Add(new Menu { Id = 2, Name = "레몬에이드", Price = "5000" });

            return View(MenuList);   
        }
    }
}

소스 4 - /Controllers/MenuController.cs

요란한 밑줄이 보이는 Menu 클래스도 생성하겠습니다.

public class Menu
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Price { get; set; }
}

소스 5 - /Models/Menu.cs

모델 클래스의 경우 생성을 하신 후에 꼭! 꼭! 꼭! 빌드를 하셔야합니다. 그래야 아래 보이는 Add View를 하실때 원하는 모델 클래스를 선택하실 수 있습니다.

ViewResult로 MenuList 모델을 파라미터로 뷰페이지로 리턴합니다. 액션메쏘드에 아무데서나 마우스 오른쪽 버튼을 클릭하여 뷰페이지를 생성하도록 하겠습니다.


Add View를 클릭하면 다음과 같은 팝업이 뜹니다. 먼저 저희가 컨트롤러에서 메뉴를 추가한 리스트를 확인해보기 위해서 다음과 같이 세팅하도록 하겠습니다.


Create a stringly-typed view를 선택하고(뷰를 생성할때 모델과 강력한 결합을 이끌게 되면 직접적으로 모델의 애트리뷰트들을 액세스할수 있게 됩니다.), View data class 를 Menu클래스로 선택합니다(여기서 Menu 클래스가 안보이시면 빌드를 안하신거에요. 빌드하셔야죠!). 마스터 페이지의 체크를 해제하도록 합니다. Add 버튼을 클릭하시면 MenuForm 페이지가 생성되는 것을 확인하실 수 있습니다. 바로 실행을 해보시면,


잘나오네요. 그러면 소스를 조금(?) 수정하도록 하겠습니다.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IEnumerable<HelloMVC.Models.Menu>>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>MenuForm</title>
</head>
<script type="text/javascript">
    var menuList = new Array();
    function Menu() {
        this.id;
        this.name;
        this.price;
        this.quantity = 0;
        this.cPrice;
    }
    function addMenu(id, name, price) {
        var menuNum = -1;
        for (var k = 0; k < menuList.length; k++) {
            if (id == menuList[k].id) {
                menuNum = k;
                break;
            }
        }
        if (menuNum == -1) {
            var mn = new Menu();
            mn.id = id;
            mn.name = name;
            mn.price = price;
            mn.quantity++;
            mn.cPrice = price;
            menuList.push(mn);
        }
        else {
            menuList[k].quantity++;
            menuList[k].cPrice = menuList[k].quantity * parseInt(menuList[k].price);
        }

        var str = "";
        var totalPrice = 0;
        for (var i = 0; i < menuList.length; i++) {
            str += menuList[i].name + " " + menuList[i].quantity + "잔 " + menuList[i].cPrice + "<br />";
            totalPrice += parseInt(menuList[i].cPrice);
        }
        document.getElementById("divMenuList").innerHTML =
str + "<br /> 돈부터 내시죠~ 여긴 선불!! " + totalPrice + "원 되겠습니다~~^^";
    }
</script>
<body>
    <h2>Menu</h2>
    <table width="400px" cellspacing="1" style="background-color:#CCCCCC">
    <% foreach (var menu in Model) { %>
    <tr>
        <td style="background-color:#EEEEEE"><%= Html.Encode(menu.Name) %></td>
        <td style="background-color:#AAAAAA" align="center"><%= Html.Encode(menu.Price) %></td>
        <td style="background-color:#BBBBBB">
<a href="javascript:addMenu('<%= menu.Id %>', '<%= menu.Name %>', '<%= menu.Price %>');"><%= Html.Encode(menu.Name)%> 추가요</a></td>
    </tr>  
    <% } %>       
    </table><br />
    =================== 주문서 ===================<br /><br />      
    <div id="divMenuList" style="border:1px solid #E3E3E3; width:400px; height:200px" ></div>
</body>
</html>

소스 6 - /Views/Menu/MenuForm.aspx

이상하게 복잡스럽게 보이지만(죄송합니다__) 수정된것은 테이블에 색깔준것과 주문추가 이벤트, 주문서네요.
결과물을 보면


추가 이벤트를 일으킬때마다 주문서가 수정되는 간단한 예제를 보고 계십니다.^^

마무리요

마지막의 결과물은 간단한데 너무 복잡하게 표현한건 아닌지 심히 걱정이 됩니다. 제 실력이 여기까지인지라..다음 포스팅때는 더 깔끔하고 세련된 코드로, 더 나아진 모습으로 찾아뵙도록 하겠습니다.


참고자료 :
http://www.techbubbles.com/aspnet/aspnet-35-mvc-application/

Welcome to Dynamic C#(11) - The Phantom of The Dynamic

C# 2010. 1. 14. 09:00 Posted by 알 수 없는 사용자
  이 포스트에서 소개해드리는 유령메서드는 정식버전에서는 없어진 개념입니다. 너무나도 복잡한 과정을 거쳐야 하기 때문에, 과정을 단순화 시키느라고 기존에 논의하던 개념들을 채택하지 않았다고 하는데요. 이 포스트는 예전에 이런 내용이 논의되었었다하는 정도로 봐주시구요. 실제 dynamic과 관계된 메서드 오버로딩 판별에 대해서는 이 글을 참조해주시기 바랍니다.

- 빙산의 일각!


사실 처음에는 그냥 단순히.. dynamic이란게 추가됐으니, '아 이거 쵸낸 신기하구나', '아 난 귀찮은 거 싫어하는데 이거 때매 이런거 편해지겠구나'하는 생각으로 접근을 했었는데요. dynamic이라는 키워드가 하나 추가되면서 생긴 많은 양의 추가사항들과 이야기들이 빙산 아래쪽으로 숨어 있었네요. 근데, 개인적인 게으름으로 아직 제대로 이야기 드리지 못하는거에 대해서 죄송하게 생각하구요-_- 일단, 원문을 한번 걸러서 약간의 창작을 보태는 수준에 불과하지만 계속해서 최선을 다해서 전달해드리고자 합니다.(저도 궁금하긴 하거든요-_-) 따쓰한 피드백!! 크하하하-_-


- 유령...뭐...?

유령 메서드(the phantom method)는 이 포스트시리즈의 바탕이 되는 Sam Ng의 포스트에서 언급이 되는 용어인데요. 컴파일러가 초기 바인딩단계에서 정적으로 해결할 수 없는 동적 바인딩을 해야하는 경우 사용하는 메서드를 말합니다. 즉, 실체는 없지만 컴파일러 내부에서 문제해결을 위해서 사용한다고 볼 수 있겠죠.

public class C
{
    public void Foo(int x)
    {
    }
   
    static void Main(string[] args)
    {
        dynamic d = 10;
        C c = new C();
        c.Foo(d);
    }
}

위와 같은 코드가 있다고 할때요, Foo의 호출을 바인드하기 위해서 컴파일러는 오버로드 판별 알고리즘을 통해서 C.Foo(int x)같이 딱들어맞는 후보메서드가 포함되어 있을 후보군을 만듭니다. 그리고 매개변수가 변환가능한지를 판단합니다. 그런데, 아직 dynamic의 변환가능성에 대해서는 이야기를 해본적이 없으므로~ 일단, 그거에 대해서 먼저 이야기 해보도록 하겠습니다.

우선, 빠르고 간단하게 정의를 내려보자면, "모든 타입은 dynamic으로 변환이 가능하고, dynamic은 어떤 타입으로도 형변환 할 수 없습니다." 말이 안된다고 생각하실 수 있습니다. 그럼 그 의문을 풀기 위해서 형변환을 고려할 수 있는 상황들을 생각해보기로 하죠.
(주석) 원문에서 "everything is convertible to dynamic, and dynamic is not convertible to anything"이라고 하고 있는데요, 조금 알아보니 Sam Ng가 이야기한 건 아마 암시적 형변환을 두고 이야기 한 것 같습니다. dynamic에서 다른 타입으로 명시적 형변환은 가능합니다. 그리고 이에 대해서 다음 포스트에서 좀 더 자세하게 말씀 드리겠습니다. 

우선, c를 정적인 타입이라고 하고, d를 동적인 타입의 표현식이라고 했을때요, 형변환에 대해서 고려해볼 수 있는 상황은 아래와 같습니다.

1. 오버로드 판별 - c.Foo(d)
2. 대입 형변환 - C c = d
3. 조건문 - if(d)
4. using절 - using(dynamic d = ..)
5. foreach - foreach(var c in d)

일단, 이번 포스트에서는 1번에 대해서 살펴보도록 하구요, 나머지는 다음기회로 미루겠습니다.


- 오버로드 판별의 경우

일단 매개변수 변환가능성으로 돌아가서 생각해보겠습니다. dynamic은 어떤 타입으로도 형변환 할 수 없기 때문에, d는 int로 형변환이 불가능합니다. 하지만, 모처럼 dynamic타입인 매개변수를 썼으니 오버로드 판별도 동적으로 일어났으면 합니다. 그래서 여기서 유령 메서드가 등장합니다.

유령메서드는 오버로드 판별의 후보군에 슬쩍 추가되는데요, 파라미터개수가 똑같고 모든 파라미터가 dynamic타입인 메서드입니다.

즉, 후보군에 Foo(int x, int y), Foo(string x, string y)가 있다면, 유령메서드는 Foo(dynamic x, dynamic y)처럼 생겼겠죠.

유령 메서드가 후보군에 끼어들게되면, 당연하겠지만, 다른 후보군 메서드와 똑같이 취급됩니다. '모든 타입은 dynamic으로 형변환이 되지만, dynamic은 어떤 타입으로도 형변환이 안된다.'는 명제를 다시 한번 떠올려 볼 때입니다. 아주 적절한 타이밍이죠. 이 말은 dynamic타입의 매개변수가 주어진 상황에서 다른 모든 오버로드 후보군은 판별을 통과하지 못하지만, 유령 메서드는 유유히 판별을 통과할 수 있다는 말입니다.

서두에서 제시했던 예제를 다시 보시면, 한개의 dynamic타입을 매개변수로 받습니다. 이 상황에서 오버로드는 두개인거죠. Foo(int)와 유령 메서드인 Foo(dynamic). 전자는 판별을 통과하지 못합니다. dynamic은 int로 형변환이 안되기 때문이죠. 하지만 후자는 성공합니다. 그래서 그 메서드에 호출을 바인드하게 되는거죠.

그리고 호출이 유령 메서드에 바인드 되면, 컴파일러는 DLR을 통해서 런타임에 호출이 제대로 이루어 지도록 조치를 취하는 거죠.

그럼, 한가지 의문이 남는데요. 유령 메서드는 언제 끼어들게 되는 걸까요?


- 유령이 끼어드는 그 순간

컴파일러가 오버로드 판별을 할때, 초기 후보군을 놓고 고심을 합니다. 메서드 호출에 dynamic 매개변수가 있다면, 컴파일러는 후보군을 찬찬히 살펴보면서 유령 메서드를 소환해야 하는지 고심합니다. 유령 메서드가 끼어드는 상황은 아래와 같습니다.

1. dynamic이 아닌 모든 매개변수는 후보군에 있는 파라미터로 형변환이 가능하다.
2. 최소한 한개 이상의 dynamic매개변수가 후보군에 있는 파라미터로 형변환이 불가능하다.

일전에, dynamic타입인 매개변수가 포함된 메서드 호출이라도 정적으로 바인드될 수 있다고 말씀을 드렸었는데요. 이 경우가 2번으로 설명이 됩니다. dynamic매개변수와 일치하는 dynamic파라미터를 갖는 후보메서드가 있다면, 호출은 그 메서드로 정적 바인딩이 됩니다.

public class C
{
    public void Foo(int x, dynamic y)
    {
    }
   
    static void Main(string[] args)
    {
        C c = new C();
        dynamic d = 10;           
        c.Foo(10, d);
    }
}

위와 같은 경우, 오버로드 후보군에 정확하게 Foo호출의 매개변수인 int와 dynamic타입에 일치하는 메서드가 있기 때문에, 정적으로 바인딩되고, dynamic lookup이 발생하지 않습니다. 즉, 오버로드 판별시에 후보군을 한번 쭉 훑었는데도 유령 메서드가 끼어들지 않았다면, 비록 dynamic타입의 매개변수가 있다고 하더라도 원래 하던대로 오버로드 판별을 진행하는 거죠.


- 유령메서드를 통한 할당과 dynamic receiver를 통한 할당은 뭐가 틀린거냐

dynamic receiver의 경우는 런타임 바인더가 실제 런타임시의 receiver의 타입을 기준으로 오버로드 판별을 하려고 합니다. 하지만 유령메서드가 끼어든 할당의 경우는 컴파일타임의 receiver의 타입을 기준으로 진행되는 거죠.

그냥 직관적으로 생각해봤을때, receiver를 컴파일타임에 알 수 있다면, 매개변수에 dynamic타입이 있다고 하더라도 오버로드 판별의 후보군은 컴파일 타임에 밝혀져야 하는 거겠죠.


- 마치면서

사실, 이 부분에 대해서 좀 더 언급할 사항들이 있습니다. 나머지 형변환 케이스에 대해서 더 나아가기 전에, dynamic의 형변환에 대한 부분과 오버로드에 대한 이야기를 좀 더 하려고 하는데요. 여러분과 제가 가진 의문이 조금씩 더 해결됐으면 하는 생각입니다.


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/09/dynamic-in-c-iv-the-phantom-method.aspx


[JumpToDX11-9] Multi-threaded Rendering 을 위한 API.

DirectX 11 2010. 1. 11. 09:00 Posted by 알 수 없는 사용자



이번 시간에는 Multi-threaded Rendering 을 위한 API 들에 대해서 살펴보겠습니다.
기능 위주의 설명을 위해서 인자들에 대한 명시는 생략했습니다.
이점 주의해주시기 바랍니다.

ID3D11Device::CreateDeferredContext()

가장 먼저 살펴볼 것은 DeferredContext 의 생성입니다.
DeferredContext 는 스레드당 하나씩 생성되어질 수 있음을 앞선 시간을 통해서 언급했습니다.
또한 이 DeferredContext 는 Command List 들을 생성해서 가지고 있습니다.
즉, 렌더링이 가능한 상태라는 것입니다.
그런 기능을 우리는 Device 인터페이스를 통해서 생성합니다.
이것은 역시 Free thread 한 작업이기 때문에 Device 인터페이스를 이용합니다.

하나의 DeferredContext 는 thread-safe 합니다.
즉, 스레드 상에서 DeferredContext 가 관련 Command 들을 기록하는 것은 안전한 작업입니다.

간단한 사용 방법은 아래와 같습니다.
ID3D11DeviceContext* pDeferredContext = NULL;
hr = g_pd3dDevice->CreateDeferredContext(0, &pDeferredContext);


ID3D11DeviceContext::FinishCommandList()

신기하게도 우리는 이 API 호출 한번으로 CommandList 들을 기록하고 생성할 수 있습니다.
API 이름이 Finish 여서 Start나 Begin 계열의 API 를 검색해 보았지만, 없었습니다.^^
각각의 DeferredContext 별로 호출되기 때문에 DeviceContext 의 멤버함수로 되어 있습니다.
앞선 시간을 통해서 DeviceContext 는 ImmeidateContext 와 DeferredContext 로
분리될 수 있다고 언급했었습니다.
두 Context 모두 ID3D11DeviceContext 인터페이스를 사용하기 때문에 오해의 소지가 약간 있습니다.
FinishCommandList 는 DeferredContext 를 위한 API 임을 유념하시기 바랍니다.

간단한 사용 방법은 다음과 같습니다.
ID3D11CommandList* pd3dCommandList = NULL;
hr = pDeferredContext->FinishCommandList( FALSE, &pd3dCommandList );


ID3D11DeviceContext::ExecuteCommandList()

이 API는 DeferredContext 에 의해서 생성된 CommandList 들을 실행합니다.
역시나 ID3D11DeviceContext 의 멤버함수이기 때문에 혼란스러울 수 있습니다.
과연 ImmediateContext 가 이 함수를 호출할까요? 아니면, DeferredContext 일까요?

지난 시간들을 통해서 우리는 실제로 Multi-threaded Rendering 이라는 것은
CommandList 생성을 Multi-thread 기반으로 하는 것이라고 언급했었습니다.
그 이후에 실제 그래픽 카드로의 전송은 하나의 스레드만 할 수 있다고 했었습니다.
바로 그 사실입니다.
이 함수는 ImmediateContext 에 의해서 호출됩니다.
즉, 이 API 는 그래픽 카드로 해당 CommandList 들을 전송하는 것입니다.

간단한 사용 방법은 아래와 같습니다.
g_pImmediateContext->ExecuteCommandList( g_pd3dCommandList, TRUE );


이상 3가지 API 에 대해서 살펴보았습니다.
믿기지 않으시겠지만(?)
Multi-threaded Rendering 작업은 이 세가지 API로 할 수 있습니다.
나머지는 스레드 생성과 제어를 위한 작업이 결합되어야 할 것입니다.
일반적인 스레드 프로그래밍과 관련된 내용이라 이곳에서는 배제를 했습니다.
현재 DirectX SDK Sample 에는 'MultithreadedRendering11' 라는 것이 있습니다.( 2009 August 버전 기준 )
이것과 관련된 소스가 있으니 참고해서 보시면 좋을 것 같습니다.

이상으로 Multi-threaded Rendering 의 기본 개념 설명을 마치고자 합니다.
이 부분과 관련된 내용은 앞으로 정리가 되는대로 추가하거나 수정이 되어질 수 있을 것입니다.
다음 시간부터는 DirectX11 의 다른 주제를 가지고 돌아오겠습니다.^^
 

Welcome to Dynamic C#(10) - Dynamic Returns Again.(2)

C# 2010. 1. 11. 09:00 Posted by 알 수 없는 사용자

- 살림살이 좀 나아시졌습니까.

날씨가 계속 춥네요. 난데없이 목이 부어서 지난 금요일에는 조퇴를 했습니다-_-. 다들 건강 조심하시구요. 날씨가 추워서 그런가 여기저기서 어려움을 호소하는 목소리가 들리는 거 같습니다. 다들 하시는 일 잘되고 살림살이 좀 나아지셨으면 좋겠네여!


- 이어서 이어서!

지난포스트에서 보셨던 예제를 다시한번 보시죠.

dynamic d = 10;
C c = new C();

d.foo();
d.SomeProp = 10;
d[10] = 10;

c.Foo(d);
C.StaticMethod(d);
c.SomeProp = d;

지난 포스트에서는 위쪽 그룹에 대해서 다뤘었는데요, 이번 포스트에서는 위 두 그룹중에서 아래쪽 그룹에 대해서 설명드려 보겠습니다. 우선, 가장 간단한 부분부터 다뤄보려고 하는데요, 아래와 같은 코드가 있다고 가정해보죠.

public class C
{
    public void Foo(decimal x) { ... }
    public void Foo(string x) { ... }
    static void Main(string[] args)
    {
        C c = new C();
        dynamic d = 10;
        c.Foo(d);
    }
}

이 코드가 실행되면 어떤일이 벌어질까요? 직관적으로 생각해봤을때... 지역변수c의 타입이 C라는 걸 알 수 있으니깐, C의 오버로드 2개중에 하나가 실행될거라고 생각해볼 수 있습니다. 근데, d의 타입이 dynamic이라는 것도 위 소스코드에서 알 수 있는데요, 그렇다는 이야기는 d의 실제 타입은 런타임에서 알 수 있으므로 컴파일러는 어떤 오버로드를 호출해야 하는지 판단할 수가 없습니다.

그래서 이렇게 생각해볼 수 있습니다. 컴파일러는 호출가능한 후보군을 추출해서 집합을 만들고, 런타임에 실제로 오버로드 판별을 통해서 적합한 메서드를 호출한다고 말이죠. 위의 경우에는 d의 값이 10이므로, 아마도 호환이 안되는 string보다는 decimal을 인자로 받는 오버로드가 호출될거라고 예측해볼 수 있습니다. 그러면, 좀 더 구체적으로 이야기 해보면서 뭐가 예측이랑 다르게 돌아가는지에 대해서 이야기 해보죠.

public class C
{
    public void Foo(decimal x) { ... }
    public void Foo(string x) { ... }
    static void Main(string[] args)
    {
        C c = new D();
        dynamic d = 10;
        c.Foo(d);
    }
}

public class D : C
{
    public void Foo(int x) {...}
}


클래스 D를 C로 부터 상속했고, c를 D의 생성자로 생성한 게 차이점인데요. 그리고 런타임에 c의 타입은 D일텐고, D에는 C가 가진 모든 오버로드보다 더 이 경우에 적합한 int형 파라미터를 가진 Foo가 있습니다. 10은 int형으로 간주될테니, D의 Foo가 가장 적합한 선택이 되겠죠.

그런데, 결과는 어떨까요? 이 부분에 대해서 잘 아시는 분이나 코드를 작성하다가 이상한 점을 발견하신 분이라면 대답하실 수 있으실텐데요. 아래의 그림에서 확인을..


네. 분명히 오버로드가 총 3개여야 할텐데, 두개 밖에 안 나옵니다. 그래서 분명히 D의 Foo가 가장 좋은 선택임에도 불구하고 계속해서 C의 Foo(decimal x)가 호출이 됩니다. 하지만, c를 생성할때 "D c = new D();"나 "dynamic c = new D()"처럼 생성하면, D의 Foo(int x)가 호출이 됩니다. 왜 그럴까요?


- 내가 알면 이글 보고 있겠냐.

dynamic과 관련된 동적 바인딩을 설계할때, 최대한 동적바인딩이 기존의 컴파일러가 정적바인딩에서 하던 짓을 비슷하게 하게끔 유지했다고 합니다. 그래서 그 결과로 dynamic이라고 명시된 매개변수나 receiver가 아니라면, 컴파일타임에 확인할 수 있는 타입을 그 변수의 타입으로 간주한다고 합니다. 즉, 위의 예제에서 c.Foo의 오버로드로 D.Foo(int x)가 포함이 안된 이유는, "C c = new D();"의 결과로 c가 런타임에 가질 타입은 D겠지만, 컴파일타임에서는 C라고 간주한다는 겁니다. 그래서 아무리 인텔리센스를 뒤져봐도 D의 Foo를 발견할 수 없으며 호출도 할 수 없는 거죠.

하지만, "D c = new D();"나 "dynamic c = new D()"처럼 생성하면, 전자의 경우는 컴파일 타임의 c의 타입을 D로 간주하므로 오버로드 3개모두를 인텔리센스에서 확인하실 수 있구요, 후자의 경우는 동적 바인딩을 통해서 c의 런타임 타입이 D임을 알기때문에, 오버로드 후보군에서 Foo(int x)를 골라낼 수 있습니다. 예제를 하나 더 보면요.

public class C
{
    public void Foo(int x, object o)
    {
        Console.WriteLine("Foo(int x, object o)");
    }

    static void Main(string[] args)
    {
        C c = new D();
        dynamic d = 10;
        c.Foo(d, c);
    }
}

public class D : C
{
    public void Foo(int x, D d)
    {
        Console.WriteLine("int x, D d");
    }
}

위의 경우와 마찬가지로, C.Foo(int x, object o)가 호출됩니다. 같은 이유로 말이죠.


- 마치면서

사실 이번 포스트는 좀 애로사항이 있었는데요. 제 실력부족으로 참고했던 원문이 잘 이해가 안가서 말이죠. 그래서 제 나름대로 이런저런 실험을 해보다가 결론을 내렸습니다. 바로 이럴때가 고수님들의 나눔이 필요한 시점입니다. 혹시 더 자세하게 아시는 분이 있다면, 따쓰한 피드백으로 풍성하게 해주시기 바랍니다. "따쓰한" 잊지마세염^^;;;


-참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/06/dynamic-in-c-iii-a-slight-twist.aspx


Welcome to Dynamic C#(9) - Dynamic Returns Again.

C# 2010. 1. 7. 09:00 Posted by 알 수 없는 사용자

- 정말 오랜만에 다시 Dynamic이군요.

안녕하세요~! 눈 때문에, 어떤 사람들은 로맨틱한 겨울이고, 어떤 사람들은 악마의 똥가루의 냄새에 신음하고, 어떤 사람들은 방에 콕처박혀 있고 뭐 아주 버라이어티한 겨울입니다. 겨울이 버라이어티 정신이 충만하네요. 연예대상같은거라도 하나 받고 싶은 가봐요. ㅋㅋ 아무튼! 정말 오랜만에 다시 dynamic시리즈를 쓰게 되네요. 워낙 한 내용도 없이 중간에 끊어서 좀 그랬습니다;;; 물론, 기다리신 분이 얼마나 있을지는 미지수지만요-_- 그럼. 한번 이야기를 시작해볼까요? dynamic에 대해서 조금씩 자세하게 들어가 보겠습니다.


- 어서내놔 dynamic

우선 예제를 하나 보시죠.

dynamic d = 10;
C c = new C();

//위쪽 그룹 
d.foo();
d.SomeProp = 10;
d[10] = 10;

//아래쪽 그룹 
c.Foo(d);
C.StaticMethod(d);
c.SomeProp = d;




위 그룹과 아래 그룹의 차이점은 뭘까요? 네~! Give that man a cigar!(누가 정답을 말했을때 하는 말이라네요) 위 그룹은 액션을 받는 객체가 동적인 객체, 즉 dynamic receiver이구요. 아래 그룹은 static receiver와 static method가 바로 차이점입니다.

위 그룹은 동적인 표현식(expression)속에서 직접적으로 동적인 행위가 일어나고, 아래그룹은 직접적으로  동적표현식은 아닙니다. 각각의 연산의 매개변수로 동적인 타입이 들어가면서, 전체적인 표현식을 간접적으로 동적으로 만들고 있는거죠. 이런 경우에는 컴파일러가 동적인 바인딩과 정적인 바인딩을 섞어서 수행하는데요. 예를 들어서 동적타입을 매개변수로 받는 오버로드가 있을 경우에, 어떤 멤버집합(member set)을 오버로드해야 할지 결정할때는 정적인 타입을 사용해서 판단할테구요, 실제로 오버로드를 판별(resolution)할때는 매개변수의 런타임 타입을 사용할 것이기 때문이죠.

컴파일러가 dynamic타입인 표현식을 보게되면, 그 안에 포함된 연산들을 동적 연산처럼 처리하게 됩니다. 즉, 표현식이 인덱스를 통한 접근이든 메서드호출이든 상관없이 그 표현식의 결과로 나오는 타입은 런타임에 결정될거라는 거죠. 그 결과로 컴파일 타임에 동적인 표현식의 결과로 나오는 타입은 dynamic이겠죠.

컴파일러는 이런 모든 동적인 연산들을 DLR을 통해서 dynamic call site라는 걸로 변환을 합니다. 지지지난 포스트에서 설명을 드렸던거 같은데요, 제네릭한 델리게이트를 가지고 있는 정적 필드입니다. 어떤 연산에 대한 호출을 가지고 있다가, 추후에 같은 타입의 연산이 호출되면 다시 call site를 생성할 필요없이 정적필드에 저장된 델리게이트를 호출해서 실행에 필요한 부하를 최대한 줄이는데 도움을 주는 친구죠. call site가 만들어지면, 컴파일러는 그 call site에 저장된 델리게이트를 호출할 코드를 생성하구요, 거기에다가 매개변수를 넘겨줍니다.

만약에, 호출한 객체가 IDynamicObject를 구현해서 스스로 동적 연산을 어떻게 처리할지 아는 객체가 아니거나, 미리 저장된 델리게이트와 타입이 안맞아서 캐시가 불발이 나면, call site와 같이 생성된 CallSiteBinder가 호출됩니다. CallSiteBinder는 call site에 필요한 바인딩을 어떻게 처리해야 하는지 알고 있는 객체인데요, C#은 이 CallSiteBinder에서 상속한 바인더를 갖고 있습니다. 이 C# CallSiteBinder가 적절한 바인딩을 통해서 DLR의 call site가 갖고 있는 델리게이트에 저장될 내용을 expression tree형태로 만들어서 리턴합니다. 이 내용역시 전전, 전포스트에서 다뤘었쬬? 못봤다고 하시면!!!! 제가 절대 가만있을수는 없는 문제고! 링크를 드..드리겠습니다. 전포스트, 전전포스트. 친절하죠?-_-


- 캐시되는 과정은 어떠냥

공개된 문서를 통해 볼 수 있는 현재의 캐시 방식은 그냥 단순히 매개변수들의 타입이 일치하는지 검사하는겁니다.  만약에.. 이런 호출이 있다고 할때...

args0.M(arg1, arg2, ...);

그리고, 이전에 args0이 C라는 타입이며, 매개변수 arg1과 arg2가 모두 int인 호출이 있었다고 해보면요, 캐시를 체크하는 코드는 대략아래와 같습니다.

if (args0.GetType() == typeof(C) &&
    arg1.GetType() == typeof(int) &&
    arg2.GetType() == typeof(int) &&
    ...
    )
{
    //CallSiteBinder의 바인드 결과는 여기에 계속 통합되구요
}
    ......//캐시 검사는 좀 더 많을 수도 있구요
else
{
    //여기서 CallSiteBinder의 bind메서드를 호출하고, 캐시를 업데이트 합니다.
}

지금까지 간단하게 알아본 내용을 그래도 마무리 하려면, C# CallSiteBinder가 뭘 어떻게 하는지를 알면 되겠네요. 서두에 두그룹의 연산중에 위 그룹의 연산을 보면요, 메서드 호출, 속성 접근, 인덱서 호출등 3가지 연산이 있었죠. 일단 모든 연산은요 표준 C# runtime binder를 통해서 생성되고, C# runtime binder가 걔네들을 데이터 객체로 사용합니다. 그 데이터객체는 바운드되야할 액션을 설명하는데요, 그런 객체를 C# payload라고 부른다고 합니다. 

C# runtime binder는 쉽게 작은 컴파일러라고 생각하면 되는데요, 얘가 일반적인 컴파일러가 갖고 있는 심볼테이블이나 타입시스템, 오버로드 판별 및 타입 교체같은 기능을 갖고 있기 때문입니다. 간단하게 d.Foo(1)을 예로 생각해보죠.

runtime binder가 호출되면, 현재 call site에 대한 payload과 call site에 대한 런타임 매개변수를 갖습니다. 그리고 dynamic receiver를 포함해서 그 모든 런타임 매개변수와 타입을 모아서는 그 타입에 대한 심볼테이블을 만듭니다.(심볼테이블에 대한 간략한 설명은 여기를 참조하세영!) 그리곤 payload꾸러미를 풀어헤쳐서 수행하려고 하는 연산의 이름을 꺼냅니다.(Foo) 그리고 d의 타입에서 리플렉션을 사용해서 Foo라는 이름을 갖는 모든 멤버를 뽑아냅니다. 그리고 걔네들도 심볼테이블에 적어넣죠. 말로 설명하니깐 깝깝하시죠? 설명하는 저도 깝깝하네여-_-;;; 제가 상상력을 동원해서 부연설명을 드리면요,

d.Foo(1)에서 먼저 매개변수의 타입과 d의 타입을 갖고와서 심볼테이블에 적어두고요.

주소     타입            이름
서울시   int             익명(= 1)

수원시   dynamic      d

그리고 리플렉션으로 d의 타입에서 Foo를 모두 찾아냈는데 대략 아래와 같다고 해보죠.
Foo(int a)
Foo(double b)
Foo(string c)

그리고 얘네들도 따로 심볼테이블에 집어넣으면?

-call site에 대한 심볼테이블
주소     타입            이름
서울시   int            익명(= 1)
수원시   dynamic      d

-d의 멤버중에 Foo라는 동명이인들
주소      타입        이름
부산시   void     Foo(int a)
창원시   void     Foo(double b)
안양시   void     Foo(string c)

그러면, 타입을 찬찬히 들여다보면, 어떤 Foo가 호출되야 할지 명확하게 보입니다. d.Foo(1)호출에서 매개변수의 런타임타입이 int이므로 Foo(int a)가 호출이 되겠죠. 이건 그냥 제가 설명을 위해서 상상력을 동원해본거니깐요 믿지는 마시기 바랍니다. 예비군 동원 무쟈게 귀찮으시져? 상상력도 무쟈게 귀찮아 하네요-_-. 어서 집에 보내고 다시 설명을 이어 가겠습니다.

위에서 설명드린 runtime binder를 설계할때 세웠던 한가지 원칙은 "runtime binder는 정적 컴파일러가 하는 짓을 똑같은 의미로 할 수 있어야 한다."였다고 하는데요. 그래서 에러메세지 역시 동일한 에러메세지를 뱉어낸다고 합니다.

위의 바인딩의 결과로 바인딩이 성공적일 경우에 수행할 동작을 표현한 expression tree가 만들어집니다. 그렇게 안되는 경우에는 runtime binder exception을 던진다고 하네요. 결과로 만들어진 expression tree는 DLR의 캐시에 포함되고 호출되면서 원래의 호출을 성공적으로 완료합니다.


- 약간의 제약사항?

그런데, 위에서 정적 컴파일러와 똑같은 짓을 하게 만들려고 했지만, 아마도 예산과 시간때문에 선택과 집중을 해야 하니깐 몇가지 못집어 넣은게 있다고 합니다. 람다식과 확장 메서드, 메서드 그룹(델리게이트)에 대한 이야기 인데요. 현재로서는 바인딩 안된 람다식을 런타임에서 표현할 방법이 없다고 합니다. 개발하다가 디버깅을 할때 브레이크 포인트를 잡고 그 상태에서 현재 상태의 객체에 값을 가져온다거나 메서드를 호출하고 값을 확인할 수 있잖아요? 근데, 람다식은 그런식으로 디버깅이 안됐던거 같은데, 아마 그문제가 계속 이어지는 거 같습니다.

그리고 메서드 그룹역시 런타임에 표현할 수 있는 방법이 없다고 합니다. 예를 들면,

delegate void D();
public class C
{
     static void Main(string[] agrs)
     {
        dynamic d = 10;
        D del = d.Foo; //뭐 이렇게는 안된다고 하네요. 그래서 런타임 익셉션이 난다고 합니다.
     }
}

그리고 확장 메서드 역시 using절과 범위를 바인딩없이 넘겨줄 방법이 없기때문에, 확장메서드역시 안된다고 하구요.


- 마물!

무척 오랜만의 포스팅인데요, 갈증이 조금이라도 해소가 되셨으면 좋겠네요. 제 실력이 바닥을 기다보니 원문의 내용을 한번 걸러서 드리는 정도밖에 못드리는 면이 많은데요. 뭐-_- 내공이 부족하니 한계가 명확하네요. 그럼 다음 포스트에서 뵙져~!!!!!!!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx


안녕하세요. 이번에 새롭게 팀원이된 박세식이라고 합니다. 많이 부족해서 컴터앞에 앉아 이렇게 자판을 두드리고 있는 시간에도 떨림이 멈추질 않네요-_-; 앞으로 잘 부탁드리겠습니다.(따스~한 피드백 아시죠^^;)  
자~ 그럼. ASP.NET MVC에 대해 알아보도록 하겠습니다.

첫번째 시간으로 ASP.NET MVC vs ASP.NET WEB FORM 에 대해 글을 써보도록 하겠습니다. 제 포스트는 ASP.NET MVC에 관한 글입니다.^^; 그래서 이 둘의 대결구도라기 보다는 웸폼의 문제점을 짚어보고 MVC에 좋은 점에 대해서 글을 써 나가려고 합니다.


ASP.NET WEB FORM의 문제점?

ASP.NET WEB FORM은 ASP.NET 개발의 전통적인 스타일이고, 큰 스케일의 웹사이트를 좀더 간단하게 만들게 해주는 기술입니다. 웹폼은 드래그 앤 드랍으로 컨트롤들을 ASP.NET 페이지에 추가하고 그것들에 맞는 코드를 작성합니다. 이러한 개발방식이 개발자들의 마음을 끄는거죠. 그!러!나! 웹폼은,

관계가 분리되어 있지 않습니다. UI와 코드가 섞여있죠--;
● 자동적으로 테스트하는 것이 쉽지 않습니다. 런타임 시작없이 테스트하기가 어렵죠. 이는 실행환경을 쭉~ 돌면서 테스트할 수 밖에 없다는 겁니다-_-;
● 상태정보를 저장하려면 각 서버페이지의 상태를 뷰스테이트라고 하는 히든 필드값으로 클라이언트 페이지에 저장합니다.

하지만, 웹폼 개발에서의 특징인 뷰스테이트와 포스트백은 축복이자 파멸의 원인이됩니다. 뷰스테이트는 클라이언트와 서버간의 오고가는 데이터들의 상태를 히든 필드로 구성하고 있는 메커니즘으로, 이것의 크기는 무지막지하게 커질 수 있습니다. 매번 호출시 이 거대한 데이터가송수신된다니 끔찍스럽죠-_-; 특히 페이지에 데이터그리드나 그리드뷰와 같은 서버컨트롤이 포함되어있다면 한페이지 한페이지 넘길때마다 응답지연의 원인이 되어 사용자들을 기다림에 지치게 만드는거죠 OTL;; 또한 포스트백은 매번 액션에 의한 트리거로서 발생이됩니다. 그 거대한 데이터(뷰스테이트)를 어떤 액션만 취하게 되면 다시 그리게 되는거죠. 이 뷰스테이트에 대해 좀더 알아보죠.


뷰스테이트(View State)란?

뷰스테이트는 포스트백시 웸폼의 상태의 변화를 유지시켜주는 기술입니다. 일반적으로 __VIEWSTATE라는 이름의 히든 필드로 웹페이지에 위치합니다. 이 히든값은 수십 킬로바이트씩 쉽게 커질 수 있습니다. 이 뷰스테이트는 사용자가 웹 페이지를 포스트백 할 때 천천히 불려지고(다운로드), 그 히든 값의 내용을 웹 요청시에 포스트해서 요청 시간을 길어지게 합니다. ASP.NET 웹 페이지의 요청이 올 때 정확하게 무슨 일이 일어나는지를 알려면 ASP.NET 페이지의 생명주기(Life Cycle)을 알아봐야 하는데요. 잠깐 간단하게만 알아보도록 하죠^^;(어째~ 이야기가 점점 옆으로 새네요.. 언제 제자리로 돌아올지는 생각하지 말아주세요-_-;)


Asp.Net Page Life Cycle

매번 ASP.NET 웹 페이지에 의해 서버에 요청이 오면 첫째로 웹 서버는 ASP.NET 엔진으로 요청을 넘깁니다. ASP.NET 엔진은 웹 페이지의 올바른 파일 접근을 확인하는 여러 단계로 구성된 파이프라인을 통해서 사용자의 세션 정보를 얻습니다. 파이프라인의 끝에서는 요청된 웹 페이지에 상응하는 클래스를 인스턴스화하고 ProcessRequest() 메쏘드를 호출합니다. ASP.NET 페이지의 생명주기는 ProcessRequest() 를 통해서 시작됩니다. 이 메쏘드는 페이지의 컨트롤 단계를 준비합니다. 그 다음으로, 페이지와 서버는 ASP.NET 웹 페이지에 필요한 여러 단계를 하나씩 단계별로 밟게 됩니다. 이 단계들에는 뷰스테이트를 관리하고, 포스트백 이벤트를 처리하고, HTML 페이지를 렌더링하는 것들이 포함되어 있습니다.


그림 1. 페이지 생명 주기의 이벤트들
출처 : http://msdn.microsoft.com/en-us/library/ms972976.aspx

다시 뷰스테이트로 돌아와서 정리하자면, 세상에 공짜는 없습니다.(비용이 든다는거죠) 여기 뷰스테이트도 예외일 수는 없습니다^^; ASP.NET 페이지가 요청될 때마다 뷰스테이트는 두가지 성능에 관련되는 일을 합니다. 첫째는, 모든 페이지를 방문할 때 뷰스테이트는 해당 컨트롤 계층의 모든 컨트롤의 뷰스테이트를 모아서 베이스 64 인코딩으로 시리얼라이즈합니다. 여기서 생성된 스트링이 __VIEWSTATE에 값인거죠^^

반대로, 포스트백시에는 뷰스테이트를 읽엇 저장된 뷰스테이트 데이터로 디시리얼라이즈하고 컨트롤 계층구조에 있는 적절한 컨트롤러로 업데이트합니다. 두번째로, 뷰스테이트는 클라이언트가 다운로드 받아야할 웹페이지의 크기를 증가시킵니다. 원래 보여질 페이지의 데이터와는 또다른 데이터인거죠. 뷰스테이트 크기가 큰 페이지는 이 뷰스테이트의 크기가 수십 킬로바이트가 됩니다.  이것을 다운받기 위해 또 수 초나 수 분의 시간을 할애해야합니다.(이게~ 뭔가요-_-;) 또, 포스트백시에도 뷰스테이트를 웹 서버로 보내야하고, 그것 때문에 포스트백 요청시간이 증가하는 거죠.


ASP.NET MVC 알아보기

ASP.NET MVC는 모델, 뷰 그리고 컨트롤러로의 3개의 파트로 분리되어 구현됩니다.


그림 2. MVC Framework
출처 : http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx

● 뷰(View) 는 애플리케이션의 UI를 담당하고, 이는 단지 컨트롤러에 의해 애플리케이션의 데이터로 채워질 HTML 템플릿에 지나지 않습니다.
● 모델(Model) 은 UI를 렌더링하는 뷰가 사용할 애플리케이션의 객체, 즉 애플리케이션의 데이터의 비즈니스 로직을 구현합니다.
● 컨트롤러(Controller) 는 사용자 입력과 상호작용하는 응답을 핸들링합니다. 웹 요청은 컨트롤러에 의해 핸들링되고, 요청된 URL의 표현될 뷰와 적절한 데이터(모델 객체)를 결정합니다.


ASP.NET MVC의 요청 흐름도

ASP.NET MVC 애플리케이션은 웹폼과 같이 주소창에 입력된 URL이 해당서버의 디스크에 있는 페이지(파일)을 찾는것이 아닌 컨트롤러를 통한 뷰를 호출하게 됩니다. URL은 컨트롤로와 매핑이 되고 컨트롤러는 뷰를 리턴해주는 식이죠. 다음의 그림을 보시면


그림 3. 요청 흐름도
출처 : http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx

처음 사용자 요청(URL)이 들어오면 Routing Rule 에 의해 컨트롤러를 매핑시킵니다. Routing Rule 은 ASP.NET MVC 프로젝트를 생성하면 Global.asax에 정의되어 있는데요. 제 블로그에 컨트롤러를 설명하면서 Global.asax에 대해 간략하게 설명한 부분이 있는데요 참고하시기 바랍니다. 여기서도 간단히 설명드리자면, 주소는 {controller}/{action}/{id} 이런식으로 들어오게됩니다. 특정 컨트롤러의 특정 액션메쏘드의 파라미터로 id를 넘겨준다는 룰이죠. (액션메쏘드는 컨트롤러 클래스에서 public으로 제공되는 메쏘드를 가리킵니다.)

다시, 컨트롤러는 뷰에 넘겨줄 데이터를 만들 모델을 선택합니다. 선택된 모델객체는 뷰에 넘겨줄 데이터(ViewData)를 만들고, 컨트롤러가 이를 뷰에 넘겨줍니다. 마지막으로 뷰는 HTML로 렌더링해서 사용자에게 보여줌으로 하나의 흐름이 흘러가는 거죠^^;

그래서 ASP.NET MVC의 장점은?

● 모델, 뷰, 컨트롤러로 명확하게 관계과 분리되어 있어 복잡한 애플리케이션 로직을 관리하기 쉽게 해줍니다. 이로 인해 각 개발자는 MVC 패턴의 분리된 컴포넌트를 각자 개발할 수 있습니다. 페이지 단위로 되어 있는 웸폼은 비하인드 코드에서 로직을 담당하게 되는데요. 다른 두개의 페이지에서 비슷한 작업을 하게 된다해도 하나의 작업으로 사용하기가 어렵다는 거죠^^; 특정 페이지의 이벤트가 그 페이지의 비하인드 코드를 호출하기 때문에 같은 작업을 해도 저처럼 잘 모르는 사람들은 저희에게 무한한 능력(?)을 심어주는 카피 앤 페이스트 작업을 거쳐야한다는 겁니다.

● 유닛테스팅을 쉽게해줍니다. 이 유닛테스트도 M,V,C 가 각각 깔끔하게 분리되어 있어주기 때문에 가능한거죠^^; 추후에 유닛테스트가 과연 얼마나 쉬운지 알아보는 시간을 갖도록 하겠습니다.

● MVC 모델은 뷰스테이트와 포스트백을 사용하지 않습니다. 그래서, MVC는 일반적으로 웹폼에 비해 페이지 사이즈가 작습니다. 폼의 히든 필드인 뷰스테이트 데이터와 같은 불필요한 데이터가 없기 때문이죠.

● 웸폼 모델에서의 URL이 서버의 존재하는 파일을 직접 요청하는 것 대신에 REST 방식의 URL을 사용하여 URL을 간단하게 해주고, 기억하기 쉽게 해주고, SEO의 도움을 줍니다.

SEO(Search Engine Optimization) 는 검색엔진의 결과로 보여질 랭크의 순위를 높이는 작업을 말합니다. 상위 랭크의 검색결과로 나타내어지면 사용자가 아무래도 계속 그것만 보게되니 좋겠죠^^; 그래서 URL 또한 사용자가 보기 쉬운 걸로 나타내면 좋다는거죠. 예를들어, /Product/Edit/4 이런 URL 이라면 4번째 상품을 수정하겠다는 거군. 이렇게 명시적으로 알수 있다는 겁니다. 예가 좀 부적합한가요^^;

● jQuery나 ExtJS와 같은 클라이언트단 자바스크립트 프레임워크와 쉽게 통합할 수 있습니다.


마무리요

사실, 웸폼과 MVC를 비교해서 꼭 이것이 더 좋으니까 이것만 사용해야지가 아닙니다. 각 프로젝트의 특성에 맞게 선택하는 거죠. (다시한번 말씀드리지만 이 포스트는 ASP.NET MVC에 관한 글입니다--;) 성능을 따지지 않고 빠른 개발을 원한다면 웹폼을 선택하는 것이 좋을 것 같고, 유닛테스팅과 성능에 중점을 두겠다 싶으면 MVC쪽을 선택해봄이 좋겠다는 거죠. 다음 포스트에는 예제를 통해서 ASP.NET MVC와 인사를 나눠보는 시간을 갖도록 하겠습니다^^


참고자료 :
http://msdn.microsoft.com/en-us/library/ms972976.aspx
http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx
http://msdn.microsoft.com/en-us/magazine/dd942833.aspx
http://www.techbubbles.com/aspnet/aspnet-mvc-and-web-forms/
http://www.taeyo.net/lecture/NET/AspNet35_pre02.asp
http://weblogs.asp.net/shijuvarghese/archive/2008/07/09/asp-net-mvc-vs-asp-net-web-form.aspx
http://weblogs.asp.net/scottgu/archive/2007/10/14/asp-net-mvc-framework.aspx

Welcome to dynamic C# 외전(3) - 감시하는 자와 감시당하는 자.

C# 2009. 12. 19. 09:00 Posted by 알 수 없는 사용자
오늘도 하루가 밝았어요.
상쾌한 기분으로 기지개를 켜고, 커튼을 젖혔어요.

오~ 마이~갓.
바깥에 왠 검은 외투를 입은 사람들이 망원경 뒤집어쓰고 이쪽을 째려보고 있어요.
아침부터 기분이 좋지 않아요.
자고일어나니 스타가 된걸까요, 왜 이쪽을 째려보는 건지 도무지 모르겠어요.
집밖을 나섰어요.
대놓고 따라와요.
집밖을 나왔는데도 왜 망원경은 계속 뒤집어 쓰고 있는건지 모르겠어요.
- [감시당하는 자]의 탐구생활


- 시작부터 탐구생활드립이냐.

그냥 한번 해보고 싶었을 뿐이니 노여워 마시길 바랍니다. 오늘은 감시하는 자와 감시당하는 자의 이야기를 해보려고 합니다. 즉, Observer패턴을 말하는 건데요. 뭔가 변화가 있을때 그 변화를 바로 알아차려서 어떤 동작을 수행해야 할때. 변화의 대상을 유심히 관찰하고 있어야 겠죠. 근데, 유심히 관찰하고 있는거 보다 변화가 일어났음을 알려주는 친구가 있다면 더 편리하겠죠. 이벤트 모델이 그중의 한 예 입니다. 이벤트가 일어났을때 통지를 받겠다고 등록을 해두면, 이벤트 발생시에 이벤트를 기다리는 모든 객체들에게 이벤트가 발생했음을 통지해줘서 적절한 조치를 취하도록 해주는 것 처럼 말이죠.

이런 건, 기존에는 직접 패턴을 통해 작성을 해야 했지만 닷넷 프레임워크 4.0에 이런 Observer패턴을 지원하기 위한 새로운 인터페이스가 추가 되었습니다. 거기에 대해서 설명을 드려볼까 합니다. 기존의 포스트와 마찬가지로 이번 포스트 역시 눈을 조심하시고 언제든지 OME를 외칠 준비를 하시길 바랍니다.



- 기존의 방식으로.

닷넷 프레임워크 4.0이전에는 어떻게 만들어야 했는지 한번 알아보겠습니다. 제 이해가 부족해서 적절치 못한 예제와 적절치 못한 수준일 수도 있으니, 마음의 준비 하시구요. 우선, UML 다이어그램을 한번 보시죠.

- 그림1. 예제의 UML 다이어그램.(인터페이스가 클래스모양을 하고 있는 것 같지만, 착시현상입니다. 이해를 위해서 부족한 실력이지만 그려봤습니다-_-)

네, 그림에서 느낌이 오듯이 라디오방송국과 청취자를 모델링해봤습니다. 제가 만드는거 중에 쓸모있는 건 별로 안나오죠-_-. 우선 RadioBroadcaster라는 인터페이스가 있구요, KBRRadioBroadcaster가 그걸 상속해서 방송국의 역할을 합니다. 그리고 각각의 Listener가 방송국에 주파수를 맞추고, 전달되는 전파를 수신하는 형태이구요.

enum BroadcastStatus
{
    StandBy = 0,
    OnAir = 1,
    End = 2
}

interface RadioBroadcaster//지켜볼 대상
{
    bool ListenTo(Listener listener);
    bool QuitFrom(Listener listener);
    void DoBroadcasting();
    void StartBroadcast();
}


네, 일단 방송의 상태를 알려주는 열거자와 지켜볼 대상이 되는 인터페이스입니다.


class KBRRadioBroadcaster : RadioBroadcaster//정보의 공급자
{
    private List<Listener> listeners = new List<Listener>();
    public BroadcastStatus broadcastStatus = BroadcastStatus.StandBy;

    private List<string> scripts = new List<string>
    {
        "안녕하십니까. KBR방송국의 김병만 기자입니다.",
        "요즘 살림살이 초큼 어찌 나아지셨슴까?",
        "안 나아지셨다구요? 그럼 이 방송에서 취업 필살전략을 알려드립니다.",
        "요즘같이 취업이 어려운때 3개 국어가 가능하면 취업 직빵이겠죠.",
        "따라 해보시져, Handle いぱい 꺽어.(핸들 이빠이 꺽어).",
        "3개국어 정말 쉽져? 이것만 한다면 당신도 취업계의 슈퍼스타!"
    };

    private int currentScriptIndex = 0;

    public bool ListenTo(Listener listener)
    {
        try
        {
            listeners.Add(listener);
        }
        catch (Exception ex)
        {
            return false;
        }

        return true;
    }

    public bool QuitFrom(Listener listener)
    {
        if (listeners.Contains(listener))
        {
            listeners.Remove(listener);
            return true;
        }

        return false;
    }

    public void DoBroadcasting()
    {
        if (scripts.Count.Equals(currentScriptIndex))
        {
            Console.WriteLine("방송끗.");
            broadcastStatus = BroadcastStatus.End;
        }
        else
        {
            foreach (Listener listener in listeners)
            {
                listener.Listening(scripts[currentScriptIndex]);
            }
            currentScriptIndex++;
        }
    }

    public void StartBroadcast()
    {
        broadcastStatus = BroadcastStatus.OnAir;
    }
}


그리고 방송국입니다. 듣고 싶은 청취자는 ListenTo메서드를 통해 주파수를 맞추고 QuitFrom을 통해 라디오를 끕니다. 그리고 DoBroadcasting을 통해 정해진 대사가 한번에 하나씩 전파를 통해 나가구요.


class Listener//지켜보는 행위의 주체
{
    private string name;

    public Listener(string name)
    {
        this.name = name;
        Console.WriteLine("나는야 {0}. 라디오를 가진 남자지.",this.name);
    }

    public void ListenToKBRRadio(RadioBroadcaster radioBroadcaster)
    {
        bool status = radioBroadcaster.ListenTo(this);

        if (status)
        {
            Console.WriteLine("{0} : 수신감도 조쿠나. ㅎㅎㅎㅎ", this.name);
        }
        else
        {
            Console.WriteLine("{0} : 아놕. 라디오도 못듣능구나", this.name);
        }
    }

    public void QuitFromKBRRadio(RadioBroadcaster radioBroadcaster)
    {
        bool status = radioBroadcaster.QuitFrom(this);

        if (status)
        {
            Console.WriteLine("{0} : 아놔. 완전 재미없엉", this.name);
        }
        else
        {
            Console.WriteLine("{0} : 아. 듣고 있던거 아니구낭", this.name);
        }
    }

    public void Listening(string script)
    {
        Console.WriteLine("{0} : {1}", this.name,script);
    }
}


그리고 이번엔 지켜보는 자, 청취자입니다. 라디오를 청취하고, 끊을 수 있으며, Listening을 통해서 매번 날아오는 전파를 수신합니다.

class Program
{
    static void Main(string[] args)
    {
        Listener listener1 = new Listener("마논개");
        Listener listener2 = new Listener("코턱용");
        Listener listener3 = new Listener("송순신");

        KBRRadioBroadcaster kbrRadioBroadcaster = new KBRRadioBroadcaster();
        listener1.ListenToKBRRadio(kbrRadioBroadcaster);//마논개 라디오 청취시작.
        int count = 0;
        do
        {
            if (count == 0)
            {
                kbrRadioBroadcaster.StartBroadcast();
            }
            if (count == 2)
            {
                listener2.ListenToKBRRadio(kbrRadioBroadcaster);//코턱용 라디오 청취시작
            }
            if (count == 3)
            {
                listener1.QuitFromKBRRadio(kbrRadioBroadcaster);//마논개 라디오 끔
                listener3.ListenToKBRRadio(kbrRadioBroadcaster);//송순신 라디오 청취시작
            }
            kbrRadioBroadcaster.DoBroadcasting();
            count++;
            Thread.Sleep(500);
        } while (kbrRadioBroadcaster.broadcastStatus != BroadcastStatus.End);
       
    }
}


그리고 프로그램 코드지요. 마논개, 코턱용, 송순신 세명이 라디오를 청취하는데요. 처음에는 마논개 혼자 듣다가, 두번이후에 코턱용이 라디오를 듣기 시작합니다. 그리고 세번째가 되면, 마논개가 라디오가 지겨워서 끄구요, 송순신이 라디오를 듣기 시작합니다. 방송이 끝날때까지, 0.5초당 한번씩 전파를 타고 방송이 날아오구요. 아웃사이더쯤되야 가능한 속도겠네요. 실행화면을 보시면 아래와 같습니다.

-그림2. 실행한 모습!

넵, 의도했던대로 방송이 전파를 타고 나오면서 사람들이 방송을 들었다가 빠집니다. 대사가 다 나온후에 방송이 종료되구요. 제 내공의 문제로 정확한 Observer패턴의 형태나, 사용방법이라고 볼 수는 없겠지만 뭐 이해에는 문제가 없을 것 같습니다. 하하하-_-;;;


- 이제 새로운 걸 내놔봐.

넵. 이젠 닷넷 4.0에 추가됐다는 새로운 인터페이스 IObservable<T>와 IObserver<T>에 대해서 말씀드려보도록 하겠습니다. IObserable<T>는 지켜볼 대상이 되는 클래스가 구현을 할 인터페이스입니다. 위에서 보자면 방송국이 되겠죠. 그리고 IObserver<T>는 지켜보는 행위를 하는 클래스가 구현을 할 인터페이스입니다. 마찬가지로 청취자가 되겠죠. 즉 IObservable<T>를 구현하는 클래스는 말그대로 observable한(지켜볼 수 있는) 클래스를 말하는 거구요, IObserver<T>를 구현하는 클래스도 말 그대로 observer클래스(지켜보는)가 되는 거죠. 그럼, 위의 예제를 여기에 맞춰서 옮겨 볼까요~~~~! 그전에, 일단 다이어그램을 한번 보시져.

-그림3. 새로운 인터페이스를 활용한 다이어그램


enum BroadcastStatus
{
    StandBy = 0,
    OnAir = 1,
    End = 2
}

interface RadioBroadcaster : IObservable<string>//지켜볼 대상
{       
    void DoBroadcasting();
    void StartBroadcast();
}


RadioBroadcaster가 IObservable<string>을 상속하고 있죠? 지켜볼 대상이 되기 때문이죠. 그리고 string은 지켜보는 observer가 전달받는 데이터를 말하는데요, 방송국에서 string타입의 전파를 뿌렸으므로 string이 됩니다.

class KBRRadioBroadcaster : RadioBroadcaster//정보의 공급자
{
    private List<IObserver<string>> listeners = new List<IObserver<string>>();
    public BroadcastStatus broadcastStatus = BroadcastStatus.StandBy;

    private List<string> scripts = new List<string>
    {
        "안녕하십니까. KBR방송국의 김병만 기자입니다.",
        "요즘 살림살이 초큼 어찌 나아지셨슴까?",
        "안 나아지셨다구요? 그럼 이 방송에서 취업 필살전략을 알려드립니다.",
        "요즘같이 취업이 어려운때 3개 국어가 가능하면 취업 직빵이겠죠.",
        "따라 해보시져, Handle いぱい 꺽어.(핸들 이빠이 꺽어).",
        "3개국어 정말 쉽져? 이것만 한다면 당신도 취업계의 슈퍼스타!"
    };

    private int currentScriptIndex = 0;

    public void DoBroadcasting()
    {
        if (scripts.Count.Equals(currentScriptIndex))
        {
            Console.WriteLine("방송끗.");
            broadcastStatus = BroadcastStatus.End;
        }
        else
        {
            foreach (IObserver<string> listener in listeners)
            {
                listener.OnNext(scripts[currentScriptIndex]);
               
                // Assume that we've arrived at location of Latitude has changed by 4.
                if (broadcastStatus == BroadcastStatus.End)
                {
                    listener.OnCompleted();
                }
            }
            currentScriptIndex++;
        }
    }

    public void StartBroadcast()
    {
        broadcastStatus = BroadcastStatus.OnAir;
    }

    #region IObservable<Listener> Members

    public IDisposable Subscribe(IObserver<string> listener)
    {
        listeners.Add(listener);
        // Announce current location to new observer.
        Console.WriteLine("{0} : 수신감도 조쿠나. ㅎㅎㅎㅎ", (listener as Listener).Name );

        return listener as IDisposable;
    }

    #endregion

    public void UnSubscribe(IObserver<string> listener)
    {
        listeners.Remove(listener);
        Console.WriteLine("{0} : 아놔. 완전 재미없엉", (listener as Listener).Name);
    }
}


변화가 있다면, IObservable의 메서드인 Subscribe를 구현했다는 것과 QuitFrom이 이름을 맞추기 위해서 UnSubscribe로 바뀐건데요. Subscribe는 ListenTo가 그랬듯이 변화가 생기면, 그걸 알려달라고 등록하는 통로가 됩니다.

class Listener : IObserver<string>//지켜보는 행위를 하는 주체
{
    private string name;

    public string Name
    {
        get
        {
            return name;
        }
    }

    public Listener(string name)
    {
        this.name = name;
        Console.WriteLine("나는야 {0}. 라디오를 가진 남자지.", this.name);
    }

    #region IObserver<string> Members

    public void OnCompleted()
    {
        Console.WriteLine("{0} : 음 재밌네 ㅋㅋㅋㅋㅋㅋㅋ", name);
    }

    public void OnError(Exception error)
    {
        Console.WriteLine("{0} : 아놔 감도 완전 좌절이네.", this.name);
    }

    public void OnNext(string script)
    {
        Console.WriteLine("{0} : {1}", this.name, script);
    }

    #endregion
}


그리고 요건 청취자죠. 지켜봐야 하기 때문에 IObserver<string>을 구현한게 보이시죠? 그리고 인터페이스의 메서드를 구현했는데요. 각각 완료시에 할 동작을 담을 OnCompleted와 에러발생시에 처리할 OnError, 그리고 기존의 Listening과 같이 변화를 전달받을때 할 동작을 담을 OnNext로 나눠집니다.


class Program
{
    static void Main(string[] args)
    {
        Listener listener1 = new Listener("마논개");
        Listener listener2 = new Listener("코턱용");
        Listener listener3 = new Listener("송순신");

        KBRRadioBroadcaster kbrRadioBroadcaster = new KBRRadioBroadcaster();
        kbrRadioBroadcaster.Subscribe(listener1);//마논개 라디오 청취시작.
        int count = 0;
        do
        {
            if (count == 0)//방송시작
            {
                kbrRadioBroadcaster.StartBroadcast();
            }
            if (count == 2)//잠시후 코턱용 라디오 청취시작
            {
                kbrRadioBroadcaster.Subscribe(listener2);
            }
            if (count == 3)//잠시후
            {
                kbrRadioBroadcaster.UnSubscribe(listener1);//마논개 라디오 끔
                kbrRadioBroadcaster.Subscribe(listener3); //송순신 라디오 청취시작
            }
            kbrRadioBroadcaster.DoBroadcasting();
            count++;
            Thread.Sleep(500);
        } while (kbrRadioBroadcaster.broadcastStatus != BroadcastStatus.End);
    }
}


그리고 프로그램 코드죠. 동작은 위와 동일합니다. 결과를 보시죠.

-그림4. 동일한 결과


- 마무리.

의의를 찾자면, 프레임워크 레벨에서 Observer패턴을 지원하면서 좀 더 정돈된 형태로 구현을 할 수 있다는 것과 그렇게 구현을 했을때 나중에 프레임워크 레벨에서 성능 개선이 있던지 하면, 그런 혜택을 코드의 변경없이 누릴 수 있다는 점이 있겠구요. 또 한가지 점은 프레임워크나 다른 기술에서 내부적으로도 이 인터페이스를 활용하게 될거고, 그리고 새로운 형태의 프레임워크도 등장을 기대할 수 있다는 점일 것 같습니다.

실제로, 이 인터페이스를 통해서
Reactive Extensions for .NET(줄여서 Rx) 라는 프로젝트가 Devlab에 공개되었습니다. 스크린캐스트를 살짝 들여다보니, 흥미로운 응용이 가능하더군요. 가능하면 다음번에는 Rx를 가지고 이야기를 한번 해보고 싶네요. 그럼, 시력을 떨어뜨리는 글을 열심히 읽어주셔서 감사합니다. 날도 추운데 피드백은 따쓰하게... 아시져? ㅋㅋㅋㅋ 그리고! 소스는 첨부해드립니다.


- 참고자료

1. http://msdn.microsoft.com/en-us/library/dd990377(VS.100).aspx




Parallel Patterns Library(PPL) - concurrent_queue - 1

VC++ 10 Concurrency Runtime 2009. 12. 18. 09:00 Posted by 알 수 없는 사용자

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를 공부하면 다른 하나도 아주 빠르고 쉽게 습득할 수 있을 것 같습니다.

오랜만의 포스팅입니다.

지난 글에서 말씀 드렸듯이, 이번 글에서는 새롭게 추가된 Data Flow Rules에 대해서 소개 드리겠습니다.

새롭게 추가된 8개의 Data Flow 규칙들

일단, 새롭게 추가된 규칙 목록을 먼저 보도록 하겠습니다.

CA1062 ValidateArgumentsOfPublicMethods: 함수의 인자유효성 검사 여부
CA1303 DoNotPassLiteralsAsLocalizedParameters: 문자열 인자의 Globalization 처리 여부
CA2100 ReviewSqlQueriesForSecurityVulnerabilities: SQL Injection 취약점 파악
CA2202 DoNotDisposeObjectsMultipleTimes: Dispose 메서드를 여러 번 호출하는지 여부
CA2204 LiteralsShouldBeSpelledCorrectly: 스펠링 체크
CA2215 DisposeMethodsShouldCallBaseClassDispose: Base 클래스의 Dispose 메서드를 호출하는지 여부
CA2241 ProvideCorrectArgumentsToFormattingMethods: Format 함수 인자 검사
CA2000 DisposeObjectsBeforeLosingScope: Dispose 메서드를 호출하는지 여부

이 규칙들 중에서, CA2000을 제외하면 모두 Visual Studio Team System 2005에서 이미 지원했었던 규칙입니다. 그리고 사실 CA2000도 VSTS 2005에 없었다 뿐이지, FxCop 1.35버전에서는 있었구요. 하지만 이 규칙들은 FxCop 1.36과 Visual Studio Team System 2008에서는 빠져있습니다.

이 규칙들이 빠졌던 이유에 대해서는 아래 URL을 보시면 됩니다.

http://code.msdn.microsoft.com/codeanalysis/Release/ProjectReleases.aspx?ReleaseId=556

즉, FxCop 1.35와 VSTS 2005에 있었던 Analysis 엔진에 성능의 결함, 버그, 일관적이지 못한 분석 결과 등의 문제가 있어서, 그 엔진을 VSTS 2008과 FxCop 1.36에서는 완전히 없애 버린 것입니다.

하지만, 이번 Visual Studio 2010에서는 화려하게 복귀가 되었습니다. 


Phoenix Framework의 탑재


그게 가능했던 이유는 새롭게 탑재된 Phoenix Framework 덕분입니다. 피닉스 프레임워크는 향후 마이크로소프트 컴파일러 기술을 위한 새로운 코드 최적화/분석 프레임워크입니다. C++/CLI 기반으로 만들어진 피닉스 프레임워크는 컴파일러, 코드 최적화, 자동 코드 생성 등 여러 분야에 사용될 수 있는 프레임워크인데요. 자세한 정보는 아래 URL에서 보실 수가 있습니다.

http://research.microsoft.com/en-us/collaboration/focus/cs/phoenix.aspx

https://connect.microsoft.com/Phoenix


그러면 다음 글을 통해서, 새로운 Analysis Engine인 Phoenix 프레임워크에 대해서 더 자세히, 그리고 어떻게 우리가 사용할 수 있을지 알아보도록 하겠습니다.

Parallel Patterns Library(PPL) - concurrent_vector - 2

VC++ 10 Concurrency Runtime 2009. 12. 9. 09:00 Posted by 알 수 없는 사용자


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에 대해서 알아 보겠습니다.


저희 Viva 2010 팀은 차세대 개발 플랫폼인 Visual Studio 2010, Visual Studio Team System 2010, C# 4.0, C++ 0x, Cloud 등을 공부하고 다양한 매체를 통해 알리는 팀 입니다.

저희 VSTS 2010 팀은 최근 Visual Studio 2010 베타 2 공개와 앞으로 곧 나오게 될 RC 및 정식 버전 출시에 발 맞춰서 활동을 더욱 강화하기 위해서 팀 멤버를 추가로 모집하고 있습니다.


그간의 활동

온라인 활동

http://vsts2010.net 블로그를 통해서 총 164개의 Article 발행, 총 방문자 약 50000, 일 평균 300명의 방문


오프라인 활동


[2009년 6월 10일 MSDN 주간 세미나]

강보람 - C#연대기 -C#의 Before/After

공성의 - VSTS2010에서의 소프트웨어 품질 관리

김병진 - VSTS 2010 Architecture & UML

엄준일 - Managed Extensibility Framework

최흥배 - Visual C++ 10, C++0x 그리고 Concurrency Runtime


[TechDays 2009 참여]

김병진, 강성재 - Visual Studio Team System 2010 Overview

최흥배새로운 시대를 여는 Visual C++ 10

강보람C# 4.0 with dynamic : 사랑과 전쟁. 그들의 4주 후…

조진현Multi-threaded rendering in DirectX11

엄준일, 공성의Visual Studio Team System 2010 With Agile

김대욱, 김태균WPF4.0 을 위한 VIsualStudio 2010



활동 영역

온라인 활동 영역

팀 블로그 활동

팀 블로그를 통해 자신의 글을 게시할 수 있습니다. 현재 수백 명의 정기 구독자에게 글이 공개가 되며, 팀 블로그가 구글 등의 검색 상위권에 이르게 됨으로 자신의 글이 상위 검색에 노출되는 간접적인 혜택을 누릴 수 있습니다.

온라인 세미나

한국 마이크로소프트와 팀 자체에서 진행하는 여러 가지 온라인 세미나의 스피커로 활동하게 됩니다.

온라인 커뮤니티(예정)

온라인 커뮤니티 활동과 함께 커뮤니티 운영 활동을 하게 됩니다.

   

오프라인 활동 영역

오프라인 스터디

오프라인 스터디를 통해 자신의 분야를 공부하고 발표합니다. 그리고 좋은 콘텐츠는 곧바로 온라인/오프라인 세미나 스피커 활동으로 이어집니다.

오프라인 세미나

한국 마이크로소프트와 팀 자체에서 진행하는 오프라인 세미나의 스피커로 활동할 수 있습니다. 팀 자체에서는 매월 오프라인 정기 세미나를 진행하며, 자신의 노하우를 오프라인 세미나를 통해 전달할 수 있는 기회를 드립니다.

기고

팀 블로그를 통해 축적된 자신의 콘텐츠는 월간 잡지 등에 기고할 수 있습니다.

책 집필, 번역(예정)

다양한 노하우를 책으로 집필하고, 외국의 유명 서적을 번역하는 활동을 계획하고 있습니다.

Microsoft MVP 추천

MVP 에 되고자 하시는 분은 한국 이크로소프트 직원과 마이크로소프트 MVP 의 추천을 드립니다.

   

모집 분야

  • Cloud Development
  • C#
  • VB.NET
  • C++
  • Agile Development
  • Parallel Development
  • Web Development
  • ASP.NET
  • Silverlight
  • Windows 7 Development
  • RIA Development
  • Architect Development
  • Office Business Application Development
  • .NET Framework 4.0
  • Visual Studio 2010
  • Visual Studio Team System 2010
  • ETC…

   

마감

정해진 마감 일자는 없습니다만, 적어도 12월 중에는 저희와 함께 할 수 있었으면 합니다. 가능한 빨리 지원해주시길 부탁드립니다.

   

지원 방법

아래의 양식을 채워주시고 kkongchi@gmail.com 으로 보내주세요.

이름

  

사진

  

블로그

  

전화번호, 이메일

  

티스토리 아이디

  

소개

(직업 및 회사명 포함)

관심 분야

(중복 가능)

   

지원해 주신 내용에 대한 심사 후, 오프라인 인터뷰를 통해서 멤버를 선정하게 됩니다. 이번에는 특히 활발한 온라인 활동, 특히 개인 블로그 활동을 많이 해오신 분들을 우선적으로 선정할 예정입니다.

   

지원 시 유의 사항

참고로 저희 스터디에서는 배우고자 지원하시는 분들은 선발하지 않습니다. 저희 팀의 스터디에서는 여러분들에게 아무것도 가르쳐주지 않습니다.

저희 팀에서는 절대 실력을 보고 맴버를 선발하지 않습니다. 물론 실력이 출중하면 좋겠지만 새로운 VSTS 2010 분야는 어느 누구도 밟아보지 않은 새로운 황야와 같습니다. 새로운 길을 함께 가실 활동력이 충분하신 분들은 꼭 지원해 주시기 바랍니다. ^^

[JumpToDX11-8] Deferred Contexts

DirectX 11 2009. 12. 2. 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 를 사용할 준비가 되었습니다.^^



Welcome to dynamic C# 외전(2) - Generic Method.

C# 2009. 11. 30. 08:30 Posted by 알 수 없는 사용자

- 또 외전이군 ㅋ

오랜만의 포스팅이네요. 넵. 또 외전입니다-_-;;; 최근에 다른 일때문에 포스팅을 못하다가 어떤 계기가 생겨서 포스팅을 하게됐습니다. 음;; 제네릭 메서드를 사용해서 코딩할 일이 있었는데요, 갑자기 전에 어떻게 했는지 기억이 안나는거 있죠-_-. 그래서, 기억을 더듬고 검색해서 코딩을 했습니다. 그래서 저도 안까먹고, 혹시 이거때매 해매는 분들을 위해서 포스팅을 할까 합니다.

* 경고 : 늘 그렇지만 제가 하는 포스팅은 허접합니다. 왜냐면, 제가 허접하기 때문이죠. 포스팅의 허접함에 눈이 아프시다면, "Oh My Eyes!"를 한번 외치시고 뒤로가기 버튼을 활용하시면 직빵입니다. ㅋ OME는 스타에서만 나오는게 아니거등요.


- 제네릭?

넵. 제네릭은 불필요한 박싱/언박싱을 없애도록 도와준 고마운 기능입니다. C# 2.0에서 추가가 됐었죠. 내부적으로는 F#에서 유용하다고 검증이 돼서, C#에 추가하기로 결정을 했었다고 합니다. F#이 쫌 까칠하긴 해도, 파워풀 하거든요^^(까칠하다보니, F#이랑 쫌 잘지내보려고 데이트도 11번 정도 했지만 일단은 손 놨습니다. 상처받은 마음을 C#과 좀 달래려구요. 근데 요즘은 C#한테도 상처를....)

혹시 모르는 분들을 위해서 허접한 실력으로 부연설명을 드리자면요. 박싱(boxing)은 포장을 하는 거구요, 언박싱(unboxing)은 포장을 푸는겁니다. 즉, C#에는 크게 타입이 크게 두가지로 분류가 되는데요. 값형식(value type)과 참조형식(reference type)입니다. 값형식은 변수의 이름이 가리키는 곳에는 실제 값이 들어있고, 참조형식은 변수의 이름이 가리키는 곳에 실제값에 대한 참조가 있습니다. 즉, C에서의 포인터의 개념을 생각하시면 됩니다. 포인터는 실제값을 가지는게 아니라, 값이 있는 곳의 주소를 가지고 있는거니까요.

근데, 값형 타입이 닷넷의 최상위 타입인 object같은 참조형 타입과 같이 처리해야 되는 경우가 있습니다. 예를 들면, C# 1.0에서는 컬렉션이 object형만 받을 수 있었습니다. 그래서, int타입만 처리하는 경우에도 컬렉션을 사용하려면, object타입으로 int타입을 포장해서 집어넣고, 빼낼때도 object타입의 포장을 풀고 int타입을 꺼내와야 했던거죠. 그런데, 이게 너무 삽질이다 보니 더 편하게 할 수 있는 방법으로 제네릭이 등장 했습니다.

제네릭은 어떤 타입도 될 수 있는 걸 말하는데요, List<int>는 int타입만 받는 컬렉션, List<string>은 string타입만 받는 컬렉션이 되면서, 불필요한 박싱/언박싱도 없어지고 코드도 훨씬 명확해 지는 장점이 있습니다. 그런데, 꼭 이럴때 뿐만 아니라도 제네릭이 필요한 경우가 생기는데요. 오늘 제가 설명드리면서 보여드릴 예제가 그런 경우입니다.


- 그래그래. 예제 내놔봐.

예제는 XML로 객체를 시리얼라이즈(Serialize)하고 다시 디시리얼라이즈(Deserialize)하는 건데요. 시리얼라이즈을 할때는 시리얼라이징 하는 객체의 타입을 명시해줘야 합니다. 그래서 한 프로그램에서 여러객체를 시리얼라이즈해야 할때는, 그 수만큼 오버로딩이 되기도 합니다. 하지만, 제네릭이 출동한다면?

제.

네.

릭.

...죄송합니다. 스덕이다 보니, 스타와 관련된 드립을 하게 되는군요. 계속해서 말씀드리죠-_-. 어떤 타입이든지 될 수 있는 제네릭을 활용한다면, 메서드를 변경시키지 않고서도 여러타입에 사용가능한 메서드를 만들 수 있습니다. 그럼, 소스를 한번 보시죠.

 class Serializer
{
    internal static bool Serialize<T>(T source, string fileName)
    {
        try
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            Stream stream = new FileStream(fileName, FileMode.Create,
                   FileAccess.Write, FileShare.None);
            serializer.Serialize(stream, source);
            stream.Close();

            return true;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    internal static T Deserialize<T>(string fileName) where T : class, new()
    {
        T target;
        FileInfo _settingFile = new FileInfo(fileName);
        if (!_settingFile.Exists)
        {
            return new T();
        }

        try
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            Stream stream = new FileStream(fileName, FileMode.Open,
                   FileAccess.Read, FileShare.None);
            target = serializer.Deserialize(stream) as T;
            stream.Close();

            return target;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}


위의 소스에서 <T>라고 쓰인부분이 바로 제네릭을 쓰겠다고 하는 겁니다. 즉, 어떤 타입이든지 될 수 있는 T라는 제네릭한 타입에 대해서 메서드를 작성하겠다는 거죠. 그리고 주목하실 부분이, Deserialize메서드 선언부의 끝부분의 "where T : class, new()"입니다. 이건, 제네릭한 타입 파라미터에 제약조건을 주는 겁니다. 즉, 위에 선언된 건 T가 제네릭한 타입이긴한데 class여야만 하고, 파라미터없고 public인 생성자가 있어야 한다는 겁니다. 그런 타입만 T를 통해서 넘어올 수 있다는 거죠. 제약조건은 그 외에도 아래와 같은 것들이 있습니다.

 제약조건  설명
 where T: struct  타입 매개변수는 반드시 Nullable을 제외한 값형(value type)이어야만 한다.
 where T : class  타입 매개변수는 반드시 참조형(reference type)이어야만 한다.
 where T : new()  타입 매개변수는 반드시 public이고 매개변수가 없는 생성자를 갖고 있어야 한다. 그리고 다른 제약 조건이랑 같이 사용될때는 new()가 맨뒤에 와야한다.
 where T : <base class name>  타입 매개변수는 반드시 명시된 클래스를 상속 해야한다.
 where T : <interface name>  타입 매개변수는 반드시 명시된 인터페이스이거나, 명시된 인터페이스를 구현해야 한다.
 where T : U  T에 해당되는 매개변수는 반드시 U에 해당되는 매개변수 타입이거나, U를 상속한 타입이어야 한다. 

네. 뭐 그렇답니다. 프레임워크 개발자나 복잡한 경우가 아니면, 저걸 다 쓸일이 있을지는 모르겠지만 알아두면 좋겠죠. 그리고 위의 코드는 아래와 같이 사용할 수 있습니다. 메서드가 static으로 된건 그냥 편의를 위해서 입니당.

public class Pair
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Program
{
    static bool SerializePairs(List<Pair> pairs)
    {
        try
        {
            Serializer.Serialize<List<Pair>>(pairs, "Pairs.xml");

            return true;
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
            return false;
        }
    }

    static List<Pair> DeserializePairs()
    {
        try
        {
            return Serializer.Deserialize<List<Pair>>("Pairs.xml");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return new List<Pair>();
        }
    }

    static void Main(string[] args)
    {
        List<Pair> pairs = new List<Pair>
        {
            new Pair{
                Id=1,
                Name="홍길동"
            },
            new Pair{
                Id=2,
                Name="루저"
            },
            new Pair{
                Id=3,
                Name="위너"
            }
        };

        SerializePairs(pairs);
        List<Pair> pairs2 = DeserializePairs();

        foreach (Pair pair in pairs2)
        {
            Console.WriteLine(
                string.Format("{0} : {1}",
                    pair.Id,
                    pair.Name)
                );
        }
    }
}


Pair라는 객체가 있고, 그 객체에는 홍길동과 루저, 위너 이렇게 3명이 들어갑니다. 그리고 그 객체를 시리얼라이즈하고, 다시 디시리얼라이즈해서 결과를 화면에 출력하고 있죠. 메서드를 호출할때 "Serialize<List<Pair>>(pairs, "Pairs.xml")" 이렇게, <>안에 제네릭한 타입인 T가 실제로 어떤 타입이 될지를 명시해주고 있습니다. 그리고 결과를 보면 아래와 같구요.


넵. 그렇습니다. 별거 없죠-_-;;;


- 여기까지.

넵. 여기까지입니다. 뭐 특별한 내용도 없지만, 그냥 한번 정리하면 저도 편하고 필요하신 분들에게도 편하지 않을까 싶어서 말이죠-_- ㅋ. 소스는 파일로 첨부해드리겠습니다. 필요하신분들은 참고하시구요~. 언제가 될지는 모르겠지만, 다음 포스트에서 뵙져!!!


- 참고자료
1. http://msdn.microsoft.com/en-us/library/d5x73970.aspx