ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

mold 项目第三方依赖 TBB 指南:make_edges 函数模板实现流图一对多/多对一建边

mold 项目第三方依赖 TBB 指南:make_edges 函数模板实现流图一对多/多对一建边 mold 项目第三方依赖 TBB 指南make_edges 函数模板实现流图一对多/多对一建边【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold导读make_edges是 oneAPI Threading Building BlocksoneTBBFlow Graph 库中用于批量建边的函数模板它可以在一个节点与一组节点之间一次性创建全部边从而把 N 次make_edge调用压缩为一行代码。本文以 mold 仓库中 vendored 的 TBB 源码third-party/tbb为对象完整讲解make_edges的启用条件、两种连接方向、配套的make_node_set/follows/precedes工具、官方示例并深入到_flow_graph_node_set_impl.h的模板实现细节与测试用例帮助读者在编写 flow graph 时写出更简洁、可读性更高的建边代码。说明mold 作为一个现代化链接器将 oneTBB 作为第三方库整体随仓库分发见 third-party/tbb本文所讲内容即来自该目录下的 TBB 源码与官方文档可在本仓库内直接查阅验证。功能概述批量创建边make_edges函数模板的职责是在单个节点与一组节点之间创建边edge。它的使用前提是先通过make_node_set把多个节点打包成一个节点集合node_set再调用make_edges一次性完成批量连接。官方文档make_edges_function.rst明确指出使用make_edges有两种连接方式make_edges(node_set, node)节点集合中的每个节点都向单个目标节点连一条边多对一make_edges(node, node_set)单个源节点向节点集合中的每个节点各连一条边一对多。两种方式的效果如下图所示图片来源仓库内官方文档配图 make_edges_usage.png启用条件预览特性宏该功能属于 TBB 的预览preview特性。使用前必须在包含头文件之前定义宏#define TBB_PREVIEW_FLOW_GRAPH_FEATURES 1官方文档make_edges_function.rst 开头的 note以及配套示例 make_edges_function_example.cpp 都强调了这个前提。从源码看整个node_set/make_edges实现都包裹在#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET条件编译块中见 flow_graph.h 中 150、671、917 等多处以及 _flow_graph_node_set_impl.h 的#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET未定义该宏时相关 API 不会被编译。API 语法与头文件头文件make_edges与make_node_set都通过公开头文件暴露#include oneapi/tbb/flow_graph.h在 flow_graph.h 中可以看到它们被显式导入到oneapi::tbb::flow命名空间using detail::d2::make_node_set; using detail::d2::make_edges;函数签名官方文档给出的两个重载签名如下其中node_set是make_node_set返回类型的说明性名称实际类型为模板node_setNode, Nodes...template typename NodeType, typename Node, typename... Nodes void make_edges(node_setNode, Nodes... set, NodeType node); template typename NodeType, typename Node, typename... Nodes void make_edges(NodeType node, node_setNode, Nodes... set);对应的实际声明位于 _flow_graph_node_set_impl.h参数为const node_setOrderFlagType, Args...。源码中还有一条 TODO 注释consider adding an overload for making edges between node sets说明目前make_edges只支持单节点 × 节点集合的组合尚不支持在两个集合之间直接建边。配套工具make_node_set / follows / precedesmake_edges依赖make_node_set构造的节点集合。文档 make_node_set_function.rst 说明make_node_set函数模板创建一组节点可将其作为参数传给make_edges、follows和precedes函数。其语法为template typename Node, typename... Nodes /*unspecified*/ make_node_set( Node node, Nodes... nodes );从实现看_flow_graph_node_set_impl.hmake_node_set返回类型为node_setorder::undefined, Node, Nodes...其中order::undefined表示尚未确定边的方向。节点集合内部用std::tupleNodes...以引用方式持有各节点_flow_graph_node_set_impl.h因此不会发生拷贝。与order::undefined对应源码还定义了另外两种方向标记_flow_graph_node_set_impl.hnamespace order { struct undefined {}; struct following {}; // 节点集合作为前驱predecessors struct preceding {}; // 节点集合作为后继successors }follows与precedes用于在构造节点尤其是join_node时声明前驱/后继集合follows(predecessors...)返回node_setorder::following, ...要求集合内所有节点都是sender具有output_typeprecedes(successors...)返回node_setorder::preceding, ...要求集合内所有节点都是receiver具有input_type。两种函数在实现中都通过static_assert对节点类型做了编译期检查_flow_graph_node_set_impl.h一旦传入不满足条件的节点编译即报错从而把类型错误提前到编译期。此外node_set之间还支持从order::undefined集合拷贝构造为带方向标记的集合第 67-68 行的转换构造函数便于先收集节点、后声明方向。完整示例扇出 扇入的流图官方示例 make_edges_function_example.cpp 构建了这样一个流图一个broadcast_nodeint作为输入源input三个function_nodeint,intdoubler×2、squarer平方、cuber立方——三者并行消费输入一个buffer_nodeint作为汇聚缓冲buffer。建边部分只有两行auto handlers make_node_set(doubler, squarer, cuber); make_edges(input, handlers); // 一对多input → doubler/squarer/cuber make_edges(handlers, buffer); // 多对一doubler/squarer/cuber → buffer完整可运行代码需 C17 及以上示例源码中通过#if __cplusplus 201703L做了版本守卫#define TBB_PREVIEW_FLOW_GRAPH_FEATURES 1 #include oneapi/tbb/flow_graph.h int main() { using namespace oneapi::tbb::flow; graph g; broadcast_nodeint input(g); function_node doubler(g, unlimited, [](const int i) { return 2 * i; }); function_node squarer(g, unlimited, [](const int i) { return i * i; }); function_node cuber(g, unlimited, [](const int i) { return i * i * i; }); buffer_nodeint buffer(g); auto handlers make_node_set(doubler, squarer, cuber); make_edges(input, handlers); make_edges(handlers, buffer); for (int i 1; i 10; i) { input.try_put(i); } g.wait_for_all(); }该图的结构如下图片来源仓库内官方文档配图 make_edges_example.png作为对比若使用底层make_edge上述建边需要手写 6 次调用而make_edges把扇出与扇入各压缩成一行且集合的构成一目了然。node_set内部按参数顺序保存节点引用因此节点在集合中的排列顺序是确定的。源码级原理模板递归建边make_edges的底层实现位于 _flow_graph_node_set_impl.h核心是编译期递归的make_edges_helper模板第 211-238 行templatesize_t I class make_edges_helper { public: templatetypename PredecessorsTuple, typename NodeType static void connect_predecessors(PredecessorsTuple predecessors, NodeType node) { make_edge(std::getI(predecessors), successor_selectorI::get(node)); make_edges_helperI - 1::connect_predecessors(predecessors, node); } templatetypename SuccessorsTuple, typename NodeType static void connect_successors(NodeType node, SuccessorsTuple successors) { make_edge(predecessor_selectorI::get(node), std::getI(successors)); make_edges_helperI - 1::connect_successors(node, successors); } }; template struct make_edges_helper0 { /* 递归终止只处理索引 0 的元素 */ };make_edges的两个重载分别调用它们make_edges(const node_set s, NodeType node)SetSize tuple_size调用make_edges_helperSetSize-1::connect_predecessors(s.nodes, node)即把集合中每个节点当作前驱、与目标节点的输入端口相连_flow_graph_node_set_impl.hmake_edges(NodeType node, const node_set s)调用connect_successors(node, s.nodes)即把集合中每个节点当作后继、与源节点的输出端口相连第 247-251 行。最终每个连接都归结为一次标准的make_edge调用因此make_edges只是make_edge的批量语法糖语义完全一致。端口选择多端口节点与 async_node 特化节点并不总是单端口。successor_selector与predecessor_selector第 154-209 行负责处理多端口情况对具有input_ports_type的节点successor_selectorI::get(node)会返回input_portI(node)即取第 I 个输入端口对具有output_ports_type的节点predecessor_selectorI::get(node)会返回output_portI(node)对单端口节点则直接返回节点自身引用。选择逻辑通过std::true_type/std::false_type标签分派和decltype尾置返回类型在编译期完成MSVC 因对尾置 decltype 支持问题源码中还有专门的__TBB_MSVC_DISABLE_TRAILING_DECLTYPE兼容分支见第 27-30 行。此外predecessor_selector对async_node有专门特化is_async_node第 100-104、196-197 行确保async_node作为前驱时直接连接其本身而非内部端口。图引用获取node_set::graph_reference()通过get_graph_helper第 38-58 行获取节点所属的graph若节点继承自graph_node直接读取其my_graph成员否则调用节点的graph_reference()方法。这使得node_set可以统一地从任意成员节点回溯到所属图。测试验证与 follows/precedes 的组合使用仓库测试代码印证了make_node_set与follows/precedes的组合用法。在 test_join_node_multiple_predecessors.h 中TBB_PREVIEW_FLOW_GRAPH_FEATURES开启时提供了两种等价的join_node连接方式// 方式一make_node_set follows将三个 buffer_node 作为 join_node 的前驱 auto bn_set make_node_set(bn1, bn2, bn3); std::unique_ptrjoin_node_t jn( new join_node_t(follows(bn_set)) ); // 方式二make_node_set precedes将 queue_node 声明为 join_node 的后继 auto qn_set make_node_set(qn); auto qn_copy_set qn_set; std::unique_ptrjoin_node_t jn( new join_node_t(precedes(qn_copy_set)) );该测试与上文make_edges的两种调用方向一一对应验证了make_node_set构建的order::undefined集合既能被follows转成order::following集合做前驱对应make_edges(set, node)也能被precedes转成order::preceding集合做后继对应make_edges(node, set)。使用注意事项必须开启预览宏TBB_PREVIEW_FLOW_GRAPH_FEATURES 1要在包含flow_graph.h之前定义否则make_node_set/make_edges不存在源码中整个功能块由__TBB_PREVIEW_FLOW_GRAPH_NODE_SET条件编译控制。C17 及以上官方示例用#if __cplusplus 201703L守卫低于 C17 时示例会被跳过提示该功能依赖较新的标准特性。只支持一 × 多组合从源码 TODO 注释看make_edges目前不支持在两个node_set之间建边follows/precedes的static_assert也会在节点类型不满足sender/receiver要求时直接编译失败。节点以引用保存node_set内部是std::tupleNodes...不会拷贝节点但要保证传入节点的生命周期覆盖建边及图执行期间。接口仍在预览演进中make_edges/make_node_set是 preview 特性其 API 形态可能随 TBB 版本演进调整本文所述以本仓库 vendored 的 third-party/tbb 版本为准。延伸阅读配套函数文档make_node_set_function.rstmake_node_set、follows、precedes的说明与 See Also 交叉引用官方示例源码make_edges_function_example.cpp核心实现_flow_graph_node_set_impl.h公开头文件与命名空间导入flow_graph.h相关测试test_join_node_multiple_predecessors.h【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表