PPL(병렬 패턴 라이브러리)

PPL(병렬 패턴 라이브러리)은 여러 애플리케이션을 동시에 개발할 수 있도록 편의성과 확장성을 높이는 명령적 프로그래밍 모델을 제공합니다. PPL은 동시성 런타임의 예약 및 리소스 관리 구성 요소를 기반으로 빌드됩니다. 또한 데이터에 대해 병렬로 작동하는 형식이 안전한 일반 알고리즘과 컨테이너를 제공함으로써 애플리케이션 코드와 기본 스레딩 메커니즘 간의 추상화 수준을 높입니다. 또한 PPL을 사용하면 공유 상태 대신 사용할 수 있는 옵션을 제공하여 확장 가능한 애플리케이션을 개발할 수 있습니다.

PPL은 다음과 같은 기능을 제공합니다.

  • 작업 병렬 처리: 여러 작업 항목(작업)을 병렬로 실행하는 Windows ThreadPool 위에서 작동하는 메커니즘

  • 병렬 알고리즘: 동시성 런타임에서 작동하여 병렬로 데이터 컬렉션에 대해 작동하는 제네릭 알고리즘

  • 병렬 컨테이너 및 개체: 요소에 대한 안전한 동시 액세스를 제공하는 제네릭 컨테이너 형식

예시

PPL은 C++ 표준 라이브러리와 유사한 프로그래밍 모델을 제공합니다. 다음 예제에서는 PPL의 여러 기능을 보여 줍니다. 이 예제에서는 여러 피보나치 수를 직렬 및 병렬로 계산합니다. 두 계산 모두 std::array 개체에서 작동합니다. 또한 이 예제에서는 두 계산을 모두 수행하는 데 필요한 시간을 콘솔에 출력합니다.

직렬 버전은 C++ 표준 라이브러리 std::for_each 알고리즘을 사용하여 배열을 트래버스하고 결과를 std::vector 개체에 저장합니다. 병렬 버전은 동일한 작업을 수행하지만 PPL 동시성::p arallel_for_each 알고리즘을 사용하고 결과를 동시성::concurrent_vector 개체에 저장합니다. concurrent_vector 클래스를 사용하면 컨테이너에 대한 쓰기 권한을 동기화하지 않아도 각 루프 반복이 요소를 동시에 추가할 수 있습니다.

parallel_for_each는 동시에 동작하므로 이 예제의 병렬 버전은 직렬 버전과 같은 결과를 생성하기 위해 concurrent_vector 개체를 정렬해야 합니다.

예제에서는 간단한 메서드를 사용하여 피보나치 수를 계산합니다. 그러나 이 방법은 동시성 런타임을 통해 긴 컴퓨팅의 성능을 개선하는 방법을 보여 줍니다.

// parallel-fibonacci.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <concurrent_vector.h>
#include <array>
#include <vector>
#include <tuple>
#include <algorithm>
#include <iostream>

using namespace concurrency;
using namespace std;

// Calls the provided work function and returns the number of milliseconds 
// that it takes to call that function.
template <class Function>
__int64 time_call(Function&& f)
{
   __int64 begin = GetTickCount();
   f();
   return GetTickCount() - begin;
}

// Computes the nth Fibonacci number.
int fibonacci(int n)
{
   if(n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   __int64 elapsed;

   // An array of Fibonacci numbers to compute.
   array<int, 4> a = { 24, 26, 41, 42 };

   // The results of the serial computation.
   vector<tuple<int,int>> results1;

   // The results of the parallel computation.
   concurrent_vector<tuple<int,int>> results2;

   // Use the for_each algorithm to compute the results serially.
   elapsed = time_call([&] 
   {
      for_each (begin(a), end(a), [&](int n) {
         results1.push_back(make_tuple(n, fibonacci(n)));
      });
   });   
   wcout << L"serial time: " << elapsed << L" ms" << endl;
   
   // Use the parallel_for_each algorithm to perform the same task.
   elapsed = time_call([&] 
   {
      parallel_for_each (begin(a), end(a), [&](int n) {
         results2.push_back(make_tuple(n, fibonacci(n)));
      });

      // Because parallel_for_each acts concurrently, the results do not 
      // have a pre-determined order. Sort the concurrent_vector object
      // so that the results match the serial version.
      sort(begin(results2), end(results2));
   });   
   wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;

   // Print the results.
   for_each (begin(results2), end(results2), [](tuple<int,int>& pair) {
      wcout << L"fib(" << get<0>(pair) << L"): " << get<1>(pair) << endl;
   });
}

프로세서가 4개인 컴퓨터의 샘플 출력은 다음과 같습니다.

serial time: 9250 ms
parallel time: 5726 ms

fib(24): 46368
fib(26): 121393
fib(41): 165580141
fib(42): 267914296

루프의 각 반복을 완료하는 데 필요한 시간은 서로 다릅니다. parallel_for_each의 성능은 마지막으로 완료되는 작업에 의해 제한됩니다. 따라서 이 예제의 직렬 버전과 병렬 버전에서 성능이 일정하게 개선된다고 기대해서는 안 됩니다.

제목 설명
작업 병렬 처리 PPL의 작업 및 작업 그룹 역할에 대해 설명합니다.
병렬 알고리즘 parallel_forparallel_for_each와 같은 병렬 알고리즘을 사용하는 방법에 대해 설명합니다.
병렬 컨테이너 및 개체 PPL에서 제공하는 여러 병렬 컨테이너 및 개체에 대해 설명합니다.
PPL에서의 취소 병렬 알고리즘에서 수행 중인 작업을 취소하는 방법을 설명합니다.
동시성 런타임 병렬 프로그래밍을 간소화하는 동시성 런타임에 대해 설명하고 관련 항목의 링크를 제공합니다.