用std :: async控制并行度

Kon*_*lph 9 c++ parallel-processing multithreading asynchronous c++11

有没有办法明确设置/限制std::async和相关类使用的并行度(=单独线程数)?

仔细阅读线程支持库并没有发现任何有希望的东西.

尽可能接近,std::async实现(通常是?)在内部使用线程池.是否有标准化的API来控制这个?

对于后台:我在一个设置(共享集群)中,我必须手动限制使用的核心数.如果我没有这样做,负载共享调度程序会抛出一个拟合,我会受到惩罚.特别是,std::thread::hardware_concurrency()没有有用的信息,因为物理核心的数量与我所受的约束无关.

这是一段相关的代码(在C++ 17中使用并行TS,可能会使用并行std::transform编写):

auto read_data(std::string const&) -> std::string;

auto multi_read_data(std::vector<std::string> const& filenames, int ncores = 2) -> std::vector<std::string> {
    auto futures = std::vector<std::future<std::string>>{};

    // Haha, I wish.
    std::thread_pool::set_max_parallelism(ncores);

    for (auto const& filename : filenames) {
        futures.push_back(std::async(std::launch::async, read_data, filename));
    }

    auto ret = std::vector<std::string>(filenames.size());
    std::transform(futures.begin(), futures.end(), ret.begin(),
            [](std::future<std::string>& f) {return f.get();});
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

从设计的角度来看,我已经期望这个std::execution::parallel_policy类(来自并行性TS)允许指定(事实上,这就是我在我为硕士论文设计的框架中所做的那样).但事实似乎并非如此.

理想情况下,我想要一个C++ 11的解决方案,但是如果有一个用于更高版本的解决方案,我仍然想知道它(尽管我不能使用它).

Ser*_*eyA 6

No.std::async是不透明的,您无法控制它对线程、线程池或其他任何东西的使用。事实上,您甚至无法保证它会使用一个线程——它也可能在同一个线程中执行(可能,注意下面的@TC 注释),并且这样的实现仍然是一致的。

C++ 线程库从来不应该处理线程管理的操作系统/硬件细节的微调,所以我担心,在你的情况下,你必须自己编码以获得适当的支持,可能使用操作系统提供的线程控制原语。


TBB*_*Ble 5

正如其他人所指出的,std::async不允许您这样做。

\n

然而(但请参阅答案末尾的 2022 年 5 月更新)

\n

您正在描述Executors的一个更简单的用例,该用例目前仍在 C++ 标准化的设计空间中活跃,特别是现在在研究组 1:并发性中。

\n

由于阅读 WG21 标准提案可能会很困难,因此它们的作者已链接到仅包含原型标头的参考实现一些示例代码

\n

它甚至包括一个静态线程池,以及几乎正是您想要的示例:\n async_1.cpp

\n
#include <experimental/thread_pool>\n#include <iostream>\n#include <tuple>\n\nnamespace execution = std::experimental::execution;\nusing std::experimental::static_thread_pool;\n\ntemplate <class Executor, class Function>\nauto async(Executor ex, Function f)\n{\n  return execution::require(ex, execution::twoway).twoway_execute(std::move(f));\n}\n\nint main()\n{\n  static_thread_pool pool{1};\n  auto f = async(pool.executor(), []{ return 42; });\n  std::cout << "result is " << f.get() << "\\n";\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

感谢 @jared-hoberock 向我指出P0668R0作为P0443R1的更简单的后续版本,我在这个答案的早期版本中引用了 P0443R1。

\n

这种简化已经得到应用,现在既有一篇描述其基本原理的论文 ( P0761R0 ),也有P0443R2中标准措辞的简单得多的版本。

\n
\n

截至 2017 年 7 月,我看到的对此的唯一实际猜测是:Concurrency TS(执行器的标准化工具)的编辑 Michael Wong 感到“有信心它将进入 C+” +20”

\n
\n

我仍在获取 Stack Overflow Points\xe2\x84\xa2 来获取此答案,因此这里是 2022 年 5 月的更新:

\n

执行器没有登陆 C++20。

\n

“A Unified Executors Proposal for C++”于 2020 年达到修订版 14 ( P0443R14 ),并提出了一篇新论文std::execution( P2300R5 ) 作为后续;关于新论文的原因以及与 P0443 的差异,请参见第 1.81.9节。

\n

尤其:

\n
\n

根据 LEWG 的指示,省略了特定的线程池实现。

\n
\n

“在线程池中执行”示例std::execution如下所示:

\n
#include <experimental/thread_pool>\n#include <iostream>\n#include <tuple>\n\nnamespace execution = std::experimental::execution;\nusing std::experimental::static_thread_pool;\n\ntemplate <class Executor, class Function>\nauto async(Executor ex, Function f)\n{\n  return execution::require(ex, execution::twoway).twoway_execute(std::move(f));\n}\n\nint main()\n{\n  static_thread_pool pool{1};\n  auto f = async(pool.executor(), []{ return 42; });\n  std::cout << "result is " << f.get() << "\\n";\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这里有很多事情需要处理。过去十年的工作几乎已经放弃了“std::async和相关类”,所以也许这个问题的实际答案不再是

\n
\n

然而

\n
\n

\n
\n

,而且永远不会有。将会有一个不同的模型可供您替代。

\n
\n

参见P2300R5 的动机部分

\n
\n

std::async// std::futurestd::promiseC++11\xe2\x80\x99s 旨在暴露异步性,效率低下,难以正确使用,并且严重缺乏通用性,使其在许多上下文中无法使用。

\n
\n

P2453R0记录了 LEWG 参与者对当前方法的粗略感受,以及它如何与现有的 Networking TS(即 Asio)交互,Asio 有自己的并发模型对民意调查和评论的阅读表明,两者都不太可能出现在 C++23 中。

\n
\n

我仍在获取 Stack Overflow Points\xe2\x84\xa2 来获取此答案,因此这里是 2023 年 6 月的更新:

\n

执行器没有登陆 C++23。

\n

P2300 看起来越来越受欢迎,一直到P2300R7,并且有一个实验性参考实现和基于它的进一步标准化探索,例如P2882R0P2500R1 + P2690R1。后一个链接表明 P2300 是针对 C++26 的,这也是LEWG 去年的弱共识,尽管只有一半的参与者和较少的评论

\n