Gewusst wie: Paralleles Ausführen von Zuordnungs- und Reduzierungsoperationen

In diesem Beispiel wird gezeigt, wie Sie die Parallelität::p arallel_transform and concurrency::p arallel_reduce algorithmen und die Parallelitätsklasse::concurrent_unordered_map verwenden, um die Vorkommen von Wörtern in Dateien zu zählen.

Ein Kartenvorgang wendet eine Funktion auf jeden Wert in einer Sequenz an. Ein Reduzierter Vorgang kombiniert die Elemente einer Sequenz in einem Wert. Sie können die C++-Standardbibliothek std::transform and std::kumulierte Funktionen verwenden, um Zuordnungen durchzuführen und Vorgänge zu reduzieren. Zur Verbesserung der Leistung bei vielen Problemen können Sie mit dem parallel_transform-Algorithmus parallel den Zuordnungsvorgang und mit dem parallel_reduce-Algorithmus parallel den Reduzierungsvorgang ausführen. In einigen Fällen können Sie mit concurrent_unordered_map die Zuordnung und Reduzierung in einem Vorgang durchführen.

Beispiel

Im folgenden Beispiel wird das Vorkommen von Wörtern in Dateien gezählt. Es verwendet std::vector , um den Inhalt von zwei Dateien darzustellen. Der Zuordnungsvorgang berechnet das Vorkommen von jedem Wort in jedem Vektor. Der Reduzierungsvorang zählt die Wörter in den zwei Vektoren zusammen.

// 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.
*/

Kompilieren des Codes

Um den Code zu kompilieren, kopieren Sie ihn, und fügen Sie ihn dann in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt parallel-map-reduce.cpp ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.

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

Stabile Programmierung

In diesem Beispiel können Sie mit der in concurrent_unordered_map.h definierten concurrent_unordered_map-Klasse die Zuordnung und Reduzierung in einem Vorgang durchführen.

// 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 der Regel wird nur die äußere oder die innere Schleife parallel ausgeführt. Führen Sie die innere Schleife parallel aus, wenn Sie über relativ wenige Dateien mit vielen Wörtern verfügen. Führen Sie die äußere Schleife parallel aus, wenn Sie über relativ viele Dateien mit wenigen Wörtern verfügen.

Siehe auch

Parallele Algorithmen
parallel_transform-Funktion
parallel_reduce-Funktion
concurrent_unordered_map-Klasse