Dela via


PPL (Parallel Patterns Library)

PPL (Parallel Patterns Library) tillhandahåller en imperativ programmeringsmodell som främjar skalbarhet och användarvänlighet för utveckling av samtidiga program. PPL bygger på komponenterna för schemaläggning och resurshantering i Concurrency Runtime. Den höjer abstraktionsnivån mellan programkoden och den underliggande trådmekanismen genom att tillhandahålla generiska, typsäkra algoritmer och containrar som fungerar parallellt med data. Med PPL kan du också utveckla program som skalar genom att tillhandahålla alternativ till delat tillstånd.

PPL innehåller följande funktioner:

  • Uppgiftsparallellitet: en mekanism som fungerar ovanpå Windows ThreadPool för att köra flera arbetsobjekt (uppgifter) parallellt

  • Parallella algoritmer: generiska algoritmer som fungerar ovanpå Concurrency Runtime för att agera på samlingar av data parallellt

  • Parallella containrar och objekt: generiska containertyper som ger säker samtidig åtkomst till sina element

Exempel

PPL tillhandahåller en programmeringsmodell som liknar C++-standardbiblioteket. I följande exempel visas många funktioner i PPL. Den beräknar flera Fibonacci-tal seriellt och parallellt. Båda beräkningarna fungerar på ett std::array-objekt . Exemplet skriver också ut till konsolen den tid som krävs för att utföra båda beräkningarna.

Serieversionen använder C++ Standard Library std::for_each-algoritmen för att bläddra i matrisen och lagrar resultatet i ett std::vector-objekt . Den parallella versionen utför samma uppgift, men använder concurrency::parallel_for_each-algoritmen och lagrar resultatet i ett concurrency::concurrent_vector-objekt. Klassen concurrent_vector gör det möjligt för varje loop-iteration att samtidigt lägga till element utan att behöva synkronisera skrivåtkomst till containern.

Eftersom parallel_for_each fungerar samtidigt måste den parallella versionen av det här exemplet sortera concurrent_vector objektet för att ge samma resultat som serieversionen.

Observera att exemplet använder en naiv metod för att beräkna Fibonacci-talen. Den här metoden illustrerar dock hur Concurrency Runtime kan förbättra prestanda för långa beräkningar.

// 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;
   });
}

Följande exempelutdata är för en dator som har fyra processorer.

serial time: 9250 ms
parallel time: 5726 ms

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

Varje iteration av loopen kräver olika lång tid för att slutföra. Prestandan för parallel_for_each begränsas av den operation som slutförs sist. Därför bör du inte förvänta dig linjära prestandaförbättringar mellan de seriella och parallella versionerna av det här exemplet.

Titel Beskrivning
Uppgiftsparallellitet Beskriver rollen för aktiviteter och aktivitetsgrupper i PPL.
Parallella algoritmer Beskriver hur du använder parallella algoritmer som parallel_for och parallel_for_each.
parallella containrar och objekt Beskriver de olika parallella containrar och objekt som tillhandahålls av PPL.
Annullering i PPL Förklarar hur du avbryter det arbete som utförs av en parallell algoritm.
Samtidighetskörning Beskriver Concurrency Runtime, som förenklar parallell programmering och innehåller länkar till relaterade ämnen.