Procedura: eseguire operazioni di mapping e riduzione in parallelo
Questo esempio illustra come usare gli algoritmi concurrency::p arallel_transform e concurrency::p arallel_reduce e la classe concurrency::concurrent_unordered_map per contare le occorrenze di parole nei file.
Un'operazione di mapping applica una funzione a ogni valore in una sequenza. Un'operazione di riduzione combina gli elementi di una sequenza in un unico valore. È possibile usare le funzioni std::transform e std::accumulate della libreria standard C++ per eseguire operazioni di mapping e riduzione. Tuttavia, per migliorare le prestazioni per molti problemi, è possibile usare l'algoritmo parallel_transform
per eseguire l'operazione di mapping in parallelo e l'algoritmo parallel_reduce
per eseguire l'operazione di riduzione in parallelo. In alcuni casi, è possibile usare concurrent_unordered_map
per eseguire le operazioni di mapping e di riduzione in un'unica operazione.
Esempio
Nell'esempio seguente si contano le occorrenze delle parole nei file. Usa std::vector per rappresentare il contenuto di due file. L'operazione di mapping calcola le occorrenze di ogni parola in ogni vettore. L'operazione di riduzione accumula i conteggi delle parole per entrambi i vettori.
// parallel-map-reduce.cpp
// compile with: /EHsc
#include <ppl.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <numeric>
#include <unordered_map>
#include <windows.h>
using namespace concurrency;
using namespace std;
class MapFunc
{
public:
unordered_map<wstring, size_t> operator()(vector<wstring>& elements) const
{
unordered_map<wstring, size_t> m;
for_each(begin(elements), end(elements), [&m](const wstring& elem)
{
m[elem]++;
});
return m;
}
};
struct ReduceFunc : binary_function<unordered_map<wstring, size_t>,
unordered_map<wstring, size_t>, unordered_map<wstring, size_t>>
{
unordered_map<wstring, size_t> operator() (
const unordered_map<wstring, size_t>& x,
const unordered_map<wstring, size_t>& y) const
{
unordered_map<wstring, size_t> ret(x);
for_each(begin(y), end(y), [&ret](const pair<wstring, size_t>& pr) {
auto key = pr.first;
auto val = pr.second;
ret[key] += val;
});
return ret;
}
};
int wmain()
{
// File 1
vector<wstring> v1 {
L"word1", // 1
L"word1", // 1
L"word2",
L"word3",
L"word4"
};
// File 2
vector<wstring> v2 {
L"word5",
L"word6",
L"word7",
L"word8",
L"word1" // 3
};
vector<vector<wstring>> v { v1, v2 };
vector<unordered_map<wstring, size_t>> map(v.size());
// The Map operation
parallel_transform(begin(v), end(v), begin(map), MapFunc());
// The Reduce operation
unordered_map<wstring, size_t> result = parallel_reduce(
begin(map), end(map), unordered_map<wstring, size_t>(), ReduceFunc());
wcout << L"\"word1\" occurs " << result.at(L"word1") << L" times. " << endl;
}
/* Output:
"word1" occurs 3 times.
*/
Compilazione del codice
Per compilare il codice, copiarlo e incollarlo in un progetto di Visual Studio oppure incollarlo in un file denominato parallel-map-reduce.cpp
e quindi eseguire il comando seguente in una finestra del prompt dei comandi di Visual Studio.
cl.exe /EHsc parallel-map-reduce.cpp
Programmazione efficiente
In questo esempio, è possibile usare la classe concurrent_unordered_map
, definita in concurrent_unordered_map.h, per esegue le operazioni di mapping e di riduzione in un'unica operazione.
// File 1
vector<wstring> v1 {
L"word1", // 1
L"word1", // 2
L"word2",
L"word3",
L"word4",
};
// File 2
vector<wstring> v2 {
L"word5",
L"word6",
L"word7",
L"word8",
L"word1", // 3
};
vector<vector<wstring>> v { v1, v2 };
concurrent_unordered_map<wstring, size_t> result;
for_each(begin(v), end(v), [&result](const vector<wstring>& words) {
parallel_for_each(begin(words), end(words), [&result](const wstring& word) {
InterlockedIncrement(&result[word]);
});
});
wcout << L"\"word1\" occurs " << result.at(L"word1") << L" times. " << endl;
/* Output:
"word1" occurs 3 times.
*/
In genere, si parallelizza solo il ciclo esterno o quello interno. Parallelizzare il ciclo interno se si dispone di un numero relativamente basso di file e ogni file contiene numerose parole. Parallelizzare il ciclo esterno se si dispone di un numero relativamente alto di file e ogni file contiene poche parole.
Vedi anche
Algoritmi paralleli
Funzione parallel_transform
Funzione parallel_reduce
Classe concurrent_unordered_map