Lire en anglais

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 dans un objet std ::array qui sont premiers. La combinable classe améliore les performances en éliminant l’état partagé.

Conseil

Dans certains cas, la carte parallèle (concurrency ::p arallel_transform) et la réduction (concurrency :: parallel_reduce) peuvent apporter des améliorations aux performances.combinable Pour obtenir un exemple qui utilise des opérations de mappage et de réduction pour produire les mêmes résultats que cet exemple, consultez Algorithmes parallèles.

Exemple : accumuler

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

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

Exemple : parallel_for_each

L’exemple suivant montre un moyen naïf de paralléliser l’exemple précédent. Cet exemple utilise l’algorithme concurrency ::p arallel_for_each pour traiter le tableau en parallèle et un objet concurrency ::critical_section pour synchroniser l’accès à la prime_sum variable. Cet exemple n’est pas mis à l’échelle, car chaque thread doit attendre que la ressource partagée devienne 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();
});

Exemple : combinable

L’exemple suivant utilise un combinable objet pour améliorer les performances de l’exemple précédent. Cet exemple élimine la nécessité d’objets de synchronisation ; il est mis à l’échelle, car l’objet combinable permet à chaque thread d’effectuer sa tâche indépendamment.

Un combinable objet est généralement utilisé en deux étapes. Tout d’abord, produisez une série de calculs affinés en effectuant des travaux en parallèle. Ensuite, combinez (ou réduisez) les calculs dans 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 ::p lus 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>());

Exemple : série et parallèle

L’exemple complet suivant calcule la somme des nombres premiers en série et en parallèle. L’exemple imprime sur la console l’heure nécessaire 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 équipé de quatre processeurs.

1709600813
serial time: 6178 ms

1709600813
parallel time: 1638 ms

Compilation du code

Pour compiler le code, copiez-le, 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

Programmation fiable

Pour obtenir un exemple qui utilise des opérations de mappage et de réduction pour produire les mêmes résultats, consultez Algorithmes parallèles.

Voir aussi

Conteneurs et objets parallèles
combinable, classe
critical_section, classe