Bibliothèque de modèles parallèles

 

Pour obtenir la dernière documentation sur Visual Studio 2017, consultez Documentation Visual Studio 2017.

La Bibliothèque de modèles parallèles (PPL) fournit un modèle de programmation essentiel qui favorise l’évolutivité et la facilité d’utilisation dans le cadre du développement d’applications simultanées. La bibliothèque PPL repose sur les composants de planification et de gestion des ressources du runtime d'accès concurrentiel. Elle élève le niveau d'abstraction entre le code de votre application et le mécanisme de threading sous-jacent en fournissant des algorithmes génériques de type sécurisé et des conteneurs qui agissent sur les données en parallèle. La bibliothèque PPL vous permet également de développer des applications qui évoluent en fournissant des alternatives à l'état partagé.

La bibliothèque PPL offre les fonctionnalités suivantes :

  • Parallélisme des tâches: un mécanisme qui fonctionne sur le pool de threads Windows d’exécuter plusieurs éléments de travail (tâches) en parallèle

  • Algorithmes parallèles: algorithmes génériques qui fonctionne sur le Runtime d’accès concurrentiel pour agir sur les collections de données en parallèle

  • Conteneurs et objets parallèles: types de conteneurs génériques qui fournissent un accès simultané sécurisé à leurs éléments

La bibliothèque PPL fournit un modèle de programmation qui s'apparente à la bibliothèque STL (Standard Template Library). L'exemple suivant illustre de nombreuses fonctionnalités de la bibliothèque PPL. Il calcule plusieurs nombres de Fibonacci en série et en parallèle. Les deux calculs agissent sur un std::array objet. L'exemple affiche également sur la console le temps requis pour effectuer les deux calculs.

La version sérialisée utilise la bibliothèque STL std::for_each algorithme pour parcourir le tableau et stocke les résultats dans un std::vector objet. La version parallèle effectue la même tâche, mais utilise la bibliothèque PPL concurrency::parallel_for_each algorithme et stocke les résultats dans un concurrency::concurrent_vector objet. La classe concurrent_vector permet à chaque itération de boucle d'ajouter simultanément des éléments sans avoir à synchroniser l'accès en écriture au conteneur.

Comme parallel_for_each agit simultanément, la version parallèle de cet exemple doit trier l'objet concurrent_vector pour produire les mêmes résultats que la version en série.

Notez que l'exemple utilise une méthode naïve pour calculer les nombres de Fibonacci ; toutefois, cette méthode illustre comment le runtime d'accès concurrentiel peut améliorer les performances de longs calculs.

// 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'exemple de sortie suivant concerne un ordinateur équipé de quatre processeurs.

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

Chaque itération de la boucle nécessite une durée de temps différente pour s'exécuter. Les performances de parallel_for_each sont limitées par l'opération qui se termine en dernier. Par conséquent, vous ne devriez pas escompter des améliorations de performances linéaires entre les versions série et parallèle de l'exemple.

TitreDescription
Parallélisme des tâchesDécrit le rôle des tâches et des groupes de tâches dans la bibliothèque PPL.
Algorithmes parallèlesDécrit comment utiliser des algorithmes parallèles tels que parallel_for et parallel_for_each.
Conteneurs et objets parallèlesDécrit les différents objets et conteneurs parallèles fournis par la bibliothèque PPL.
AnnulationExplique comment annuler le travail effectué par un algorithme parallèle.
Runtime d’accès concurrentielDécrit le runtime d'accès concurrentiel, qui simplifie la programmation parallèle, et contient des liens vers les rubriques connexes.
Afficher: