티스토리 뷰
최근 대부분의 사용자들의 컴퓨터의 사양이 코어2 로 업그레이드 되고 있습니다. CPU 제품에 따라 코어에 대한 아키텍처가 다르지만, 기본적으로 이들 제품은 하나의 컴퓨터에 CPU 가 두 개인 제품들입니다. 인간과 비교하자면 뇌가 두 개인 사람인데 그다지 상상해서 떠올리고 싶지 않네요^^.
컴퓨터는 CPU 두 개를 보다 효율적으로 이용하기 위해 바로 Parallelism Processing(병렬 처리)를 하게 됩니다. 하나의 CPU 의 성능을 향상시키는 방법이 아닌, 두 개의 CPU 에게 작업을 할당함으로써 데이터의 처리 성능을 극대화 시키게 됩니다. 우리에게 익숙한 운영체제인 윈도우(Windows) 의 멀티 쓰레딩(Multi Threading) 을 생각하면 병렬 처리(Parallelism Processing) 는 그렇게 어려운 개념은 아닙니다.
원래 오픈 소스 프로젝트로 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 의 병렬 처리 성능을 기대할 수 없습니다.
이제 살며시 그 내부 구조도 궁금해 집니다. (다음에 계속…)
'.NET > C#' 카테고리의 다른 글
Language Server Protocol, OmniSharp-Roslyn 빌드 오류 해결 (0) | 2017.01.23 |
---|---|
[C# 4.0] Parallel Extension - [2] 병렬 처리 아키텍처 (0) | 2009.02.16 |
C# 코드로 GAC 어셈블리 등록하기 (0) | 2008.08.21 |
LINQ 퀴즈 문제 풀어봅시다 (0) | 2008.08.07 |
.NET Framework 2.0 에서 LINQ TO Object 사용하기 (1) | 2008.07.24 |
- TAG
- c#, C# 4.0, Parallel Extension
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- 2,841,419
- Today
- 40
- Yesterday
- 71
링크
- ***** MY SOCIAL *****
- [SOCIAL] 페이스북
- [SOCIAL] 팀 블로그 트위터
- .
- ***** MY OPEN SOURCE *****
- [GITHUB] POWERUMC
- .
- ***** MY PUBLISH *****
- [MSDN] e-Book 백서
- .
- ***** MY TOOLS *****
- [VSX] VSGesture for VS2005,200…
- [VSX] VSGesture for VS2010,201…
- [VSX] Comment Helper for VS200…
- [VSX] VSExplorer for VS2005,20…
- [VSX] VSCmd for VS2005,2008
- .
- ***** MY FAVORITES *****
- MSDN 포럼
- MSDN 라이브러리
- Mono Project
- STEN
- 일본 ATMARKIT
- C++ 빌더 포럼
- .
TAG
- TFS 2010
- ALM
- ASP.NET
- 비주얼 스튜디오
- POWERUMC
- Visual Studio 2010
- Visual Studio 11
- github
- Team Foundation Server 2010
- testing
- Visual Studio
- Silverlight
- 엄준일
- .NET Framework 4.0
- Windows 8
- LINQ
- mono
- 팀 파운데이션 서버
- 땡초
- .NET
- Visual Studio 2008
- 비주얼 스튜디오 2010
- monodevelop
- MEF
- umc
- Managed Extensibility Framework
- test
- TFS
- Team Foundation Server
- c#
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 |
글 보관함
- 2020/05 (1)
- 2019/10 (3)
- 2018/11 (1)
- 2018/08 (2)
- 2017/04 (1)
- 2017/01 (2)
- 2016/11 (2)
- 2016/08 (1)
- 2016/05 (1)
- 2016/04 (2)
- 2016/02 (2)
- 2016/01 (1)
- 2015/05 (1)
- 2015/04 (2)
- 2015/03 (1)
- 2015/02 (1)
- 2015/01 (1)
- 2014/11 (1)
- 2014/09 (2)
- 2014/08 (2)
- 2014/05 (2)
- 2014/04 (3)
- 2014/03 (2)
- 2014/02 (2)
- 2014/01 (4)
- 2013/12 (2)
- 2013/11 (1)
- 2013/10 (2)
- 2013/09 (6)
- 2013/08 (3)