简体   繁体   English

std :: reduce with std :: unordered_map

[英]std::reduce with std::unordered_map

I have an unordered_map of vectors and I'm trying to use std::reduce to get the sum of all values in all vectors in the map. 我有一个vectorsunordered_map ,并且尝试使用std::reduce获取地图中所有vector中所有值的总和。 My current functional code (which I want to replace) looks like this: 我当前的功能代码(我要替换)如下所示:

// input is std::unordered_map<std::vector<uint64_t>>
template<typename T>
uint64_t get_map_sum(T& my_map)
{
    uint64_t totalcount = 0;
    for (auto& p : my_map) 
    {
        for (const auto& q : p.second)
            totalcount += q;
    }
    return total_count;
}

I'd like to replace this with std::reduce to utilize the parallel execution; 我想用std::reduce代替它来利用并行执行; I thought this would be straight forward as I only needed to replace each loop with a call to std::reduce , but this doesn't appear to be working. 我认为这很简单,因为我只需要用对std::reduce的调用来替换每个循环,但这似乎不起作用。 My attempt is this: 我的尝试是这样的:

#include <numeric>
#include <execution>
#include <vector>
#include <unordered_map>
#include <cstdint>
// reduces the vectors
template <typename Iter, typename T>
T get_vector_sum(Iter begin, Iter end, T initial = 0)
{
    return std::reduce(std::execution::par_unseq, begin, end, initial, 
           [&](auto cur, auto prev) { return cur + prev; });
}

// calls get_vector_sum for all vectors and then reduces vector sums
template<typename Iter>
uint64_t get_map_sum(Iter begin, Iter end)
{
    return std::reduce(std::execution::par_unseq, begin, end, 0ULL,
            [&](auto prev, auto cur)
            {
                return get_vector_sum<std::vector<uint64_t>::iterator, 
                       uint64_t>(cur.begin(), cur.end(), prev);
                //return get_vector_sum<std::vector<uint64_t>::iterator,
                //       uint64_t>(cur.second.begin(), cur.second.end(), prev);
            });
}

With the code above, I get an error message saying error C2039: 'begin': is not a member of 'std::pair' referring to the auto cur in the lambda inside get_map_sum . 使用上面的代码,我收到一条错误消息,提示error C2039: 'begin': is not a member of 'std::pair'指的是get_map_sum内部的lambda中的auto cur I initially used cur as a std::pair , but when I did that I got a different error saying error C2228: left of '.second' must have class/struct/union . 我最初使用cur作为std::pair ,但是当我这样做的时候,我得到了另一个错误,提示error C2228: left of '.second' must have class/struct/union

int main()
{
    std::unordered_map<uint64_t, std::vector<uint64_t>> in({ 
        {1, std::vector<uint64_t>{1,2,3,4,5} }, 
        {2, std::vector<uint64_t>{1,2,3,4,5}}, 
        {3, std::vector<uint64_t>{1,2,3,4,5}}});

    auto x = get_map_sum(in); // output 45
    auto y = get_map_sum(in.begin(), in.end()); // error

    return 0;
}

Is it possible to use std::reduce with maps like this and, if so, what changes do I need to make to get this working? 是否可以将std::reduce与这样的映射一起使用,如果可以,我需要进行哪些更改才能使它正常工作?

Note this requirements for binary_op of std::reduce : 请注意std::reduce binary_op的要求:

binary FunctionObject that will be applied in unspecified order to the result of dereferencing the input iterators , the results of other binary_op and init . 二进制FunctionObject ,将以不确定的顺序应用于取消引用输入迭代器结果,其他binary_opinit结果

This implies that the result of your lambda result and init needs to be of the same type as map's value type, ie, std::pair<const uint64_t, std::vector<uint64_t>> . 这意味着您的lambda结果和init的结果必须与地图的值类型具有相同的类型,即std::pair<const uint64_t, std::vector<uint64_t>>

You would therefore need to perform the outer reduction over values of this type, which would involve construction of new vectors. 因此,您将需要对该类型的值进行外部归约,这将涉及构建新向量。


I have also tried to create an exemplary code as follows: 我还尝试创建示例代码,如下所示:

using M = std::unordered_map<uint64_t, std::vector<uint64_t>>;
using V = M::value_type;

M in({ {1, std::vector<uint64_t>{1,2,3,4,5}}, 
       {2, std::vector<uint64_t>{1,2,3,4,5}}, 
       {3, std::vector<uint64_t>{1,2,3,4,5}} });

auto p = std::reduce(in.begin(), in.end(), V{}, 
    [](const V& a, const V& b) {
        auto ra = std::reduce(a.second.begin(), a.second.end(), 0UL,
            [](uint64_t i1, uint64_t i2){ return i1 + i2; });
        auto rb = std::reduce(b.second.begin(), b.second.end(), 0UL,
            [](uint64_t i1, uint64_t i2){ return i1 + i2; });
        return V{0, { ra + rb }};
});

But it does not compile with GCC due to seemingly missing std::reduce implementation and Clang complains about missing copy assignment operator for value type, which is not copy-assignable due to const key: https://wandbox.org/permlink/FBYAhCArtOHvwu8C . 但由于看似缺少std::reduce实现,因此无法使用GCC进行编译,Clang抱怨缺少值类型的副本赋值运算符,由于const键,该赋值运算符不可复制: https : //wandbox.org/permlink/FBYAhCArtOHvwu8C

However, in cppreference, the requirements for the value type is only MoveConstructible , not Copy/MoveAssignable . 但是,在cppreference中,值类型的要求仅是MoveConstructible ,而不是Copy / MoveAssignable So, there seems to be an incorrect implementation in libc++. 因此,libc ++中似乎有一个错误的实现。


In this exemplary code, I was able to make it working by defning V without const as follows: 在此示例代码中,我可以通过不带const的V来使其工作,如下所示:

using V = std::pair<uint64_t, std::vector<uint64_t>>; 

See https://wandbox.org/permlink/lF9VuJwISYXhpBJL . 请参阅https://wandbox.org/permlink/lF9VuJwISYXhpBJL

Rather than constructing vectors as the intermediate result, we just need to provide a type implicitly convertible from M::value_type . 无需构造向量作为中间结果,我们只需要提供一个可从M::value_type隐式转换的类型。

using M = std::unordered_map<uint64_t, std::vector<uint64_t>>;

template <typename Iter, typename T>
T par_unseq_sum(Iter begin, Iter end, T initial = 0)
{
    // std::plus is the default reducer
    return std::reduce(std::execution::par_unseq, begin, end, initial);
}

class map_vector_sum
{
public:
    map_vector_sum() : sum(0) {}
    map_vector_sum(M::const_reference elem) : sum(par_unseq_sum(elem.second)) {}

    map_vector_sum& operator+(const map_vector_sum & rhs) { sum += rhs.sum; }

    explicit operator uint64_t() { return sum; }
private:
    uint64_t sum;
}

M in({ {1, std::vector<uint64_t>{1,2,3,4,5}}, 
       {2, std::vector<uint64_t>{1,2,3,4,5}}, 
       {3, std::vector<uint64_t>{1,2,3,4,5}} });

uint64_t sum = par_unseq_sum(in.begin(), in.end(), map_vector_sum());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM