多线程将映射分为多个部分

Multithreaded splitting a map into multiple sections

本文关键字:个部 多线程 映射      更新时间:2023-10-16

据说我有一个较大的std::map<SomeType1, std::vector<SomeType2>> my_map,我需要对地图中的所有向量进行排序。目前我正在以一个线程运行:

for (auto& item : my_map)
{
    std::sort(item.second.begin(), item.second.end(), &some_comparer);
}

使用上面的代码,我的CPU经常闲置〜15%,因此我认为我可以将地图分为较小的部分,并在单独的线程中对每个部分进行排序。

我想问,如何分配地图?例如,我想将其分为4个部分:

auto& section1 = my_map.divide(0, 0.25); // <~ how to apply this?
auto& section2 = my_map.divide(0.25, 0.5);
auto& section1 = my_map.divide(0.5, 0.75);
auto& section1 = my_map.divide(0.5, 1);
std::thread thread1([&section1] { sort_for_me_pls(section1); });
std::thread thread2([&section2] { sort_for_me_pls(section2); });
std::thread thread3([&section3] { sort_for_me_pls(section3); });
std::thread thread4([&section4] { sort_for_me_pls(section4); });
thread1.join();
thread2.join();
thread3.join();
thread4.join();

使用C 17,并行对向量排序很容易:

for (auto& [key, value] : my_map) {
    std::sort(std::execution::par, std::begin(value), std::end(value), &some_comparer);
}

不幸的是,我不认为有任何编译器已经在此时在标准库中实现了算法的并行版本。不过,它可能很快就会发生(一年之内?(

您可以用类似的东西手动使用std::thread

std::vector<std::thread> threads;
for (auto& [key, value] : my_map) {
    threads.emplace_back([&] {
        std::sort(std::begin(value), std::end(value), &some_comparer);
    });
}
for (auto&& t : threads) {
    t.join();
}

您可以参考此问题的第一个答案(如何从std :: Map中检索所有键(或值(并将其放入向量?(,以查看如何获取地图中所有键的向量。完成此操作后,您可以传递到函数每个线程执行迭代器(或索引(以在密钥向量中开始及其应处理的键数。然后,每个线程只能对与密钥向量部分中的密钥关联的所有向量进行分类。该实现是不平凡的,因此我只将其留给您(例如,如果键少于4个键,您该怎么办,如果键的数量不均衡为4等,该怎么办。(。

SNP提供了一个很好的答案来解决您的实际问题。但是,由于您的问题是要将地图分成多个部分,所以我认为您应该查看此答案,以解决类似(但更通用的(问题。您应该能够将此解决方案应用于地图或将其概括为与任何类型的容器一起使用。例如(splitc容器拆分为parts零件(:

template <typename C>
using CItRange = boost::sub_range<const C>;
template <typename C>
std::vector<CItRange<C>> split(const C& c, size_t parts) {
   const size_t step = c.size() / parts;
   int remainder = c.size() % parts;
   std::vector<CItRange<C>> slices;
   auto it = begin(c);
   while(it != end(c)) {
      auto sliceBegin = it;
      size_t remainderSpread = remainder-- > 0 ? 1 : 0;
      std::advance(it, std::min(step + remainderSpread, (size_t)std::distance(it, end(c))));
      slices.push_back(CItRange<C>{sliceBegin, it});
   }
   return slices;
}

您可以这样使用它:

std::map<int, std::vector<int>> myMap = {{1,{}}, {2,{}}, {3,{}}, {4,{}}, {5,{}}};
for(const auto& mapSlice : split(myMap, 2)) {
   ...
}

http://coliru.stacked-crooked.com/a/a/9d82fe79cc274dd7