【问题标题】:Multithreaded splitting a map into multiple sections多线程将地图拆分为多个部分
【发布时间】:2018-07-23 01:10:37
【问题描述】:

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

【问题讨论】:

  • 为什么不简单地对向量进行并行排序,,每个线程一个向量?划分工作负载然后将其连接在一起(合并排序段等)非常麻烦。
  • @Snps 我也想学习如何并行排序向量,但目前我不知道如何编写线程池管理器来处理线程。你能把答案贴出来吗?

标签: c++ multithreading algorithm vector hashmap


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      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

      【讨论】:

        猜你喜欢
        • 2011-05-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-03
        • 2018-03-10
        • 1970-01-01
        • 2011-08-26
        相关资源
        最近更新 更多