Condividi tramite


Procedura: Usare la classe combinable per migliorare le prestazioni

In questo esempio viene illustrato come usare la classe concurrency::combinable per calcolare la somma dei numeri in un oggetto std::array prime. La combinable classe migliora le prestazioni eliminando lo stato condiviso.

Suggerimento

In alcuni casi, la mappa parallela (concurrency::p arallel_transform) e la riduzione (concorrenza:: parallel_reduce) possono offrire miglioramenti delle prestazioni rispetto combinablea . Per un esempio che usa operazioni di mapping e riduzione per produrre gli stessi risultati di questo esempio, vedere Algoritmi paralleli.

Esempio : accumulare

Nell'esempio seguente viene usata la funzione std::accumulate per calcolare la somma degli elementi in una matrice primi. In questo esempio, a è un array oggetto e la is_prime funzione determina se il valore di input è primo.

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

Esempio - parallel_for_each

L'esempio seguente illustra un modo ingenuo per parallelizzare l'esempio precedente. Questo esempio usa l'algoritmo concurrency::p arallel_for_each per elaborare la matrice in parallelo e un oggetto concurrency::critical_section per sincronizzare l'accesso alla prime_sum variabile. Questo esempio non viene ridimensionato perché ogni thread deve attendere che la risorsa condivisa diventi disponibile.

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

Esempio : combinabile

Nell'esempio seguente viene utilizzato un combinable oggetto per migliorare le prestazioni dell'esempio precedente. In questo esempio viene eliminata la necessità di oggetti di sincronizzazione; viene ridimensionato perché l'oggetto consente a ogni thread di eseguire l'attività combinable in modo indipendente.

Un combinable oggetto viene in genere usato in due passaggi. Prima di tutto, produrre una serie di calcoli con granularità fine eseguendo il lavoro in parallelo. Combinare (o ridurre) i calcoli in un risultato finale. In questo esempio viene usato il metodo concurrency::combinable::local per ottenere un riferimento alla somma locale. Usa quindi il metodo concurrency::combinable::combine e un oggetto std::p lus per combinare i calcoli locali nel risultato finale.

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

Esempio: seriale e parallelo

Nell'esempio completo seguente viene calcolata la somma dei numeri primi sia serialmente che in parallelo. Nell'esempio viene stampato 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.
   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'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 incollarlo in un progetto di Visual Studio oppure incollarlo in un file denominato parallel-sum-of-primes.cpp e quindi eseguire il comando seguente in una finestra del prompt dei comandi di Visual Studio.

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

Programmazione efficiente

Per un esempio che usa operazioni di mapping e riduzione per produrre gli stessi risultati, vedere Algoritmi paralleli.

Vedi anche

Contenitori e oggetti paralleli
Classe combinable
Classe critical_section