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 combinable
a . 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