Procedura: utilizzare la classe combinable per migliorare le prestazioni

In questo esempio viene illustrato come utilizzare la classe Concurrency::combinable per calcolare la somma dei numeri primi di un oggetto std::array. La classe combinable migliora le prestazioni eliminando lo stato condiviso.

Esempio

Nell'esempio seguente viene utilizzata la funzione std::accumulate per calcolare la somma dei numeri primi di una matrice. In questo esempio a è un oggetto array e la funzione is_prime determina se il relativo valore di input è un numero primo.

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

Nell'esempio seguente viene illustrata una modalità naïve per parallelizzare l'esempio precedente. In questo esempio viene utilizzato l'algoritmo Concurrency::parallel_for_each per elaborare la matrice in parallelo e un oggetto Concurrency::critical_section per sincronizzare l'accesso alla variabile prime_sum. Questo esempio non è adatto perché ogni thread deve attendere che la risorsa condivisa diventi disponibile.

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();
});

Nell'esempio seguente viene utilizzato un oggetto combinable per migliorare le prestazioni dell'esempio precedente. Questo esempio elimina la necessità di oggetti di sincronizzazione ed è adatto poiché l'oggetto combinable consente a ogni thread di eseguire la relativa attività indipendentemente.

Un oggetto combinable viene utilizzato in genere in due passaggi. Nel primo produrre una serie di calcoli accurati eseguendo il lavoro in parallelo. Nel secondo combinare o ridurre i calcoli in un risultato finale. In questo esempio viene utilizzato il metodo Concurrency::combinable::local per ottenere un riferimento alla somma locale. Vene quindi utilizzato il metodo Concurrency::combinable::combine e un oggetto std::plus per combinare i calcoli locali nel risultato finale.

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>());

Nell'esempio completo seguente viene calcolata la somma dei numeri primi sia in serie che in parallelo. L'esempio visualizza nella console il tempo necessario per eseguire entrambi i calcoli.

// 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'output di esempio seguente è relativo a un computer con quattro processori.

1709600813
serial time: 6178 ms

1709600813
parallel time: 1638 ms

Compilazione del codice

Per compilare il codice, copiarlo e quindi incollarlo in un progetto di Visual Studio o incollarlo in un file denominato parallel-sum-of-primes.cpp, quindi eseguire il comando seguente in una finestra del prompt dei comandi di Visual Studio.

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

Vedere anche

Riferimenti

Classe combinable

Classe critical_section

Concetti

Contenitori e oggetti paralleli