김명신님께서 또 아주 좋은 지적을 해주셨습니다~~ List<T>도 ConcurrentBag<T>이든 하나의 컬렉션을 이용하는 건 바람직하지 않은 것 같다는 의견이셨는데요, 동감합니다 :) 그래서 제 나름대로 해결책을 강구해봤는데욤. 스레드 로컬 변수를 이용해서 스레드 별로 작업을 시키고, 나중에 하나의 리스트로 모으는 것입니다. 코드를 볼까욤~?
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class Program { static readonly object _sync = new object(); static IEnumerable<long> GetPrimeNumber(long num) { List<long> primeList = new List<long>(); Parallel.For<List<long>>(0, num + 1, () => new List<long>(), //스레드 로컬 변수 초기화 (i, outerLoopState, subList) => { bool isPrime = true; Parallel.For(2, i, (j, loopState) => { if (i % j == 0) { isPrime = false; loopState.Break(); } }); if (isPrime) { subList.Add(i); //스레드 로컬 리스트에 추가 } return subList; }, (subList) => //각 스레드 종료 후에 취합 { lock (_sync) { primeList.AddRange(subList); } }); return primeList; } static void Main(string[] args) { Stopwatch sw = new Stopwatch(); sw.Start(); IEnumerable<long> primeList = GetPrimeNumber(99999); sw.Stop(); Console.WriteLine("Elapsed : {0}, Found prime counts : {1}", sw.Elapsed.ToString(), primeList.Count()); //뭔가 한다. } } } |
위 코드를 보면, Parallel.For 루프에서 스레드 로컬 변수를 사용하도록 밑줄 친 부분들이 추가된 것을 볼 수 있습니다. 즉 이 Parallel.For는 List<long>타입의 스레드 지역 변수를 사용하며, 그 지역변수를 어떻게 초기화 할 것인지를 명시하고, 각 스레드가 종료할 때 지역 변수를 리턴하도록 한 것이죠. 그리고 Parallel.For의 마지막 매개변수로, 각 스레드가 종료할 때 어떤 동작을 취할 것인가를 명시하는데, 스레드가 끝날 때 리턴한 지역 변수가 매개변수로 들어오게 됩니다. 이렇게 하면 동기화를 좀 더 줄일 수 있는 것이죠. 그래서, 이왕 코드를 고친 김에 시간을 재봤습니다.
ConcurrentBag<T>사용(초) | 스레드 지역 변수 사용(초) | |
GetPrimeNumber(99999) | 2.54 | 2.52 |
GetPrimeNumber(199999) | 9.32 | 9.25 |
GetPrimeNumber(299999) | 20.03 | 19.96 |
미세하게 스레드 지역 변수를 사용한 쪽이 빠르긴 한데요, 위 코드의 경우 하나의 리스트로 값을 합치는 데 시간이 좀 소요되어서 큰 속도 향상이 없는 것으로 보입니다. 동기화해서 수행해야 하는 작업이 간단할 수록 속도차이는 벌어질 것으로 보이는데요, 가능하면 각자 작업 후에 하나로 합치는 쪽이 더 효율적일 것 같네요.
부족한 실력이다 보니 이 이상 뭐가 떠오르지 않네요 -_-;; 더 나은 방법이 있으면 알려주세용~ :)
'C# Parallel Programming' 카테고리의 다른 글
Welcome to Parallel C#(16) - 분배 전략. (1) | 2012.02.17 |
---|---|
Welcome to Parallel C#(15) - 브레이크와 멈춤의 상관관계. (2) | 2012.02.15 |
Welcome to Parallel C#(14.1) - 긴급 패치. (0) | 2012.02.13 |
Welcome to Parallel C#(14) - 거기까지. (6) | 2012.02.13 |
Welcome to Parallel C#(13) - 더 편하겡... 더 빠르겡... (0) | 2012.02.10 |