)
oneTBB 流图原理剖析节点如何映射为任务Mapping Nodes to Tasks【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold导读本文解读 mold 仓库内置的 oneTBB 用户指南中《Mapping Nodes to Tasks》一章剖析流图Flow Graph中节点Node与任务Task之间的映射机制一次try_put调用如何催生一个可并行执行的任务、节点的并发度concurrency如何决定任务的生成节奏、以及wait_for_all()阻塞期间调用线程为何不会空转。读完本文你将掌握 oneTBB 流图异步执行模型的核心时序并能依据并发度参数精确预测两节点流图的运行行为。本文对应的原始文档位于 third-party/tbb/doc/main/tbb_userguide/Mapping_Nodes2Tasks.rst它是 oneTBB 用户指南「流图基础」主题Basic Flow Graph Concepts的第四个章节前序章节依次讲解了 图对象Graph Object、节点Nodes 与 边Edges。图两节点流图的一次可能执行时间线来自 Images/execution_timeline2node.jpg。横轴为时间上方为节点 n 的三个任务并发执行下方为节点 m 按到达顺序串行执行的任务。一、背景本文讨论的两节点流图《Mapping Nodes to Tasks》所分析的执行时间线基于前一节Edges.rst给出的两节点示例。先回顾该示例它是理解任务映射的前提graph g; function_node int, int n( g, unlimited, []( int v ) - int { cout v; spin_for( v ); cout v; return v; } ); function_node int, int m( g, 1, []( int v ) - int { v * v; cout v; spin_for( v ); cout v; return v; } ); make_edge( n, m ); n.try_put( 1 ); n.try_put( 2 ); n.try_put( 3 ); g.wait_for_all();图中包含两个function_node节点 n以unlimited无限制并发构造body 为 λ_n对输入消息 v 打印、spin_for(v)忙等、再打印并原样返回 v节点 m以并发度1构造body 为 λ_m将输入平方后打印make_edge( n, m )建立从 n 到 m 的有向边n 的每个输出 v 会被运行库自动投递给 m三次n.try_put(...)注入三个输入消息最后g.wait_for_all()等待图中所有任务完成。下文时间线中的 λ_n 与 λ_m 即分别指代这两个节点的 lambda 表达式 body。二、核心时序三次 try_put 产生三个任务原文档给出的执行时间线揭示了一个关键结论对节点 n 的三次try_put调用对应产生三个任务task每个任务将 λ_n 应用于三条输入消息之一。由于节点 n 的并发度是unlimited这些任务只要线程池中有足够的线程就可以并发执行。任务的生成与线程的创建是两回事oneTBB 并不为每个任务创建线程任务被投递到库的线程池工作队列由有限数量的工作线程调度执行。任务在节点之间的流转每当节点 n 的一个任务完成其输出就会投递给后继节点 m因为 m 是 n 的 successor。与 n 不同m 的构造并发度为 1m不会一次性派生所有任务而是按消息到达的顺序串行地sequentially逐个派生任务执行 λ_m当图中所有任务执行完毕wait_for_all()调用返回。也就是说并发度参数直接决定了「任务映射策略」unlimited节点把每条输入消息立即映射为一个任务并放入工作池serial并发度 1节点则把消息先缓冲起来前一个任务结束后再映射下一个任务。并发度枚举的源码依据在 oneTBB 头文件 third-party/tbb/include/oneapi/tbb/flow_graph.h 中两种最常用的并发度以枚举形式定义//! An enumeration the provides the two most common concurrency levels: unlimited and serial enum concurrency { unlimited 0, serial 1 };其中unlimited 0表示不限并发serial 1表示严格串行除此之外function_node的构造参数concurrency也接受任意正整数如 4、8表示最多允许多少个 body 调用并发执行。从 flow_graph.h 的类定义可以看到function_node同时继承graph_node、输入侧的function_input与输出侧的function_output其构造函数签名与文档一致template typename Body function_node( graph g, size_t concurrency, Body body, Policy Policy(), node_priority_t a_priority no_priority )三、wait_for_all阻塞但不空转时间线中唯一真正阻塞的调用是g.wait_for_all()它一直阻塞到图中不再有正在执行的任务。原文档特别强调与 oneTBB 中其他wait_for_all函数一样调用wait_for_all的线程在这段时间内并非空转自旋spinning idly而是可以加入工作池执行其他任务。这是理解 oneTBB 调度模型的关键点wait_for_all并非简单的忙等等待线程本身会成为工作线程池中的一员参与执行被投递的任务。在本文的两节点示例中等待线程可能实际执行 n 或 m 的 body 任务从而提升线程利用率。这一语义与 oneTBB 任务调度器work-stealing 工作窃取的设计一脉相承相关背景可进一步阅读用户指南中的 任务调度器The Task Scheduler 与 任务调度器如何工作How Task Scheduler Works。四、全异步执行模型try_put 快速返回原文档在注记note中总结了流图执行的非阻塞本质这是本主题最需要牢记的运行时契约流图中的所有执行都是异步发生的try_put调用会很快将控制权返回给调用线程——它要么立即派生一个任务要么把消息缓冲buffer在节点内部随后返回body 任务执行 lambda 表达式然后把结果投递给任何后继节点只有wait_for_all会且应当阻塞而且即使阻塞调用线程也可能被用于执行 oneTBB 工作池中的其他任务。这意味着在并发度为 1 的节点上若当前已有任务在执行后到的消息不会被丢弃而是进入节点的输入缓冲队列待并发度允许时再被映射为任务。这一「先缓冲、后派生」的行为正是 Nodes.rst 中描述的消息处理流程try_put不会阻塞到任务派生为止节点暂时无法派生任务时消息被缓冲当符合并发度限制时再派生任务处理下一条缓冲消息。五、线程不足时任务排队等待前文时间线描述的是「线程足够」的理想情形——所有可以并行执行的任务都被同时执行。原文档明确指出了受约束的情形如果可用线程较少部分已派生的任务将不得不等待直到有线程可用时才被执行。也就是说任务的派生spawn与任务的执行execute是解耦的即使节点以unlimited并发度一口气派生了三个任务若系统只有一个线程三个 body 调用依然串行执行。任务数量不等于线程数量实际并行度取决于 oneTBB 线程池中可用工作线程的数目。这一结论与 Nodes.rst 末尾的示例相互印证系统线程充足时三个 λ_n 并行执行只有一个线程时则顺序执行。六、延伸阅读消息传递协议与流图进阶本文的主题节点到任务的映射只是流图运行时行为的起点。理解「任务何时被派生、消息何时被接受」之后可继续阅读同主题下的后续章节消息传递协议Flow Graph Message Passing Protocol讲解边在 push 与 pull 两种模式之间的动态切换——当后继节点无法接收消息时边反转状态由后继节点主动向前驱拉取消息避免前驱反复重试投递浪费资源单播与广播Single vs Broadcast讲解消息是被单个后继接收还是广播给所有后继节点中的缓冲Buffering in Nodes 与 预留机制Flow Graph Reservation深入消息在节点间的缓冲与预留语义流图主类别Graph Main Categories 与 预定义节点类型Predefined Node Types掌握更多节点类型为构建真实流图做准备。完整主题索引见 Parallelizing Data Flow and Dependence Graphs。小结「节点映射到任务」是 oneTBB 流图并发模型的核心机制可归纳为四条要点每次try_put都可能派生一个任务任务的产生与消息的到达一一对应具体节奏由节点的并发度控制并发度决定映射策略unlimited立即派生任务serial1串行派生任意正整数则限定最大并发数见 flow_graph.h 的concurrency枚举wait_for_all阻塞但不空转等待线程可参与执行工作池中的任务任务派生与执行解耦实际并行度受线程池可用线程数约束线程不足时任务排队等待。掌握这套时序模型是正确设计流图、预估并行收益、避免意外串行化的前提。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考