안녕하세요. VSTS 2010 공식 팀에서 Twitter 를 시작했습니다. Twitter 를 통해 차마 시간이 없어 정리하지 못한 정보나 알아두면 좋은 팁과 정보 등을 단문 메시지로 여러분들에게 전달해 드릴 예정입니다.

차세대 플랫폼인 Visual Studio / Team System 2010 등 관심 있는 분들의 많은 Following 을 부탁 드립니다.

http://twitter.com/vsts2010

   

 

   

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 하나둘넷 2009.10.06 23:42 Address Modify/Delete Reply

    트위터에 좋은 내용들이 짧게 올라오네용.. 잘 보고 가요~^^

    • 땡초 2009.10.07 09:17 Address Modify/Delete

      저는 트위터를 twhirl 이라는 클라이언트 어플케이션으로 사용하고 있답니다.
      제가 써 본 것중에는 제일 편하네요.
      http://www.twhirl.org/download

지난 6월 10일 VSTS 2010 팀에서 세미나를 진행하였습니다. 세미나 프레젠테이션은 MEF 세미나 자료 에서 볼 수 있습니다.

그리고 얼마 전에 촬영한 동영상도 공개가 되었습니다. VSTS 2010 은 굉장히 큰 규모의 개발 도구, 개발 플랫폼 등의 버전 업으로 아직도 많은 부분을 알려드리지 못했고, 미처 저희들도 모두 알지 못하는 부분도 많습니다.

하지만 남들보다 먼저 접해본 분야이고 이것을 알려드리기 위해 진행한 세미나입니다. 아래의 동영상을 시청하시고 VSTS 2010 에 많은 관심을 가져주세요. ^^

   

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

   

공성의 - VSTS 2010 의 소프트웨어 품질 관리

   

김병진님의 - VSTS 2010 Architecture & UML

   

   

엄준일 ASP.NET MVP - Managed Extensibility Framework

   

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

   

Posted by 땡초 POWERUMC

댓글을 달아 주세요

지난 포스트에서 Parallel Extension 과 LINQ 를 이용한 PLINQ 에 대해서 살펴보았습니다. 지난번에 얘기했듯이 Manual Parallelism 는 Parallel Extension 의 성능을 절대 따라올 수 없다고 하였습니다. 왜냐하면, Parallel Extension 은 Manual Parallelism 의 병렬 처리 방식보다 더 복잡하고 정밀한 병렬 처리 알고리즘으로 처리하기 때문입니다.
 
Parallel Extension 이란?
 
Parallel Extension 은 전혀 새로운 것이 아닙니다. C# 3.0 의 LINQ 는 LINQ 쿼리식을 제공하기 위해 새로운 컴파일러(Compiler) 가 필요했습니다. 정확히 말하자면 C# 의 Language Service 의 버전이 업그레이드 되었고, LINQ 쿼리식을 편하게 쓸 수 있도록 Visual Studio 2008 을 사용해야 했습니다. 다시 말하자면, LINQ 쿼리식이 아닌 확장 메서드(Extension Methods) 만으로 쿼리가 가능했다는 것이 이것을 증명해 줍니다. 확장 메서드(Extension Methods) 는 결국 IL 레벨에서는 정적(Static) 인 인스턴스(Instance) 에 불과하니까요.
 
Parallel Extension 은 새로운 컴파일러(Compiler) 가 필요하지 않습니다. .NET 의 기본적인 코어(Core) 인 mscorelib.dll, System.dll, System.Core.dll 만을 사용하여 구현이 되었습니다. 그리고, 기존의 ThreadPool 을 개선하였고, LINQ 와 통합하여 선언적으로 Parallel Extension 을 사용할 수도 있게 되었죠.
 
Task Parallel Library 를 통해 데이터 처리와 어떤 작업(Task)에 대해서도 병렬 처리도 가능해 졌습니다. 이제는 데이터의 병렬 처리뿐만 아니라, 작업(Task) 단위로서도 Task Parallel Library 로 병렬 처리가 가능합니다.
 
이제는 병렬 처리가 된다는 것이 중요한 게 아니라, 병렬 처리의 내부적인 예외 핸들링이나 Visual Studio 에서 디버깅(Debugging) 이 가능합니다. 이러한 새로운 매커니즘으로 내부적으로는 새로운 예외 핸들링 모델(Exception Handling Model)이 필요했습니다.
 
또한 .NET Framework 4.0 의 Parallel Extension 은 다양한 언어를 지원합니다. C#, VB.NET, C++, F# 그리고 .NET 컴파일러(Compiler) 로 컴파일(Compile) 되는 언어라면 상관없습니다. RoR/PHP 라도 .NET 컴파일러(Compiler)에 의해 컴파일(Compile) 된다면 Parallel Extension 을 사용하는데 전혀 문제가 없습니다.
 
 
Parallel Extension 아키텍처
 
 
[그림1] Parallel Extension 아키텍처 (클릭하면 확대)
 
Parallel Extension 의 병렬 처리는 .NET 컴파일러(Compiler) 로 컴파일(Compile) 되는 어떤 언어든 차별을 두지 않고, 병렬 처리 기능을 사용할 수 있습니다. PLINQ 로 작성된 쿼리(Query)는 별도의 PLINQ Provider 의 엔진(Engine) 에서 쿼리를 분석하는 과정을 거치게 됩니다. 쿼리 분석(Query Analysis) 에 의해 선언된 LINQ 쿼리식을 분석하여 최적의 알고리즘으로 각각의 작업을 배치하게 됩니다.
 

[그림2] Parallel Extension 작업 분할
 
각각 분배되는 작업은 쓰레드 큐(Thread Queue) 에 쌓이고, 이 큐에 쌓이는 작업(Task) 는 실제 작업자 쓰레드(Worker Thread) 에 할당이 됩니다. 하지만, Parallel Extension 은 단지 쓰레드에 할당하는 것으로 작업이 마치기를 기다리지 않습니다. 만약, 작업을 분배하는 것은 Manual Parallel 과 크게 다르지 않기 때문이죠.
 

[그림3] Parallel Extension 작업 분할
 
Parallel Extension Library 는 병렬 처리의 작업을 지속적으로 최적의 상태를 감시합니다. 예를 들어, A 의 작업이 Task 1, Task 2, Task 3 인데, B 의 작업은 모두 마친 상태라고 할 때, Parallel Extension Library 는 A 의 작업을 놀고 있는 B 에게 또 다시 분배합니다. 이러한 반복적으로 병렬 처리의 작업이 최적화 될 수 있도록 하여, 병렬 처리의 성능을 극한으로 끌어올립니다.
 
LINQ 만 알면 난 병렬 처리 개발자
 
Parallel Extension 은 LINQ 와 통합하기 위한 프로바이더(Provider) 를 제공합니다. 아직 LINQ 잘 모르신다구요? 30분만 투자하시면 LINQ 를 사용하는데 큰 지장이 없습니다. 그리고 그만큼 쉽습니다. LINQ 를 이해하기 위해 제네릭(Generic), 확장 메서드(Extension Methods), 익명 타입(Anonymous Methods) 도 함께 공부하시면 좋습니다.
 
예전에 이런 광고도 있었죠.
“비트 박스를 잘하려면?” “북치기, 박치기만 잘하면 됩니다”
 
“그럼 PLINQ 개발자가 되기 위해서는?” “AsParallel() 만 잘하면 됩니다.”
 
맞습니다. Parallel Extension Library 의 AsParallel() 확장 메서드(Extension Methods) 만 알면 당신도 이제 병렬 처리 개발자입니다. 이전 포스트의 PLINQ 예제에서 처럼 AsParallel() 만 붙이면 그것을 PLINQ 라고 부릅니다^^ (병렬 처리를 위한 확장 메서드와 옵션은 더 많이 존재합니다 )
 
아래는 AsParallel() 의 예 입니다.
private static void ParallelSort(List<Person> people)
{
       var resultPerson = from person in people.AsParallel()
                                    where person.Age >= 50
                                    orderby person.Age ascending
                                    select person;
 
       foreach (var item in resultPeople) { }
}
 
하지만, 무조건적인 병렬 처리는 오히려 성능을 저하시킬 수 있습니다. 특히 PLINQ 를 사용하는 병렬 처리는 .NET Framework 내부적으로 쿼리 분석(Query Analysis) 과정을 거치게 됩니다. 각각 프로세서(Processor) 에 분배된 데이터는 또 분배되고, 최적화가 가능할 때까지 계속적으로 분배됩니다. 마치 세포 분할이 일어나는 것처럼 말이죠.
 
최소한 병렬 처리를 위해서 데이터에 대한 이해와 추측이 가능해야 합니다.
 
1.     추측 가능한 데이터의 양
2.     추측 가능한 데이터의 내용
3.     추측 가능한 데이터 처리 시간
 
이러한 최소한의 예측 작업을 하지 않는다면, 오히려 PLINQ 를 이용할 때 성능은 저하될 수 있습니다. 예를 들어, 평균 데이터의 양이 2개라고 가정한다면, PLINQ 의 쿼리 분석(Query Analysis) 작업은 오히려 성능 저하의 요인이 됩니다. PLINQ 쿼리 분석(Query Analysis) 에 의해 두 번째 프로세서(Processor) 의 사용량이 많다고 판단된다면, 병렬 작업은 의미가 없어지고 오히려 성능을 저하시킬 테니까요. ‘쿼리 분석(Query Analysis) 작업이 눈 깜빡 거리는 시간(1/40(0.025)초) 이라고 가정한다면, 만 건의 쿼리 분석(Query Analysis) 작업 시간은 250초’가 될 테니까요.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

최근 대부분의 사용자들의 컴퓨터의 사양이 코어2 로 업그레이드 되고 있습니다. CPU 제품에 따라 코어에 대한 아키텍처가 다르지만, 기본적으로 이들 제품은 하나의 컴퓨터에 CPU 가 두 개인 제품들입니다. 인간과 비교하자면 뇌가 두 개인 사람인데 그다지 상상해서 떠올리고 싶지 않네요^^.

컴퓨터는 CPU 두 개를 보다 효율적으로 이용하기 위해 바로 Parallelism Processing(병렬 처리)를 하게 됩니다. 하나의 CPU 의 성능을 향상시키는 방법이 아닌, 두 개의 CPU 에게 작업을 할당함으로써 데이터의 처리 성능을 극대화 시키게 됩니다. 우리에게 익숙한 운영체제인 윈도우(Windows) 의 멀티 쓰레딩(Multi Threading) 을 생각하면 병렬 처리(Parallelism Processing) 는 그렇게 어려운 개념은 아닙니다.
 
[그림1] 어쨌든 뇌가 두 개 (여기에서 참조)
 
원래 오픈 소스 프로젝트로 Parallel Extension 프로젝트를 CodePlex 에서 본 기억이 있는데, 지금은 링크의 주소를 찾을 수 가 없네요. 구글을 통해 “Parallel Extension” 을 검색하시면, .NET 에서의 Parallel Programming 의 흔적을 찾아볼 수 있습니다.
 
우선 아래의 Person 클래스를 작성하여 테스트에 사용할 것입니다.
 
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
 
 
General~
 
코어(Core) 하나로 작업할 경우, 개발자는 아무것도 염려 하지 않아도 됩니다. 그 동안 우리가 배웠던 대로 코드를 작성하기만 하면 됩니다. 병렬 처리에 대한 고민을 하지 않고 개발한 코드라면 모두 이 범주에 속하겠네요. 이러한 방법은 가장 보편적으로 작성할 수 있습니다.
 
private static void GeneralSort(List<Person> people)
{
       List<Person> resultPeople = new List<Person>();
       foreach (Person person in people)
       {
             if (person.Age >= 50)
                    resultPeople.Add(person);
       }
 
       resultPeople.Sort((p1, p2) => p1.Age.CompareTo(p2.Age));
 
       foreach (var item in resultPeople) { }
}
 
List<Person> 개체를 파라메터로 넘겨주고, Person 중에 Age 가 50이 넘는 개체를 정렬하는 코드입니다.
바로 이 코드를 병렬 처리를 하고자 합니다. 이 코드를 병렬 처리를 하고자 한다면 코드의 양은 훨씬 늘어나고, 복잡한 처리를 해야 합니다.
 
 
Manual Parallelism
 
일반적으로 데이터의 처리를 병렬 처리로 전환하기 위해서는 쓰레드(Thread) 를 사용합니다. 쓰레드(Thread) 가 생성이 되면 커널 또는 물리적인 프로세서에 의해 의해 유휴 상태 또는 처리가 가능한 코어(Core) 로 작업이 할당되어 다중 작업(Multi Process) 을 가능하게 됩니다.
 
이러한 방법의 병렬 처리는 프로세서(Processor) 개수만큼 쓰레드(Thread) 를 생성하여 비동기 작업을 합니다.
 
private static void ThreadSort(List<Person> people)
{
       var resultPeople = new List<Person>();
       int partitionsCount = Environment.ProcessorCount;
       int remainingCount = partitionsCount;
       var enumerator = (IEnumerator<Person>)people.GetEnumerator();
       try
       {
             using (var done = new ManualResetEvent(false))
             {
                    for (int i = 0; i < partitionsCount; i++)
                    {
                           ThreadPool.QueueUserWorkItem(delegate
                           {
                                 var partialResults = new List<Person>();
                                 while (true)
                                 {
                                        Person baby;
                                        lock (enumerator)
                                        {
                                              if (!enumerator.MoveNext()) break;
                                              baby = enumerator.Current;
                                        }
                                        if (baby.Age >= 50)
                                        {
                                              partialResults.Add(baby);
                                        }
                                 }
                                 lock (resultPeople) resultPeople.AddRange(partialResults);
                                 if (Interlocked.Decrement(ref remainingCount) == 0) done.Set();
                           });
                    }
                    done.WaitOne();
                    resultPeople.Sort((p1, p2) => p1.Age.CompareTo(p2.Age));
             }
       }
       finally
       {
             if (enumerator is IDisposable) ((IDisposable)enumerator).Dispose();
       }
 
       foreach (var item in resultPeople) { }
}
 
중요한 부분은 추출된 데이터의 정렬(Sort) 작업입니다. 이 작업을 하기 위해서는 모든 쓰레드(Thread) 의 작업이 끝나야 합니다. 만약 모든 쓰레드(Thread) 가 종료되지 않은 시점에서 정렬 작업을 하게 되면, 과연 정렬된 데이터를 신뢰할 수 있을까요?? ( 왜 그런지는 여러분의 상상에 맡기도록 합니다. )
 
정렬 작업을 하기 전 ManualResetEvent 의 WaitOne() 메서드를 호출하여 모든 쓰레드(Thread) 의 WaitHandle 이 작업이 신호를 받을 때까지(동기화 작업) 기다려야 합니다. 예를 들어, 두 개의 쓰레드(Thread) 가 생성 되고 첫 번째 쓰레드는 이미 작업을 종료하였지만, 두 번째 쓰레드는 아직 작업이 완료되지 않았다면, 작업을 마친 모든 쓰레드(Thread) 는 가장 늦게 처리가 완료되는 쓰레드를 기다려야 정렬 작업을 진행할 수 있습니다.
 
마지막으로, 위의 코드의 병렬 처리 작업은 성능에 한계가 있습니다. 프로세서(Processor) 개수만큼 쓰레드(Thread) 를 생성하여 작업을 분배하는 방식이기 때문에, 병렬 처리 작업의 성능은 곧 프로세서(Processor) 개수가 될테니까요!
 
 
Parallel Extension
 
C# 4.0 은 병렬 처리를 하기 위해 코드의 양을 획기적으로 줄일 수 있습니다.
 
private static void ParallelSort(List<Person> people)
{
       var resultPerson = from person in people.AsParallel()
                                    where person.Age >= 50
                                    orderby person.Age ascending
                                    select person;
 
       foreach (var item in resultPeople) { }
}
 
LINQ 식을 사용하여 데이터 처리와 정렬 작업을 간단하게 할 수 있습니다. 감격이네요^^ 바로, .NET Framework 4.0 의 Parallel Extension 을 사용하여 LINQ 처럼 사용하는 것을 PLINQ 라고 합니다.
 
Q : foreach (var item in resultPeople) { } 코드를넣었나요?
 
A: 동일한 테스트를 하기 위함입니다. LINQ 식은 내부 구조의 특성상 “쿼리식”에 불과합니다.
보다 자세한 내용은 필자의 블로그를 참고하세요.
 
Parallel Extension 은 Manual Parallelism 보다 더 복잡하고 좋은 성능을 낼 수 있는 알고리즘으로 구현이 되어 있습니다. 그렇기 때문에 아무리 많은 코어를 가진 컴퓨터에서 동일한 테스트를 한다고 하여도 결코 Manual Parallelism 은 Parallel Extension 의 병렬 처리 성능을 기대할 수 없습니다.
 
이제 살며시 그 내부 구조도 궁금해 집니다. (다음에 계속…)
Posted by 땡초 POWERUMC

댓글을 달아 주세요

Task Parallel Library
 
Parallel Extension 은 PLINQ 와 더불어 확장 가능한 Task Parallel Library 를 제공합니다. Task Parallel Library 는 PLINQ 를 이용하지 않고 개별적이고 수동적인 병렬 처리 작업을 위해 사용할 수 있습니다.
 
Task Parallel Library 는 크게 세 가지 방법으로 병렬 처리를 위한 Library 를 제공합니다.
 
Loops
 
[그림1] Parallel.For 를 이용한 병렬 처리
 
[그림2] Parallel.Foreach 를 이용한 병렬 처리
 
Task Parallel Extension 으로 병렬 처리를 쉽게 처리할 수 있으며, 병렬 처리로 인자값을 넘기거나 하는 작업을 쉽게 할 수 있습니다.
 
Statements
 
 
[그림3] Parallel.Invoke 를 이용한 병렬 처리
 
 
Task
 
특히 Parallel Extension Library 에서 Task 는 수동적으로 병렬 처리를 하기 위해 다양한 기능을 지원합니다. 정교하게 스레드(Thread) 를 처리했던 것에 비하면 심플하고도 직관적으로 병렬 작업을 처리할 수 있습니다.
 
Task 는 보다 정교하게 병렬 처리 작업을 할 수 있습니다.
l 대기
l 취소
l 연장
l 상하(부모/자식) 간의 관계
l 디버그 지원
 
아래는 ThreadPool.QueueUserWorkItem 처럼 바로 작업을 시작하도록 합니다.
 
Task.StartNew(…);
 
아래는 Task 에 대해 대기 및 취소 작업을 진행하도록 합니다.
 
Task t1 = Task.StartNew(…);
t1.Wait();
t1.Cancel();
Task t2 = t1.ContinueWith(…);
 
아래는 작업에 대해 지속적인 병렬 처리를 가능하도록 합니다.
 
var p = Task.StartNew(() => {
    var c = Task.StartNew(…);
}
 
아래는 특정 작업의 결과를 받아 올 수 있습니다.
 
var p =
 Future.StartNew(() => C());
int result = p.Value;
 
 
 
Coordination Data Structures
 
병렬 처리 작업은 PLINQ 와 TPL(Task Parallel Library) 를 지원하기 위해 기존의 데이터 컬렉션 등이 등장하였습니다. 내부적으로 동기화를 지원하지 않았던 문제들을 지원하게 되었고, 특히 오늘날 멀티 코어(Multi Core) 프로세스를 위해 많은 동기적인 문제를 고민해야 했습니다. .NET Framework 4.0 은 이러한 공통적인 문제들을 해결하기 할 수 있습니다.
 
l Thread-safe collections
       ConcurrentStack<T>
       ConcurrentQueue<T>
       ConcurrentDictionary<TKey,TValue>
      
l Work exchange
       BlockingCollection<T>
       IProducerConsumerCollection<T>
l Phased Operation
       CountdownEvent
       Barrier
l Locks
       ManualResetEventSlim
       SemaphoreSlim
       SpinLock
       SpinWait
l Initialization
       LazyInit<T>
       WriteOnce<T>

Posted by 땡초 POWERUMC

댓글을 달아 주세요