Search

C++ STL을 알고 있는 분들은 ‘parallel_for_each’에서 ‘parallel_’만 빼면 남는 ‘for_each’는 본적이 있고 사용해본 경험도 있을 것입니다.

 

parallel_for가 for문을 병렬화 한 알고리즘이라면 parallel_for_each는 STL의 for_each 알고리즘을 병렬화 한 것입니다.

 

STL 컨테이너에 있는 데이터를 처리할 때 for_each를 사용한 것을 쉽게 parallel_for_each로 바꾸어 아주 손 쉽게 병렬화 할 수 있습니다.

 

 

parallel_for_each의 원형

 

template < typename _Input_iterator, typename _Function >

_Function parallel_for_each( _Input_iterator _First,  _Input_iterator _Last,   _Function _Func );

 

_First : 시작 위치

_Last : 마지막 위치

_Func : 병렬 처리에 사용할 함수(함수 객체, 함수, 람다 식)

 

for_each에 대해서 알고 있는 분들은 앞서 소개한 parallel_for 보다 더 쉽다고 느낄 것입니다. 기존의 for_each가 사용하는 파라미터도 같습니다. 기존에 사용했던 for_each parallel_for_each로 바꿀려면 알고리즘 이름만 바꾸어도 됩니다.

 

 

 

초 간단 parallel_for_each 사용 방법

 

1. 필요한 헤더 파일 포함

#include <ppl.h>


2.네임 스페이스 선언

using namespace Concurrency;

 

3. parallel_for_each에서 사용할 함수 정의

 

4. parallel_for_each에서 사용할 STL 컨테이너 정의

 

5. parallel_for_each 사용

 

 

 

parallel_for_each를 사용하는 간단한 예제


#include <iostream>

#include <algorithm>

#include <vector>

using namespace std;

 

#include <ppl.h>

using namespace Concurrency;

 

int main()

{

     vector< int > ItemCdList(10);

     generate( ItemCdList.begin(), ItemCdList.end(), []() -> int {

                                       int n = rand();

                                       return n; }

              );

 

      cout << "for_each" << endl;

      for_each( ItemCdList.begin(), ItemCdList.end(), [] (int n) {

                            cout << "<" << n << ">"; } );

      cout << endl << endl;

 

      cout << "parallel_for_each - 1" << endl;

      parallel_for_each( ItemCdList.begin(), ItemCdList.end(), [] (int n) {

                                    cout << "<" << n << ">"; }

                        );

      cout << endl << endl;

 

      cout << "parallel_for_each - 2" << endl;

      critical_section rt;

      parallel_for_each( ItemCdList.begin(), ItemCdList.end(), [&] (int n) {

                               rt.lock();

                              cout << "<" << n << ">";

                               rt.unlock(); }

                       );

 

      getchar();

      return 0;

}

 


위 예제는 vecter 컨테이너에 램덤으로 10개의 숫자 값을 채운 후 출력하는 것입니다.


for_each paralle_for_each 사용 방법이 이름만 다를 뿐 똑 같습니다.




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

 

 

위 예제의 결과입니다.

 



공유 자원 동기화 문제


parallel_for 때도 잠시 언급했듯이 parallel_for_each는 순서대로 실행하지 않고 병렬로 실행하므로 for_each를 사용한 것과 비교해 보면 출력 순서가 서로 다릅니다.

그리고 특히 문제가 되는 것이 공유 자원을 사용할 때 따로 동기화 시키지 않으면 원하지 않는 결과가 나옵니다.

 



위와 같은 잘못된 결과는 나올 수도 있고 안 나올 수도 있습니다. 즉 타이밍에 의해서 발생하는 것이기 때문입니다. 이것이 병렬 프로그래밍의 어려움 중의 하나인데 에러가 언제나 발생하면 빨리 발견하여 처리할 수 있는데 공유 자원을 동기화 하지 않았을 때 발생하는 문제는 바로 발생할 수도 있고 때로는 여러 번 실행했을 때 간혹 나올 때도 있어서 버그 찾기에 어려움이 있습니다.

 

공유 자원의 동기화가 깨어지는 것을 막기 위해서는 동기화 객체를 사용하면 됩니다. 위 예제에서 두 번째 사용한 parallel_for_each‘critical_section’이라는 동기화 객체를 사용하여 공유 자원을 안전하게 보호하고 있어서 올바르게 값을 출력하고 있습니다.

‘critical_section’에 대해서는 다음 기회에 자세하게 설명하겠습니다.

 

parallel_for_each에 대해서는 이것으로 마무리하고 다음 번에는 parallel_invoke에 대해서 설명하겠습니다.

 

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

Parallel Patterns Library(이하 PPL)에는 데이터 컬렉션을 대상으로 쉽게 병렬 작업을 할 수 있게 해 주는 알고리즘이 있습니다. 이 알고리즘들은 생소한 것들이 아니고 C++의 표준 템플릿 라이브러리(STL)에서 제공하는 알고리즘과 비슷한 모양과 사용법을 가지고 있습니다.

( *데이터 컬렉션은 데이터 모음으로 배열이나 STL 컨테이너를 생각하면 됩니다 )

 

 

PPL에서 제공하는 병렬 알고리즘은 총 세 개가 있습니다.

 

1. parallel_for        알고리즘

2. parallel_for_each 알고리즘

3. parallel_invoke    알고리즘

 

 

세 개의 알고리즘 중 3 parallel_invoke만 생소하지 1번과 2번은 앞의 ‘parallel_’이라는 글자만 빼면 ‘for’‘for_each’ C++로 프로그래밍할 때 자주 사용하는 것이므로 친숙하게 느껴질 겁니다.

실제 병렬 여부만 제외하면 우리가 알고 있는 것들과 비슷한 동작을 합니다. 그래서 쉽게 배울 수 있고 기존의 코드에 적용하기도 쉽습니다.

 


parallel_for 알고리즘은 일반적인 for문을 사용할 때와 비슷하게 데이터 컬렉션에서 시작할 위치와 마지막 위치, 증가분(생략 가능합니다)에 해야할 작업 함수를 파라미터로 넘기면 됩니다. 사용 방법에서 for문과 다른 점은 작업 함수를 넘긴다는 점입니다.

 

parallel_for_each 알고리즘은 기존 for_each와 거의 같습니다. 데이터 컬렉션에서 시작할 위치, 마지막 위치, 작업 함수를 파라미터로 넘기면 됩니다. parallel_for의 경우 기존의 for문을 사용할 때는 작업 함수를 파라미터로 넘기지 않기 때문에 기존 for 문에 비해서 구조가 달라지지만 parallel_for_each는 기존 for_each와 파라미터 사용 방법이 같기 때문에 알고리즘의 이름만 바꾸면 될 정도입니다.

 

parallel_invoke 알고리즘 이전 회에 설명한 태스크 그룹과 비슷한면이 있습니다. 태스크 그룹과의 큰 차이점은 병렬로 할수 있는 작업은 10개로 제한 되지만 사용 방법은 태스크 그룹보다 더 간결한 점입니다다. 병렬 작업의 개수가 10개 이하인 경우 태스크 그룹보다 parallel_invoke를 사용하는 것이 훨씬 더 적합하다고 생각합니다.

 

 

 

 

이번은 간단하게 PPL에 있는 세 가지 병렬 알고리즘을 소개하는 것으로 마칩니다. 다음 회부터는 이번에 소개했던 세 개의 알고리즘을 하나씩 하나씩 자세하게 설명하겠습니다.

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


 

티스토리 툴바