PPL (Parallel Patterns Library)

 

Per la documentazione più recente di Visual Studio 2017 RC, vedere Documentazione di Visual Studio 2017 RC.

La libreria PPL (Parallel Patterns Library) fornisce un modello di programmazione imperativa in grado di offrire scalabilità e semplicità per lo sviluppo di applicazioni simultanee. La libreria PPL si basa sulla pianificazione e sui componenti di gestione delle risorse del runtime di concorrenza. Innalza il livello di astrazione tra il codice dell'applicazione e il meccanismo di threading sottostante fornendo algoritmi generici indipendenti dai tipi e contenitori che agiscono sui dati in parallelo. La libreria PPL consente inoltre di sviluppare applicazioni adeguate fornendo alternative allo stato condiviso.

La libreria PPL offre le funzionalità seguenti:

  • Parallelismo fra attività: un meccanismo che funziona su ThreadPool Windows per eseguire diversi elementi di lavoro (attività) in parallelo

  • Gli algoritmi paralleli: algoritmi generici che funziona su Runtime di concorrenza per agire sulle raccolte di dati in parallelo

  • Contenitori e oggetti paralleli: tipi di contenitore generici che consentono l'accesso simultaneo sicuro ai relativi elementi

La libreria PPL fornisce un modello di programmazione simile alla libreria STL (Standard Template Library). L'esempio seguente illustra molte funzionalità della libreria PPL. Vengono calcolati diversi numeri di Fibonacci in serie e in parallelo. Entrambi i calcoli agiscono su un std:: Array oggetto. L'esempio inoltre visualizza nella console il tempo necessario per eseguire entrambi i calcoli.

La versione seriale Usa STL std:: for_each algoritmo per attraversare la matrice e archivia i risultati in un std:: Vector oggetto. La versione parallela esegue la stessa attività, ma utilizza la libreria PPL Concurrency:: parallel_for_each algoritmo e archivia i risultati in un concurrent_vector oggetto. La classe concurrent_vector consente a ogni iterazione del ciclo di aggiungere contemporaneamente gli elementi senza la necessità di sincronizzare l'accesso in scrittura al contenitore.

Poiché parallel_for_each agisce contemporaneamente, la versione parallela di questo esempio deve ordinare l'oggetto concurrent_vector per produrre gli stessi risultati della versione seriale.

Si noti che nell'esempio viene usato un metodo naïve per calcolare i numeri di Fibonacci; tuttavia, questo metodo illustra come il runtime di concorrenza possa migliorare le prestazioni di calcoli lunghi.

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

L'output di esempio seguente è relativo a un computer con quattro processori.

serial time: 9250 ms  
parallel time: 5726 ms  
 
fib(24): 46368  
fib(26): 121393  
fib(41): 165580141  
fib(42): 267914296  

Ogni iterazione del ciclo richiede una quantità di tempo diversa per il completamento. Le prestazioni di parallel_for_each dipendono dall'operazione che richiede più tempo. Pertanto, non c'era da aspettarsi un miglioramento lineare delle prestazioni tra la versione seriale e quella parallela di questo esempio.

TitoloDescrizione
Parallelismo delle attivitàDescrive il ruolo delle attività e dei gruppi di attività nella libreria PPL.
Algoritmi paralleliDescrive come usare gli algoritmi paralleli come parallel_for e parallel_for_each.
Contenitori e oggetti paralleliDescrive i vari contenitori e oggetti paralleli forniti dalla libreria PPL.
AnnullamentoIllustra come annullare il lavoro eseguito da un algoritmo parallelo.
Runtime di concorrenzaDescrive il runtime di concorrenza che semplifica la programmazione parallela e contiene i collegamenti ad argomenti correlati.
Mostra: