Visual C++ 10의 변화

Visual C++ 10 2010. 5. 2. 22:08 Posted by 알 수 없는 사용자

영문판 VS 2010(Visual C++ 10)은 나왔고, 한글판은 61일에 나온다고 합니다.

 

Visual C++ 10을 도입하려고 하시는 분들은 전체적으로 어떤 변화가 있는지 알고 싶을 것이고, 특히 구매를 위해 회사 윗 분들에게 보고를 해야 되는 분들은 관련된 문서를 만들어야 하는 분들도 있으리라 생각합니다.

 

VC++ 10 도입이나 내부 스터디에 사용하도록 간단하게 VC++ 10의 달라진 점을 정리하여 문서를 만들어 보았습니다.

 

이 문서는 러프하게 만들어진 문서이니 참고로 사용하여 더 좋은 문서를 만드시기를 바랍니다. 특히 윗 분들에게 보고 하기 위해서 PT문서를 만들 때는 이 문서에 왜 필요한지에 대해서 좀 더 강력한 메시지를 넣기를 바랍니다.^^






WCF - Serialization

WCF 2010. 4. 27. 09:00 Posted by 알 수 없는 사용자
안녕하세요~ ^^
오늘 너무 오랜만에 WCF 포스팅을 하게 되었습니다. 자주자주 인사를 드려야 하는데 그렇게 하지 못해 너무 죄송합니다.
오늘 감기 기운 덕분에 약을 먹어놨더니 정신이 헤롱헤롱 하네요 ^^;;
글이 조금 이상하더라도 하해(?)와 같은 마음으로 이해해주시길 바랄께요~

이번 글에서는 serialization(직렬화)에 대해 얘기를 해볼까 합니다.

우선, serialization이 무엇인지 알아야 하겠죠?
컴퓨터 용어에서 serialization은 종종 하나의 오브젝트를 바이트의 배열로 전환(converting)시키는 것을 의미합니다. 하지만 WCF 에서의 serialization은 이러한 의미로 사용되지는 않구요, 하나의 오브젝트(or 닷넷 클래스)를 XML Information Set(XML Infoset) 으로 전환시킨다는 의미로 사용됩니다.

아직은 조금 이해하기가 어렵나요? 음,, 아마 이번 포스팅을 끝까지 읽으면 이해가 되실 겁니다. ^^
이제부터 WCF에서의 Serialization에 대해 조금 더 자세하게 얘기해보겠습니다.

WCF에서는 아래와 같은 네 가지의 serialization을 위한 클래스를 제공합니다.

  • DataContractSerializer
    : WCF 에서 사용하는 기본 serializer 를 제공한다.
  • NetDataContractSerializer
    : 타입에 대한 추가적인 정보를 제공하는 serializer
  • XmlSerializer
    : .NET 2.0에서 제공되었던 serialization을 수행한다.
  • DataContractJsonSerializer
    : serialization 포맷으로 JSON을 제공한다.

이번 포스팅에서는 이러한 클래스들이 직렬화를 했을 때 어떤 결과물을 보여주는지에 대해 써볼까 합니다. 이러한 내용들이 WCF 서비스를 만들때 직접적인 도움이 되진 않겠지만, WCF 서비스를 이해하고, 목적에 맞는 서비스를 만들기 위한 커스터마이징을 하기 위해선 도움이 될 수 있을 것입니다.

DataContractSerializer

DataContractSerializer는 WCF에서 사용하는 기본 serialization을 수행합니다. (이후 부터는 serialization 대신 "직렬화"를 사용하겠습니다.) 이 serializer를 사용하기 위해서는 직렬화가 되는 클래스에 [DataContract] 특성을 지정하여 주면 됩니다. 이 방법은 WCF 서비스를 만들때 많이 사용했던 방법이기에 다들 익숙하리라 생각됩니다.

DataContractSerializer 클래스가 어떤 형태로 직렬화를 수행하는지 알아보도록 하겠습니다. 우선, 다음과 같은 클래스를 생성합니다.

[DataContract]

public class Employee

{
     public Employee(int employeeID, string firstName, string lastName
     {

         this.EmployeeID = employeeID;

         this.FirstName = firstName;

         this.LastName = lastName;

     }

 

    [DataMember]

    public int EmployeeID { get; set; }

 

    [DataMember]

    public string FirstName { get; set; }

 

    [DataMember]

    public string LastName { get; set; }

}


그리고, 콘솔 어플리케이션에서 다음과 같은 코드를 작성합니다.

using System.Runtime.Serialization;

using System.IO;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Employee e = new Employee(101, "Tae kyeom", "Oh");

            FileStream writer = new FileStream("sample.xml", FileMode.Create);

 

            DataContractSerializer ser = new DataContractSerializer(typeof(Employee));

            ser.WriteObject(writer, e);

            writer.Close();

        }

    }

}


이 코드를 실행을 시켜보면~ 프로젝트의 bin\Debug 폴더안에 sample.xml 파일이 생성됩니다.
코드는 간단하기에 이해하는데 큰 어려움은 없을 것 같지만, 간단히 설명하자면,,, Employee 클래스 인스턴스를 생성하고, 이 오브젝트를 XML 형태로 직렬화를 시킵니다. 그리고 이에 대한 내용을 sample.xml 파일에 썼구요. 그리고, 직렬화 할 때는, DataContractSerializer 클래스를 사용했습니다

그럼, sample.xml 파일의 내용을 확인 해보겠습니다.

네~ 다음과 같은 내용을 보여주고 있네요, 딱 봐도 Employee 인스턴스가 가지고 있는 데이터를 XML로 표현해주고 있다는 것을 알 수 있습니다.

여기서 다시 한번 더 되새겨봐야 할 것은 WCF에서 [DataContract] 특성이 적용된 클래스의 경우 서비스에서 클라이언트로 전달될 때, 위 모습과 같은 형태로 전달된다는 것입니다.


XmlSerializer

XmlSerializer는 이 전 닷넷 버전에서도 지원해주었던 클래스이며, 이는 ASP.NET 웹 서비스에서 사용하던 직렬화 방법을 제공해줍니다.
이러한 직렬화 방법을 WCF 에서도 사용할 수 있으며, 이는 ASP.NET 웹 서비스와 호환이 가능하다는 장점이 있죠. 또한, 이것은 웹 서비스를 WCF로의 전환하는 것이 그리 어려운 일이 아니라는 것을 의미하기도 합니다.^^

XmlSerializer는 public 접근자의 기본 생성자, 필드, 그리고 프로퍼티를 직렬화 시켜줍니다.

Employee 클래스를 다음과 같이 조금 수정해보았습니다.
public 기본 생성자가 필요하기에 추가시켜주었구요, [DataContract]와 [DataMember] 특성을 뺐습니다.

public class Employee

{
    public int EmployeeID { get; set; }

 

    public string FirstName { get; set; }

 

    public string LastName { get; set; }

 

    public Employee()

    {

    }

 

public Employee(int employeeID, string firstName, string lastName)

{
           
this.EmployeeID = employeeID;

            this.FirstName = firstName;

            this.LastName = lastName;

}

}


그리고, 이 클래스를 XmlSerializer 를 이용하여 직렬화 시켜보겠습니다.


using System.IO;

using System.Xml.Serialization;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Employee e = new Employee(101, "Tae kyeom", "Oh");

            FileStream writer = new FileStream("sample.xml", FileMode.Create);

 

            XmlSerializer ser = new XmlSerializer(typeof(Employee));

            ser.Serialize(writer, e);

            writer.Close();

        }

    }

}


코드는 아주 간단하죠. DataContractSerializer 클래스를 이용했던 코드와 별반 다를건 없구요, 단지 직렬화 할때 XmlSerializer 클래스를 사용했습니다. 그리고 이 클래스엔 Serialize 메소드를 이용해 직렬화를 수행하죠.

sample.xml 파일을 확인해보면 아래와 같은 모습을 하고 있는 것을 알 수 있을 것입니다.


결과로 생성 된 xml 파일 역시 앞 예제에서의 결과 파일과 많이 다르진 않지만, 네임스페이스가 조금 바뀐 것을 확인할 수 있습니다.

아,, 참고로~ WCF에서 XmlSerializer를 사용할 수 있게끔 하기 위해선 서비스 계약(Service Contract)에서 [XmlSerializerFormat] 특성을 적용해주면 됩니다.


DataContractJsonSerializer

DataContractJsonSerializer는 직렬화의 결과로 JSON 형태를 제공합니다.
JSON 형태의 경우 XML 보다 데이터의 양이 적은 장점이 있고, 이 직렬화를 사용하면, 자바 스크립트를 이용하여 서비스를 호출할 수 있어, 웹 어플리케이션에서 서비스를 쉽게 활용할 수 있다는 장점이 있습니다.

WCF 에서는 REST 서비스를 구현하는 경우 이러한 DataContractJsonSerializer를 사용합니다.

앞 예제에서 사용한 Employee 클래스를 DataContractJsonSerializer를 이용하여 직렬화 시켜보도록 하겠습니다.

using System.IO;

using System.Runtime.Serialization.Json;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Employee e = new Employee(101, "Tae kyeom", "Oh");

            FileStream writer = new FileStream("sample.txt", FileMode.Create);

 

            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Employee));

            ser.WriteObject(writer, e);

            writer.Close();

        }

    }

}


앞 예제와는 다르게 직렬화 결과를 xml 파일이 아닌 텍스트 파일로 썼습니다. 그리고 DataContractJsonSerializer 클래스를 사용하여 직렬화를 수행했습니다.

다음은 그 결과입니다.


멋지게 JSON 형태로 그 결과를 보여주고 있네요. ㅎ


오늘의 포스팅은 여기까지 하려 합니다. (몸이 좋지 않다는 핑계로 성급히 마무리 하려는,, ㅡ,.ㅡ;;;)

오늘 포스팅의 내용은 WCF 서비스를 만드는 팁도 아니고, 서비스를 만드는데 직접적인 영향을 미치는 것은 아니었던것 같습니다. 하지만, 이 부분은 앞에서도 언급했지만, WCF 서비스에 대한 이해를 위해서 알아두어야 하는 부분이라 생각하기에 포스팅을 감행(?)했습니다. 비록, 실무적으로 많은 도움이 되진 못하겠지만 이러한 기본적인 지식도 중요한 부분이니깐요~ ^^

다음 포스팅에서는 WCF 에서 제공하는 기본 직렬화 방법이 적절하지 않을때 커스터마이징을 수행하는 방법에 대해 알아보도록 하겠습니다.

감사합니다.

[JumpToDX11-12] DirectCompute 의 절차.

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


앞선 시간에서 우리는 GPGPU 의 실행에 대한 간단한 개념에 살펴보았습니다.
이제 실제적으로 GPGPU 를 활용하는 절차를 살펴볼 차례입니다.
큰 절차는 다음과 같습니다.


 

< DirectCompute 의 초기화 >

가장 먼저 DirectCompute 를 초기화 해야 합니다.
 

hr = D3D11CreateDevice

(

     NULL,     // default gfx adapter

  D3D_DRIVER_TYPE_HARDWARE,  // use hw

     NULL,               // not sw rasterizer

     uCreationFlags,     // Debug, Threaded, etc.

     NULL,               // feature levels

     0,                  // size of above

     D3D11_SDK_VERSION,  // SDK version

     ppDeviceOut,        // D3D Device

     &FeatureLevelOut,   // of actual device

     ppContextOut );     // subunit of device

);

어디서 많이 본 API 라고 생각이 드시죠.
DirectCompute 를 초기화하는 작업은 바로 전통적인 CreateDevice() API 를 사용하는 것입니다.
즉, DirectX 를 사용하는 것입니다.
이로 인해서 DirectX 는 더욱 더 넓은 범위에서 활용되어 질 것입니다.


< HLSL 의 로드 >

그 다음은 실제적으로 GPU 가 실행을 하게될 로직을 로드할 차례입니다.
이것은 워낙 다양할 수 있는 부분이기 때문에, 여기서는 간단하게 예를 들겠습니다.


#define BLOCK_SIZE 256

StructuredBuffer   gBuf1;
StructuredBuffer   gBuf2;
RWStructuredBuffer gBufOut;

[numthreads(BLOCK_SIZE,1,1)]
void VectorAdd( uint3 id: SV_DispatchThreadID )
{
  gBufOut[id] = gBuf1[id] + gBuf2[id];
}


보통 이를 두고 ComputeShader 라고 합니다.
ComputeShader 를 위한 여러종류의 버퍼가 존재합니다.
더 많은 종류의 버퍼는 차후에 설명드리기로 하겠습니다.

StructuredBuffer 라고 정의된 키워드는 C언어의 구조체와 같은 구조를 가집니다.
즉, 개발자가 정의한 구조체입니다.
그런데 앞에 식별자가 없으면 읽기 전용이라는 의미입니다.
반면에 앞에 'RW' 라고 명시된 버퍼는 읽기/쓰기 가 가능한 버퍼라는 의미입니다.
우리는 GPU 가 처리한 결과는 읽기/쓰기가 가능해야 하기 때문에,
결과를 저장하는 버퍼는 'RW" 가 명시되어 있습니다.
최적화를 위해서 각 목적에 맞게 버퍼를 사용해야 할 것입니다.^^


< ComputeShader 의 생성 >

pD3D->CreateComputeShader( pBlob->GetBufferPointer(),
                                             pBlob->GetBufferSize(),
                                             NULL,
                                             &pMyShader );  // hw fmt


CreateComputeShader() API 를 통해서 간단히 ComputeShader 를 생성할 수 있습니다.


< 입력을 위한 GPU 버퍼 만들기 >

우리가 GPGPU 를 활용하는 것은 CPU 를 활용하는 것보다 빠르게 결과를 도출하기 위해서입니다.
이를 위해서는 GPU 가 빠르게 액세스할 수 있는 버퍼가 있어야 할 것이며,
당연히 이것은 비디오 메모리에 존재해야 할 것입니다.
그래서 우리는 DirectX 인터페이스를 통해서 비디오 메모리를 생성을 합니다.


D3D11_BUFFER_DESC descBuf;
ZeroMemory( &descBuf, sizeof(descBuf) );
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
desc.StructureByteStride = uElementSize;
desc.ByteWidth = uElementSize * uCount;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;

pD3D->CreateBuffer( &desc, pInput, ppBuffer );

주의해야 할 것은 바로 'BindFlags' 입니다.
'D3D11_BIND_UNORDERED_ACCESS' 라는 플래그를 주고 있습니다.
이것은 PixelShader 나 ComputeShader 에서 병렬적으로 실행하는 버퍼를 의미
합니다.


< 뷰를 만들자!! >

버퍼 리소스를 만들었으면, 이제 이를 실제 파이프라인에서 액세스할 수 있는 매커니즘을 만들어야 합니다.
즉, ShaderResourceView 를 만들어야 합니다.
DirectX10 부터는 아래와 같이 리소스들을 다루어야 합니다.



앞선 시간들을 통해서 View 라는 개념을 충분히(?) 숙지하셨을 것이라 생각합니다.^^
대체로 ShaderResourceView 는 파이프라인 스테이지에서 읽기 전용입니다.
그런데, 아래 UnorderedAccessView 는 양방향 화살표로 되어있습니다.
읽기/쓰기가 가능한 형태입니다.
이를 통해서 결과를 비디오 메모리에 있는 버퍼에 결과를 기록할 수 있음을 보여주고 있는 것입니다.


실제 API 를 통한 View 생성은 다음과 같습니다.

D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
desc.Buffer.FirstElement = 0;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Buffer.NumElements = uCount;

pD3D->CreateUnorderedAccessView( pBuffer, // Buffer view is into
                                                      &desc,  // above data
                                                      &pMyUAV ); // result


중요한 부분은 ViewDimension 부분입니다.
'D3D11_UAV_DIMENSION_BUFFER' 를 설정하고 있는데,
이는 ComputeShader 상에서 이 버퍼를 일반적인 버퍼로 보겠다
는 의미입니다.
즉, 샘플링 작업을 전혀하지 않습니다. 
이는 어떠한 수정도 없이 데이터를 있는 그대로 보존합니다.


< 실행 단계 >

이제까지는 모두 준비 단계였습니다.
이제는 실제 실행 단계에 대해서 언급해 보겠습니다.

먼저, ComputeShader 를 현재 파이프라인 스테이지에 아래와 같이 바인딩 해주어야 합니다.
pD3D->CSSetShader( pMyShader, NULL, 0 );


그 다음에는 뷰를 바인딩해야 합니다.
pD3D->CSSetUnorderedAccessViews( 0,
                                                        1,
                                                       &pMyUAV,
                                                       NULL );


이제 마지막으로 GPU 에게 현재 바인딩된 내용을 바탕으로 연산해 줄 것을 요청합니다.
pD3D->Dispatch( GrpsX, GrpsY, GrpsZ );


이제 실행의 단계가 모두 끝났습니다.
이 단계까지 끝나면, 실행 결과가 비디오 메모리에 존재합니다.
우리가 결과를 확인하기 위해서는 CPU 가 액세스할 수 있는 버퍼로 결과를 복사
해야 합니다.


< 결과 복사해 오기 >

결과를 CPU 가 액세스 하기 위해서는 어떻게 해야 할까요?
이전 시간을 통해서 언급드렸듯이,
DX10 부터는 리소스에 대한 세부적인 액세스 권한에 대한 플래그를 설정할 수 있습니다.
그래서 다음과 같은 설정으로 버퍼를 만듭니다.

D3D11_BUFFER_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.MiscFlags = 0;
pD3D->CreateBuffer( &desc, NULL, &StagingBuf );


'D3D11_CPU_ACCESS_READ' 라는 플래그를 통해서,
이 버퍼는 CPU 가 액세스 할 수 있는 형태로 만듭니다.
그리고 'D3D11_USAGE_STAGING' 라는 플래그를 통해서
단순히 비디오 메모리에 있는 내용을
CPU 도 접근 할 수 있는 메모리로 복사해오는 버퍼임을 명시
합니다.


그리고 아래와 같이, 준비해둔 버퍼에 실제로 메모리를 복사해 옵니다.
pD3D->CopyResource( StagingBuf, pBuffer );

마침내 우리는 GPU 에 의해서 처리된 결과를 확인할 수 있게 되었습니다.


< 마치며...>

지금까지 DirectCompute 를 활용하는 일련의 절차에 대해서 살펴보았습니다.
DirectX11 의 API 가 생소해서 어려워 보일 수 있지만,
실제로 DirectCompute 의 절차는 그리 복잡하지는 않습니다.
현재 DirectCompute 의 활용은 SDK 샘플에 'NBodyGravity' 라는 이름으로 들어있습니다.
제가 여기에 대해서 자세히 언급드리면 좋겠지만,
그것은 차후로 미루기로 하겠습니다.^^


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