2010-07-19 3 views
12

목록의 요소에 대해 철저한 쌍 비교를 수행하는 방법을 병렬 처리해야합니다. 직렬 구현은 간단합니다 :같은 목록에 중첩 된 Parallel.ForEach 루프가 있습니까?

foreach (var element1 in list) 
    foreach (var element2 in list) 
     foo(element1, element2); 

이 경우 foo는 element1 또는 element2의 상태를 변경하지 않습니다. 나는 간단하게 할 중첩 Parallel.ForEach 문에 안전하지 알고 :

Parallel.ForEach(list, delegate(A element1) 
{ 
    Parallel.ForEach(list, delegate(A element2) 
    { 
     foo(element1, element2); 
    }); 
}); 

은 무엇 병렬 작업 라이브러리를 사용하여이를 구현하는 이상적인 방법이 될 것이다?

답변

11

병렬 및 일반 루프가 하나만 있을까요? 그러니

Parallel.ForEach(list, delegate(A element1) 
{ 
    foreach(A element2 in list) 
    foo(element1, element2) 
});

또는

foreach(A element1 in list) 
{ 
    Parallel.ForEach(list, delegate(A element2) 
    { 
    foo(element1, element2); 
    }); 
}

는 잘 속도를해야합니다. 어쨌든 사이클 당 쓰레드가 될 수 없었기 때문에 중첩 된 병렬 루프보다 빠르거나 약간 느릴 수 있습니다.

당신이 코어 수는 목록의 항목 수는 두 배 이상되는 시스템에서 코드를 실행하는 적어도 경우
14

, 나는 Parallel.ForEach의 임베디드 않는 것이 좋습니다 모르겠어요.

즉, 쿼드 코어를 대상으로하고 목록에 1,000 개의 항목이있는 경우 상위 루프를 병렬 처리하면됩니다. 두 루프를 병렬 처리한다고해서 코드가 더 빠르지는 않지만 은 병렬 작업에 성능 비용이 많이 들기 때문에 훨씬 느립니다.. 각 반복에서

alt text http://www.freeimagehosting.net/uploads/ca97f403f8.png

는 몇 밀리 초는 다음 반복을 실행해야하는 스레드를 결정하기 위해 Parallel.ForEach에 의해 삭제됩니다. 예를 들어 7 개 항목이 있다고 가정 해 보겠습니다. 상위 루프를 병렬 처리하면 해당 밀리 초가 7 회 손실됩니다. 두 루프를 병렬 처리하면 7 × 7 = 49 번 손실됩니다. 설정이 크면 클수록 과열이 커집니다.

+3

병렬 작업이 있기 때문에 - 그보다 더 똑똑합니다. –

+0

물론 아닙니다. 기본적으로 코어로 스레드를 만듭니다. 그러나 문제는 각 반복 후에 어느 스레드가 다음 반복을 실행해야하는지 찾기 위해 시간을 할애한다는 것입니다. –

+0

많은 스레드가있을 것이라고는 생각하지 않습니다. 모든 함수 호출에 대한 작업을 큐에 넣는 것은 각 외부 루프에 대해 PFX 엔진을 호출하는 것보다 훨씬 더 많은 오버 헤드가있을 것입니다. – Gabe

1

두 개의 중첩 루프는 본질적으로 당신이 foo 목록 자체의 카트리 지 제품을 원함을 의미합니다. 먼저 모든 쌍을 임시 목록에 작성한 다음 Parallel.ForEach를 사용하여 해당 목록을 반복하여 전체 작업을 병렬 처리 할 수 ​​있습니다.

EDIT : 모든 조합의 목록을 만드는 대신 반복자를 사용하여 조합으로 2 요소 튜플을 반환 할 수 있습니다. Parallel.ForEach는 여전히 튜플 처리를 병렬 처리합니다.

병렬 처리 중에 예상되는대로 결과가 다시 밖으로 순서가 올 것을 보여주기 위해 현재 반복 단계 밖으로 샘플 인쇄를 다음과 같은 많은 스레드를 만들 것 PFX를 가정하지 마십시오

const int SIZE = 10; 
    static void Main(string[] args) 
    { 
     List<int> list = new List<int>(SIZE); 
     for(int i=0;i<SIZE;i++) 
     { 
      list.Add(i); 
     } 


     Parallel.ForEach(GetCombinations(list),(t,state,l)=> 
      Console.WriteLine("{0},{1},{2}",l,t.Item1,t.Item2)); 

    } 

    static IEnumerable<Tuple<int,int>> GetCombinations(List<int> list) 
    { 
     for(int i=0;i<list.Count;i++) 
      for(int j=0;j<list.Count;j++) 
       yield return Tuple.Create(list[i],list[j]); 
    } 
+0

아마도 Enumerable.Range()에 대해 알지 못합니다. 정말 편리합니다! 코드에서, 여기서 2 개가 아닌 3 개의 루프를 실행합니다. 그 중 2 개는 전혀 병렬이 아닙니다. 그것은'Foo()'가 무엇을하는지에 달려 있습니다 (답은 Console.WriteLine). 그러나 이것은 평행도를 추가하지 않고 정상적으로 두 번 반복하는 것보다 느려질 것입니다. –

관련 문제