It can be done using a similar approach to the one taken in the answer you linked. The one issue we face is that std::transform
uses an unfortunate line when it comes to maps.
//GCC version, but the documentation suggests the same thing.
*__result = __binary_op(*__first1, *__first2);
Since maps store types std::pair<const T1, T2>
(i.e. the first must always be const, you can't modify the key), this would cause an error, because the operator=
is deleted for this case.
For that reason we end up having to write this whole thing ourselves (the answer that follows could be made cleaner, I just hard-coded your type...).
We could just start with the examples of std::transform
(look at example implementation 2) and modify the problematic part, but @Zulan raises a good point in the comments that simultaneously traversing unordered maps might not be a good idea (as they are, by definition, not ordered). While it might make some sense that the copy constructor retain the order, it seems that this is not guaranteed by the standard (at least I couldn't find it anywhere), as such the approach that std::transform
takes becomes pretty useless.
We can resolve this issue with a slightly different reduction.
#include <unordered_map>
#include <string>
#include <iostream>
#include <utility>
void reduce_umaps(\
std::unordered_map<std::string, double>& output, \
std::unordered_map<std::string, double>& input)
{
for (auto& X : input) {
output.at(X.first) += X.second; //Will throw if X.first doesn't exist in output.
}
}
#pragma omp declare reduction(umap_reduction : \
std::unordered_map<std::string, double> : \
reduce_umaps(omp_out, omp_in)) \
initializer(omp_priv(omp_orig))
using namespace std;
unordered_map<string, double> umap {{"foo", 0}, {"bar", 0}};
string some_string(int in) {
if (in % 2 == 0) return "foo";
else return "bar";
}
inline double some_double(int in) {
return static_cast<double>(in);
}
int main(void) {
#pragma omp parallel for reduction(umap_reduction:umap)
for (int i = 0; i < 100; ++i) {
umap.at(some_string(i)) += some_double(i);
}
std::cerr << umap["foo"] << " " << umap["bar"] << "\n";
return 0;
}
You can also generalise this to allow the addition of keys within the parallel loop, but that won't parallelise well unless the number of added keys remains much smaller than the number of times you increase the values.
As a final side note, I replaced umap[some_string(i)]
with umap.at(some_string(i))
, to avoid accidentally adding elements much like was suggested in the comments, but find
isn't the most practical function for that purpose.