Comment : exécuter des opérations de mappage et de réduction en parallèle
Cet exemple montre comment utiliser les algorithmes concurrency::parallel_transform et concurrency::parallel_reduce et la classe concurrency::concurrent_unordered_map pour compter les occurrences des mots dans les fichiers.
Une opération de mappage s'applique la fonction à chaque valeur dans une séquence. Une opération de réduction combine les éléments d'une séquence dans une valeur. Vous pouvez utiliser les classes de (STL) std::transform std::accumulate de Standard TEMPLATE Library (STL) pour effectuer le mappage et les opération de réductions. Toutefois, pour améliorer les performances dans de nombreuses situations, vous pouvez utiliser l'algorithme parallel_transform pour exécuter l'opération de mappage en parallèle et l'algorithme parallel_reduce pour exécuter l'opération de réduction en parallèle. Dans certains cas, vous pouvez utiliser concurrent_unordered_map pour effectuer le mappage et la réduction d'une opération.
Exemple
L'exemple suivant compte les occurrences des mots dans les fichiers. Il utilise std::vector pour représenter le contenu de deux fichiers. L'opération de mappage calcule les occurrences de chaque mot de chaque vecteur. L'opération de réduction cumule les comptes de mots entre les deux vecteurs.
// 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.
*/
Compilation du code
Pour compiler le code, copiez-le puis collez-le dans un projet Visual Studio , ou collez-le dans un fichier nommé parallel-map-reduce.cpp puis exécutez la commande suivante dans une fenêtre d'invite de commandes Visual Studio.
cl.exe /EHsc parallel-map-reduce.cpp
Programmation fiable
Dans cet exemple, vous pouvez utiliser la classe concurrent_unordered_map qui est définie dans concurrent_unordered_map.h-to pour effectuer le mappage et réduire dans une opération.
// 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.
*/
En général, vous ne parallélisez que la boucle interne ou externe. Parallélisez la boucle interne si vous avez relativement peu de fichiers et si chaque fichier contient plusieurs mots. Parallélisez la boucle externe si vous avez de nombreux de fichiers et si chaque fichier contient peu de mots.
Voir aussi
Référence
concurrent_unordered_map, classe