Partager via


Bibliothèque de modèles parallèles

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 task, classe et les types connexes définis dans ppltasks.h sont transférables entre plateformes. Les conteneurs et les algorithmes parallèles ne sont pas transférables.

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

  • Parallélisme des tâches : mécanisme permettant d'exécuter plusieurs éléments de travail (tâches) en parallèle

  • Algorithmes parallèles : algorithmes génériques agissant sur des collections de données en parallèle

  • Objets et conteneurs parallèles : types de conteneurs génériques fournissant un accès simultané sécurisé à leurs éléments

Exemple

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 objet std::array. L'exemple affiche également sur la console le temps requis pour effectuer les deux calculs.

La version sérialisée utilise l'algorithme STL std::for_each pour parcourir le tableau et stocke les résultats dans un objet std::vector. La version parallèle effectue la même tâche, mais utilise l'algorithme PPL concurrency::parallel_for_each et stocke les résultats dans un objetconcurrency::concurrent_vector. 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.

  

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.

Rubriques connexes

Titre

Description

Parallélisme des tâches (runtime d'accès concurrentiel)

Décrit le rôle des tâches et des groupes de tâches dans la bibliothèque PPL.

Algorithmes parallèles

Décrit comment utiliser des algorithmes parallèles tels que parallel_for et parallel_for_each.

Conteneurs et objets parallèles

Décrit les différents objets et conteneurs parallèles fournis par la bibliothèque PPL.

Annulation dans la bibliothèque de modèles parallèles

Explique comment annuler le travail effectué par un algorithme parallèle.

Concurrency Runtime

Décrit le runtime d'accès concurrentiel, qui simplifie la programmation parallèle, et contient des liens vers les rubriques connexes.