병렬 알고리즘

PPL(병렬 패턴 라이브러리)은 데이터 컬렉션에 대한 작업을 동시에 수행하는 알고리즘을 제공합니다. 이러한 알고리즘은 STL(표준 템플릿 라이브러리)에서 제공하는 알고리즘과 유사합니다.

병렬 알고리즘은 동시성 런타임의 기존 기능으로 구성됩니다. 예를 들어 Concurrency::parallel_for 알고리즘은 Concurrency::structured_task_group 개체를 사용하여 병렬 루프 반복을 수행합니다. 또한 parallel_for 알고리즘은 사용 가능한 컴퓨팅 리소스 수가 제공되면 최적의 방법으로 작업을 분할합니다.

단원

이 항목에서는 다음과 같은 병렬 알고리즘에 대해 자세히 설명합니다.

  • parallel_for 알고리즘

  • parallel_for_each 알고리즘

  • parallel_invoke 알고리즘

parallel_for 알고리즘

Concurrency::parallel_for 알고리즘은 같은 작업을 병렬로 반복적으로 수행합니다. 각 작업은 반복 값에 의해 매개 변수화됩니다. 이 알고리즘은 해당 루프의 반복 간에 리소스를 공유하지 않는 루프 본문을 사용하는 경우에 유용합니다.

parallel_for 알고리즘은 병렬 실행을 위한 최적의 방법으로 작업을 분할합니다. 작업 부하가 분산되지 않은 경우 작업 가로채기 알고리즘을 사용하여 이러한 파티션을 분산시킵니다. 하나의 루프 반복이 협조적으로 차단되는 경우 런타임에서 현재 스레드에 할당된 반복 범위를 다른 스레드 또는 프로세서에 재배포합니다. 마찬가지로 스레드가 반복 범위를 완료하면 런타임은 다른 스레드의 작업을 현재 스레드에 재배포합니다. parallel_for 알고리즘은 중첩 병렬 처리도 지원합니다. 하나의 병렬 루프에 다른 병렬 루프가 포함되어 있는 경우 런타임은 효율적인 병렬 실행 방식으로 루프 본문 간에 리소스 처리를 조정합니다.

parallel_for 알고리즘에는 두 개의 오버로드된 버전이 있습니다. 첫 번째 버전은 시작 값, 끝 값 및 작업 함수(람다 식, 함수 개체 또는 함수 포인터)를 사용하고 두 번째 버전은 시작 값, 끝 값, 단계 기준 값 및 작업 함수를 사용합니다. 이 함수의 첫 번째 버전에서는 1을 단계 값으로 사용합니다.

parallel_for를 사용하기 위해 많은 for 루프를 변환할 수 있습니다. 그러나 다음과 같은 면에서 parallel_for 알고리즘은 for 문과는 다릅니다.

  • parallel_for 알고리즘은 미리 결정된 순서로 작업을 실행합니다.

  • parallel_for 알고리즘은 임의의 종료 조건을 지원하지 않습니다. parallel_for 알고리즘은 반복 변수의 현재 값이 _Last보다 1 작을 때 중지됩니다.

  • _Index_type 형식 매개 변수는 정수 계열 형식이어야 합니다. 이 정수 형식은 부호가 있거나 없을 수 있습니다.

  • 루프 반복은 정방향이어야 합니다. _Step 매개 변수가 1보다 작으면 parallel_for 알고리즘에서 std::invalid_argument 형식의 예외를 throw합니다.

  • parallel_for에 대한 예외 처리 메커니즘은 for 루프의 예외 처리 메커니즘과 다릅니다. 병렬 루프 본문에서 동시에 여러 예외가 발생하는 경우 런타임은 실행 중에서 하나만 parallel_for라는 스레드로 전파합니다. 또한 하나의 루프 반복에서 예외를 throw하는 경우 런타임에서 바로 전체 루프를 중지하지는 않습니다. 대신 해당 루프가 취소 상태에 놓이고 런타임에서 아직 시작되지 않은 모든 작업을 무시합니다. 예외 처리 및 병렬 알고리즘에 대한 자세한 내용은 동시성 런타임에서 예외 처리를 참조하십시오.

parallel_for 알고리즘에서 임의의 종료 조건을 지원하지 않더라도 취소를 사용하여 모든 작업을 중지할 수 있습니다. 취소에 대한 자세한 내용은 PPL에서의 취소를 참조하십시오.

참고

특히 루프 본문이 비교적 작은 경우에는 부하 분산 때문에 발생하는 예약 비용 및 취소와 같은 기능 지원이 루프 본문을 병렬로 실행하여 얻을 수 있는 이점보다 크지 않을 수 있습니다.

예제

다음 예제에서는 parallel_for 알고리즘의 기본 구조를 보여 줍니다. 이 예제에서는 [1, 5] 범위의 각 값을 병렬로 콘솔에 출력합니다.

// parallel-for-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Print each value from 1 to 5 in parallel.
   parallel_for(1, 6, [](int value) {
      wstringstream ss;
      ss << value << L' ';
      wcout << ss.str();
   });
}

이 예제를 실행하면 다음과 같은 샘플 결과가 출력됩니다.

1 2 4 3 5

parallel_for 알고리즘은 각 항목에 대해 병렬로 작동하기 때문에 콘솔에 값이 출력되는 순서가 다릅니다.

parallel_for 알고리즘을 사용하는 전체 예제를 보려면 방법: parallel_for 루프 작성을 참조하십시오.

[맨 위로 이동]

parallel_for_each 알고리즘

Concurrency::parallel_for_each 알고리즘은 STL에서 제공되는 것과 같은 반복 컨테이너에 대해 병렬로 작업을 수행합니다. 이 알고리즘은 parallel_for 알고리즘에서 사용하는 것과 같은 분할 논리를 사용합니다.

parallel_for_each 알고리즘은 parallel_for_each 알고리즘이 동시에 작업을 실행한다는 점을 제외하고는 STL std::for_each 알고리즘과 비슷합니다. 다른 병렬 알고리즘과 마찬가지로 parallel_for_each도 특정 순서로 작업을 실행하지 않습니다.

parallel_for_each 알고리즘은 정방향 반복기와 임의 액세스 반복기에 대해 모두 사용할 수 있지만 임의 액세스 반복기를 사용할 경우 성능이 더 좋습니다.

예제

다음 예제에서는 parallel_for_each 알고리즘의 기본 구조를 보여 줍니다. 이 예제에서는 std::array 개체의 각 값을 병렬로 콘솔에 출력합니다.

// parallel-for-each-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an array of integer values.
   array<int, 5> values = { 1, 2, 3, 4, 5 };

   // Print each value in the array in parallel.
   parallel_for_each(values.begin(), values.end(), [](int value) {
      wstringstream ss;
      ss << value << L' ';
      wcout << ss.str();
   });
}

이 예제를 실행하면 다음과 같은 샘플 결과가 출력됩니다.

4 5 1 2 3

parallel_for_each 알고리즘은 각 항목에 대해 병렬로 작동하기 때문에 콘솔에 값이 출력되는 순서가 다릅니다.

parallel_for_each 알고리즘을 사용하는 전체 예제를 보려면 방법: parallel_for_each 루프 작성을 참조하십시오.

[맨 위로 이동]

parallel_invoke 알고리즘

Concurrency::parallel_invoke 알고리즘은 작업 집합을 병렬로 실행하며, 각 작업이 완료될 때까지 반환하지 않습니다. 이 알고리즘은 서로 관련이 없는 여러 작업을 동시에 실행하려는 경우에 유용합니다.

parallel_invoke 알고리즘은 일련의 작업 함수(람다 함수, 함수 개체 또는 함수 포인터)를 매개 변수로 사용합니다. parallel_invoke 알고리즘은 2개에서 10개 사이의 매개 변수를 사용하도록 오버로드됩니다. parallel_invoke에 전달하는 모든 함수에는 매개 변수가 사용되지 않아야 합니다.

다른 병렬 알고리즘과 마찬가지로 parallel_invoke는 작업을 특성 순서로 실행하지 않습니다. 작업 병렬 처리(동시성 런타임) 항목에서는 parallel_invoke 알고리즘을 작업 및 작업 그룹과 관계를 설정하는 방법에 대해 설명합니다.

예제

다음 예제에서는 parallel_invoke 알고리즘의 기본 구조를 보여 줍니다. 이 예제에서는 세 개의 로컬 변수에 대해 twice 함수를 동시에 호출하고 결과를 콘솔에 출력합니다.

// parallel-invoke-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <string>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Returns the result of adding a value to itself.
template <typename T>
T twice(const T& t) {
   return t + t;
}

int wmain()
{
   // Define several values.
   int n = 54;
   double d = 5.6;
   wstring s = L"Hello";

   // Call the twice function on each value concurrently.
   parallel_invoke(
      [&n] { n = twice(n); },
      [&d] { d = twice(d); },
      [&s] { s = twice(s); }
   );

   // Print the values to the console.
   wcout << n << L' ' << d << L' ' << s << endl;
}

이 예제의 결과는 다음과 같습니다.

108 11.2 HelloHello

parallel_invoke 알고리즘을 사용하는 전체 예제를 보려면 방법: parallel_invoke를 사용하여 병렬 정렬 루틴 작성방법: parallel_invoke를 사용하여 병렬 작업 실행을 참조하십시오.

[맨 위로 이동]

관련 항목

참조

parallel_for 함수

parallel_for_each 함수

parallel_invoke 함수