Partager via


Comment : utiliser la classe combinable pour améliorer les performances

Cet exemple montre comment utiliser la concurrency::combinable classe pour calculer la somme des nombres dans une std::array objet sont prime.La classe combinable améliore les performances en éliminant l'état partagé.

ConseilConseil

Dans certains cas, parallèle au plan (concurrency::parallel_transform) et de réduire (simultanéité:: parallel_reduce) peut améliorer les performances via combinable.Pour obtenir un exemple qu'utilisations mappent et réduisent les opérations afin de produire les mêmes résultats que cet exemple, consultez Algorithmes parallèles.

Exemple

L'exemple suivant utilise la fonction std::accumulate pour calculer la somme des éléments premiers dans un tableau.Dans cet exemple, a est un objet array et la fonction is_prime détermine si sa valeur d'entrée est première.

prime_sum = accumulate(begin(a), end(a), 0, [&](int acc, int i) {
   return acc + (is_prime(i) ? i : 0);
});

L'exemple suivant illustre une méthode naïve pour paralléliser l'exemple précédent.Cet exemple utilise la concurrency::parallel_for_each algorithme pour traiter le tableau en parallèle et une concurrency::critical_section objet utilisé pour synchroniser l'accès à la prime_sum variable.Cet exemple ne monte pas en charge car chaque thread doit attendre que la ressource partagée soit disponible.

critical_section cs;
prime_sum = 0;
parallel_for_each(begin(a), end(a), [&](int i) {
   cs.lock();
   prime_sum += (is_prime(i) ? i : 0);
   cs.unlock();
});

L'exemple suivant utilise un objet combinable pour améliorer les performances de l'exemple précédent.Cet exemple élimine le besoin en objets de synchronisation ; il monte en charge car l'objet combinable permet à chaque thread d'effectuer sa tâche indépendamment.

Un objet combinable s'utilise en général en deux étapes.En premier lieu, produisez une série de calculs affinés en effectuant un travail en parallèle.Ensuite, combinez (ou réduisez) les calculs en un résultat final.Cet exemple utilise la concurrency::combinable::local méthode pour obtenir une référence à la somme locale.Il utilise ensuite la concurrency::combinable::combine méthode et un std::plus objet pour combiner les calculs locaux dans le résultat final.

combinable<int> sum;
parallel_for_each(begin(a), end(a), [&](int i) {
   sum.local() += (is_prime(i) ? i : 0);
});
prime_sum = sum.combine(plus<int>());

L'exemple complet suivant calcule la somme des nombres premiers à la fois de façon séquentielle et en parallèle.L'exemple imprime sur la console le temps requis pour effectuer les deux calculs.

// parallel-sum-of-primes.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <array>
#include <numeric>
#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;
}

// Determines whether the input value is prime.
bool is_prime(int n)
{
   if (n < 2)
      return false;
   for (int i = 2; i < n; ++i)
   {
      if ((n % i) == 0)
         return false;
   }
   return true;
}

int wmain()
{   
   // Create an array object that contains 200000 integers.
   array<int, 200000> a;

   // Initialize the array such that a[i] == i.
   iota(begin(a), end(a), 0);

   int prime_sum;
   __int64 elapsed;

   // Compute the sum of the numbers in the array that are prime.
   elapsed = time_call([&] {
      prime_sum = accumulate(begin(a), end(a), 0, [&](int acc, int i) {
         return acc + (is_prime(i) ? i : 0);
      });
   });   
   wcout << prime_sum << endl;   
   wcout << L"serial time: " << elapsed << L" ms" << endl << endl;

   // Now perform the same task in parallel.
   elapsed = time_call([&] {
      combinable<int> sum;
      parallel_for_each(begin(a), end(a), [&](int i) {
         sum.local() += (is_prime(i) ? i : 0);
      });
      prime_sum = sum.combine(plus<int>());
   });
   wcout << prime_sum << endl;
   wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;
}

L'exemple de sortie suivant concerne un ordinateur avec quatre processeurs.

1709600813
serial time: 6178 ms

1709600813
parallel time: 1638 ms

Compilation du code

Pour compiler le code, copiez-le et collez-le dans un projet Visual Studio ou le coller dans un fichier nommé parallèle somme de primes.cpp , puis exécutez la commande suivante dans une fenêtre d'invite de commande Visual Studio.

cl.exe /EHsc parallel-sum-of-primes.cpp

Programmation fiable

Pour obtenir un exemple qui utilise de mapper et de réduire les opérations afin de produire les mêmes résultats, voir Algorithmes parallèles.

Voir aussi

Référence

combinable, classe

critical_section, classe

Concepts

Conteneurs et objets parallèles