Partager via


Bibliothèque de modèles parallèles

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

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 qui agissent sur des 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

Exemple

La bibliothèque PPL fournit un modèle de programmation qui ressemble à la Bibliothèque de modèles standard (STL, Standard Template Library). L'exemple suivant illustre de nombreuses fonctionnalités des PPL : Il calcule plusieurs nombres de Fibonacci de façon séquentielle et en parallèle. Les deux calculs agissent sur un objet std::array. L'exemple imprime également dans la console le temps requis pour effectuer les deux calculs.

La version sérialisée utilise l'algorithme std::for_each STL 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 Concurrency::parallel_for_each PPL et stocke les résultats dans un objet Concurrency::concurrent_vector. La classe concurrent_vector permet à chaque itération de boucle d'ajouter simultanément des éléments sans qu'il soit nécessaire de synchroniser l'accès en écriture au conteneur.

Étant donné que 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 sérialisée.

Notez que l'exemple utilise une méthode naïve pour calculer les nombres de Fibonacci ; cependant, cette méthode illustre comment le runtime d'accès concurrentiel peut améliorer les performances des 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 (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;
   });
}

L'exemple de sortie suivant concerne un ordinateur avec 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 requiert une durée différente. Les performances de parallel_for_each sont limitées par l'opération qui se termine en dernier. Par conséquent, vous ne devez pas vous attendre à constater des améliorations de performances linéaires entre les versions série et parallèle de cet 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 conteneurs et objets parallèles fournis par la bibliothèque PPL.

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

Explique comment annuler le travail exécuté 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 des rubriques connexes.