Visual Studio 2010 공식 팀 블로그 @vsts2010

Posted by 흥배

원래 저번 주에 글을 올릴 예정이었으나 근래에 제 몸 상태와 집 PC 상태가 메롱이 되어버려 한 주 늦게 글을 올립니다(혹시 기다리고 계시는 분이 있었는지 모르겠네요 ^^;;; )



for 문의 병렬화 

이번에는 PPL의 세 개의 알고리즘 중 parallel_for 알고리즘에 대해서 이야기 하겠습니다.

앞 글에서 간단하게 설명했듯이 parallel_for는 그 이름을 보면 유추 할 수 있듯이 for 문을 병렬화 한 알고리즘입니다.

 

아주 많은 횟수로 반복 작업을 해야할 때 하나의 스레드로 처리하는 것보다는 여러 스레드로 동시에 처리하면 훨씬 빨라지는 것은 당연하겠죠? 바로 이 때 사용하면 좋습니다.

하지만 parallel_for 알고리즘은 아무 곳에나 사용할 수는 없습니다. 루프의 반복 계산 사이에 리소스를 공유하지는 않으면서 루프의 본체가 있는 경우 사용하면 편리합니다.

( 앞의 계산 결과를 다음 계산에서 사용해야 된다면 병렬로 실행하기 힘듭니다 )

 

 

parallel-for의 원형

 

두 개의 오버로드 버전이 있습니다.

 

template < typename _Index_type, typename _Function >

_Function parallel_for( _Index_type _First,  _Index_type _Last, _Function _Func );

_Index_type _First : 시작 위치

_Index_type _Last : 마지막 위치

_Function _Func : 병렬 처리로 사용할 함수

 

 

template < typename _Index_type, typename _Function >

_Function parallel_for( _Index_type _First, _Index_type _Last, _Index_type _Step, _Function _Func );

_Index_type _First : 시작 위치

_Index_type _Last : 마지막 위치

_Index_type _Step : 증분 값

_Function _Func : 병렬 처리로 사용할 함수

 

파라미터 값을 보면 for에서 사용하는 것과 비슷하다는 것을 알 수 있을겁니다. 차이점은 첫 번째 버전의 경우 증분 값으로 1이 자동으로 사용된다는 것과 마지막 파리미터로 병렬 처리에 사용할 함수를 사용한다는 것입니다.

 

 

for와 비슷하므로 for를 사용하는 대 부분을 prarallel_for로 변경할 수 있습니다. 다만 parallel_for 알고리즘에서는 반복 변수의 현재 값이 _Last 보다 작으면 중단합니다 ( 보통 for 문과 다르게 ‘<’ 조건만 사용합니다 ).

또 _Index_type 입력 파라미터는 정수형이어야만 합니다.

parallel_for 파라미터가 1보다 작은 경우 invalid_argument_Step 예외를 던집니다.

 


 

초 간단 parallel_for 사용 방법

 

1. 필요한 헤더 파일 포함
  #include <ppl.h>


2.
네임 스페이스 선언

  using namespace Concurrency;

 

3. parallel_for에서 호출할 작업 함수 정의

 

4. parallel_for에서 사용할 data set 정의

 

5. parallel_for 사용

 

 

 그럼 아주 간단한 실제 사용 예제 코드를 볼까요?

 

#include <ppl.h>

#include <iostream>

 

using namespace Concurrency;

using namespace std;

 

 

int main()

{

    int CallNum = 0;

    int Numbers[50] = { 0, };


   
parallel_for( 0, 50-1, [&](
int n ) {

        ++CallNum;

        Numbers[n] += CallNum;

       }               

      );

 

    for( int i = 0; i < 50; ++i )

    {

        cout << i << " : " << Numbers[i] << endl;

    }

 

    getchar();

    return 0;

}


 

위 예제는 Numbers라는 int 형 배열의 각 요소에 CallNum 이라는 변수를 더하는 것입니다. 간단하고 확실하게 parallel_for 사용 방법을 보이기 위해 허접한 예제를 만들게 되었음을 양해 바랍니다.^^;;; ( 다음에 기회가 되면 좀 더 멋지고 실용적인 예제를 보여드리도록 하겠습니다 )

예제에서는 코드를 간략화 하기 위해서 parallel_for의 마지막 파리미터로 람다 식을 사용했습니다.

위 예제를 '초 간단 parallel_for 사용 방법'의 순서에 비추어보면 아래 그림과 같습니다.

 

 


예제를 실행하면 아래와 같은 결과가 나옵니다.

 

(길어서 일부만 캡쳐 했습니다)

 

실행 결과를 보면 Numbers 배열의 각 요소의 값이 순서대로 증가되지 않았다라는 것을 알 수 있습니다. 만약 보통의 for 문이라면 Numbers[0] 1, Numbers[1] 2 라는 값으로 됩니다. 그러나 parallel_for는 병렬적으로 실행되므로 순서가 지켜지지 않습니다. CallNum 라는 변수는 parallel_for의 모든 스레드에서 접근하는 공유 변수이므로 동기화 되지 않았다라는 것도 유의해야 합니다.

 

Parallel_for를 사용할 때 순서대로 실행하지 않고, 공유 변수는 동기화 되지 않음을 잊지마시기를 바랍니다.

 

이것으로 (너무?)간단하게 parallel_for에 대해서 알아 보았습니다. 다음에는 parallel_for_each에 대해서 설명하겠습니다.




수정

1. 덧글의 ivyfore님이 알려주신대로

parallel_for( 0, 50-1, [&]( int n )가 아닌

 parallel_for( 0, 50, [&]( int n ) 가 되어야 합니다.

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 이 경우 data race로 맨끝에 CallNum이 50보다 작을 수 있는데... 그 사실을 보여주고 atomic 연산을 사용하면 항상 50이 나오게 됨을 보여주시면 더 좋을 것 같습니다. ^^

    • 좋은 생각입니다. Concurrency Runtime에 동기화 객체도 있으니 다음에 이걸 설명할 때 위 예제의 문제을 해결하는 예제를 만들어 볼께요^^

  2. 메론우유 2011/08/26 15:19

    음 궁금한게 있는데 저 패러랠 구문의 마지막에 나오는 [](int n)이나 [&](int n), []()->int 와 같은 구문은 뭐가 어떻게 사용되는 건가요;;;

    완전 처음 보는 구문이라 전혀 감이 안 잡히는데...
    게다가 만약 int n이 구문안에서 안 쓰이면 어떻게 되는건가요?

    • 람다 형식인데요..
      [&]는 람다식이 있는곳의 지역변수 참조..
      (int n) 은 넘겨받는 인자 파라메타..
      -> int 반환값이 int형

      요걸 함수로 표현하면

      int func(int n)
      {
      return 0;
      }

      c++0x 카테고리의 람다식을 찾아보시면 될꺼같네요

    • 메론우유 2011/09/06 14:19

      아 지역변수를 참조하겠다는 문장이였군요 ㄷㄷ;;
      주소 개념에서만 계속 생각했더니 대체 뭐지 라는 생각뿐이였는데...

      만약 기존의 for 문들에 적용을 하게 된다면 그냥 for문의 형태를 parallel_for 쪽으로 돌리면 되는건가요? 크리티컬 섹션이나 그런거를 신경쓰지 않는 다는 가정하에서요.

      아 그리고 이중 for문 같은 경우는 parallel_for을 두번 쓰게 되는건가요?

  3. 제가 실험을 해보니,

    for(i=0;i<50;i++)는,


    parallel_for(0,50-1,[&](int i){}) 가 아닌,

    parallel_for(0,50,[&](int i){})와 대응되네요.

    새로 배우시는 분들 착오 없으시기 바랍니다!!