多线程将地图拆分为多个部分

时间:2018-02-12 19:04:30

标签: c++ multithreading algorithm vector hashmap

据说我有一个很大的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();

3 个答案:

答案 0 :(得分:3)

使用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();
}

答案 1 :(得分:0)

您可以参考此问题的第一个答案(How to retrieve all keys (or values) from a std::map and put them into a vector?),了解如何获取地图中所有键的向量。执行此操作后,您可以传递给函数,每个线程执行一个迭代器(或索引),从键向量开始,以及它应处理的键数。然后,每个线程可以只对与键向量部分中的键相关联的所有向量进行排序。实现是非常重要的,所以我将把它留给你(例如,如果键少于4个键怎么办,如果键的数量不能被4整除,怎么办等。)

答案 2 :(得分:0)

Snps为解决您的实际问题提供了一个很好的答案。但由于你的问题是将地图分成多个部分,我认为你应该看看this answer to a similar (yet more generic) question。您应该能够将此解决方案应用于地图或将其概括为适用于任何类型的容器。例如(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/9d82fe79cc274dd7