std::reduce 和 std::unordered_map
std::reduce with std::unordered_map
我有 vectors
的 unordered_map
,我正在尝试使用 std::reduce
来获取地图中所有向量中所有值的总和。我当前的功能代码(我想替换)如下所示:
// 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;
}
我想将其替换为 std::reduce
以利用并行执行;我认为这将是直截了当的,因为我只需要用对 std::reduce
的调用替换每个循环,但这似乎不起作用。我的尝试是这样的:
#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);
});
}
使用上面的代码,我收到一条错误消息,指出 error C2039: 'begin': is not a member of 'std::pair'
指的是 get_map_sum
内 lambda 中的 auto cur
。我最初使用 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;
}
是否可以将 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.
这意味着您的 lambda result 和 init 的结果需要与 map 的值类型相同,即 std::pair<const uint64_t, std::vector<uint64_t>>
。
因此,您需要对这种类型的值执行外部归约,这将涉及构建新向量。
我也试过创建一个示例代码如下:
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 }};
});
但由于似乎缺少 std::reduce
实现,它无法使用 GCC 进行编译,并且 Clang 抱怨缺少值类型的复制赋值运算符,由于 const 键,该运算符不可复制赋值:https://wandbox.org/permlink/FBYAhCArtOHvwu8C .
但是在cppreference中,对值类型的要求只有MoveConstructible,而不是Copy/MoveAssignable。所以,在 libc++ 中似乎有一个不正确的实现。
在这个示例代码中,我能够通过定义 V
而不使用 const 来使其工作,如下所示:
using V = std::pair<uint64_t, std::vector<uint64_t>>;
无需构造向量作为中间结果,我们只需要提供一个可从 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());
我有 vectors
的 unordered_map
,我正在尝试使用 std::reduce
来获取地图中所有向量中所有值的总和。我当前的功能代码(我想替换)如下所示:
// 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;
}
我想将其替换为 std::reduce
以利用并行执行;我认为这将是直截了当的,因为我只需要用对 std::reduce
的调用替换每个循环,但这似乎不起作用。我的尝试是这样的:
#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);
});
}
使用上面的代码,我收到一条错误消息,指出 error C2039: 'begin': is not a member of 'std::pair'
指的是 get_map_sum
内 lambda 中的 auto cur
。我最初使用 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;
}
是否可以将 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.
这意味着您的 lambda result 和 init 的结果需要与 map 的值类型相同,即 std::pair<const uint64_t, std::vector<uint64_t>>
。
因此,您需要对这种类型的值执行外部归约,这将涉及构建新向量。
我也试过创建一个示例代码如下:
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 }};
});
但由于似乎缺少 std::reduce
实现,它无法使用 GCC 进行编译,并且 Clang 抱怨缺少值类型的复制赋值运算符,由于 const 键,该运算符不可复制赋值:https://wandbox.org/permlink/FBYAhCArtOHvwu8C .
但是在cppreference中,对值类型的要求只有MoveConstructible,而不是Copy/MoveAssignable。所以,在 libc++ 中似乎有一个不正确的实现。
在这个示例代码中,我能够通过定义 V
而不使用 const 来使其工作,如下所示:
using V = std::pair<uint64_t, std::vector<uint64_t>>;
无需构造向量作为中间结果,我们只需要提供一个可从 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());