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

PPL(병렬 패턴 라이브러리)은 동시 응용 프로그램 개발을 위해 사용 편리성 및 확장성을 향상시키는 명령형 프로그래밍 모델을 제공합니다. PPL은 동시성 런타임의 일정 예약 및 리소스 관리 구성 요소를 바탕으로 합니다. 이는 데이터에 대해 형식이 안전한 제네릭 알고리즘 및 컨테이너를 동시에 제공하여 응용 프로그램 코드와 내부 스레딩 메커니즘 간의 추상화 수준을 높입니다. 또한 PPL을 사용하면 공유 상태에 대한 대안을 제공하여 크기를 조정하는 응용 프로그램을 개발할 수 있습니다.

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

  • 작업 병렬 처리: 여러 작업 항목(작업)을 동시에 실행하는 메커니즘입니다.

  • 병렬 알고리즘: 데이터 컬렉션에 대해 동시에 수행되는 제네릭 알고리즘입니다.

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

예제

PPL은 STL(표준 템플릿 라이브러리)과 유사한 프로그래밍 모델을 제공합니다. 다음 예제에서는 PPL의 여러 기능을 보여 주고 몇 가지 피보나치(Fibonacci) 수를 연속적으로 또는 병렬로 계산합니다. 두 계산은 모두 std::array 개체에 대해 작용합니다. 이 예제에서는 두 계산을 모두 수행하는 데 필요한 시간을 콘솔에 출력합니다.

연속 버전은 STL std::for_each 알고리즘을 사용하여 배열을 트래버스하고 결과를 std::vector 개체에 저장합니다. 병렬 버전은 동일한 작업을 수행하지만 PPL Concurrency::parallel_for_each 알고리즘을 사용하고 결과를 Concurrency::concurrent_vector 개체에 저장합니다. concurrent_vector 클래스를 사용하면 컨테이너에 대한 쓰기 권한을 동기화하지 않고도 루프 반복에서 동시에 요소를 추가할 수 있습니다.

parallel_for_each는 동시에 동작하므로 이 예제의 병렬 버전은 연속 버전과 같은 결과가 나타나도록 concurrent_vector 개체를 정렬해야 합니다.

이 예제에서는 단순한 메서드를 사용하여 피보나치(Fibonacci) 수를 계산하지만 이 메서드는 동시성 런타임에서 긴 계산의 성능을 향상시킬 수 있는 방법을 보여 줍니다.

// 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 (a.begin(), a.end(), [&](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 (a.begin(), a.end(), [&](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(results2.begin(), results2.end());
   });   
   wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;

   // Print the results.
   for_each (results2.begin(), results2.end(), [](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에서의 취소

병렬 알고리즘에서 수행 중인 작업을 취소하는 방법에 대해 설명합니다.

동시성 런타임

병렬 프로그래밍을 단순화하는 동시성 런타임에 대해 설명하고 관련 항목에 대한 링크를 포함합니다.