Partager via


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

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

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(a.begin(), a.end(), 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 l'algorithme Concurrency::parallel_for_each pour traiter le tableau en parallèle et un objet Concurrency::critical_section pour synchroniser l'accès à la variable prime_sum. 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(a.begin(), a.end(), [&](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 méthode Concurrency::combinable::local pour obtenir une référence à la somme locale. Il utilise ensuite la méthode Concurrency::combinable::combine et un objet std::plus fusionner les calculs locaux dans le résultat final.

combinable<int> sum;
parallel_for_each(a.begin(), a.end(), [&](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.
   int n = 0;
   generate(a.begin(), a.end(), [&] {
      return n++;
   });

   int prime_sum;
   __int64 elapsed;

   // Compute the sum of the numbers in the array that are prime.
   elapsed = time_call([&] {
      prime_sum = accumulate(a.begin(), a.end(), 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(a.begin(), a.end(), [&](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 puis collez-le dans un projet Visual Studio, ou collez-le dans un fichier nommé parallel-sum-of-primes.cpp puis exécutez la commande suivante dans une fenêtre d'invite de commandes Visual Studio.

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

Voir aussi

Référence

combinable, classe

critical_section, classe

Concepts

Conteneurs et objets parallèles