Procedura: eseguire operazioni di mapping e riduzione in parallelo

In questo esempio viene illustrato come utilizzare il concurrency::parallel_transform e concurrency::parallel_reduce gli algoritmi e le concurrency::concurrent_unordered_map classe per contare le occorrenze delle parole nei file.

A mappa operazione si applica una funzione per ogni valore di una sequenza.A ridurre operazione combina gli elementi di una sequenza in un unico valore.È possibile utilizzare il modello di libreria STL (Standard) std::transformstd::accumulate classi per eseguire il mapping e ridurre le operazioni.Tuttavia, per migliorare le prestazioni per molti problemi, è possibile utilizzare la parallel_transform algoritmo per eseguire l'operazione di mappa in parallelo e la parallel_reduce algoritmo per eseguire l'operazione di riduzione in parallelo.In alcuni casi, è possibile utilizzare concurrent_unordered_map per eseguire il mapping e la riduzione in un'unica operazione.

Esempio

Nell'esempio seguente viene contato le occorrenze delle parole nei file.Utilizza std:: Vector per rappresentare il contenuto di due file.L'operazione di mapping consente di calcolare le occorrenze di ogni parola in ciascun vettore.L'operazione di riduzione si accumula il conteggio delle parole in 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;
    v1.push_back(L"word1"); //1 
    v1.push_back(L"word1"); //2 
    v1.push_back(L"word2"); 
    v1.push_back(L"word3"); 
    v1.push_back(L"word4"); 

    // File 2 
    vector<wstring> v2; 
    v2.push_back(L"word5"); 
    v2.push_back(L"word6"); 
    v2.push_back(L"word7"); 
    v2.push_back(L"word8"); 
    v2.push_back(L"word1"); //3 

    vector<vector<wstring>> v;
    v.push_back(v1);
    v.push_back(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 o incollarlo in un file denominato parallelo-mappa-reduce.cpp e quindi eseguire il comando riportato di seguito in una finestra del prompt dei comandi di Visual Studio.

cl.exe /EHsc parallel-map-reduce.cpp

Programmazione efficiente

In questo esempio, è possibile utilizzare la concurrent_unordered_map classe, ovvero che è definito in concurrent_unordered_map.h—to di eseguire il mapping e ridurre in un'unica operazione.

// File 1 
vector<wstring> v1;
v1.push_back(L"word1"); //1 
v1.push_back(L"word1"); //2 
v1.push_back(L"word2"); 
v1.push_back(L"word3"); 
v1.push_back(L"word4"); 

// File 2 
vector<wstring> v2; 
v2.push_back(L"word5"); 
v2.push_back(L"word6"); 
v2.push_back(L"word7"); 
v2.push_back(L"word8"); 
v2.push_back(L"word1"); //3 

vector<vector<wstring>> v;
v.push_back(v1);
v.push_back(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, parallelizzare solo con l'esterno o il ciclo interno.Se si dispone di un numero relativamente limitato di file e ciascun file contiene molte parole, parallelizzare il ciclo interno.Parallelizzare il ciclo esterno se si dispone relativamente molti file e ciascun file contiene alcune parole.

Vedere anche

Riferimenti

Funzione parallel_transform

Funzione parallel_reduce

Classe concurrent_unordered_map

Concetti

Algoritmi paralleli