join_node graph flow construction

时间:2017-07-20 15:10:08

标签: tbb tbb-flow-graph

我正在尝试TBB的英特尔图表流程。我对结果非常满意,我发现产品令人惊叹,具有无限的可能性。然而,我遇到了一个我修复的pb,但我并不满意。 pb如下。

   message
A ----------\       tuple<message,message>             WHATEVER
   message   join ------------------------- C------------------------
B ----------/

当我们想要同步并避免传播 n 次消息(及其值)时,将应用此模式。英特尔提供了一个例子,它解释了pb(以及解决方案 - Intel example)。我的pb是构造的tupple和使用静态方法的图的构造。它是完全静态的,特别是如果连接节点的输入边缘({1}}的数量)是变量。

TBB图流的大师是否知道一种动态方法&#34;这个pb?

最佳,

添 [编辑我的代码真实的pb]

我能做到:

input_port<i>

我做不到:

std::vector<tbb::flow::function_node<std::size_t, message>> vec_node;

for (int i(0) ; i < 3 ;++i)
        nodes_cont_.emplace_back(my_amazing_function_to_create_node(g_));

tbb::flow::make_edge(vec_node[0], tbb::flow::input_port<0>

tbb::flow::make_edge(vec_node[1], tbb::flow::input_port<1>(node_join_));
tbb::flow::make_edge(vec_node[2], tbb::flow::input_port<2>(node_join_));

由于&#34;元组&#34;和&#34; tbb :: flow :: input_port&#34;功能。

2 个答案:

答案 0 :(得分:3)

join节点上的端口数是静态的(在编译时确定。)如果要为一个输出提供变量数量的输入,则需要能够指示哪个&#34;端口&# 34;消息传来,以及它的价值。

TBB有一种变体类型,它封装了端口号和值(它是indexer_node的输出。)如果您使用该类型(定义{{​​1}},但不要实例化它,你可以使用节点的indexer_node作为::output_type的输入类型(它可能有多个输出,但只能有一个输出),并让函数multifunction_node的主体决定何时输出正确数量,然后您可以在输入时存储值,并在multifunction_node看到&#34;正确&#34;输入数量,它可以构造一个输出值并将其转发给它的后继者。

图表如下:

我看到的一个问题是您必须定义multifunction_node的输出类型。这也是一个静态声明,尽管变量元组可能就是你所需要的。

  

编辑:

让我们做一些简化的假设:

  • 虽然在编译时不知道multifunction_node,但它在运行时是已知且不变的。放宽此约束将需要为每条消息传递一些额外的数据。
  • 虽然您使用的是N,但我认为这是因为tuple的输出是join_nodes(我试图添加向量作为一个特例,但我不是&#39} ; t认为这是一个选项。)我假设你在向量中的所有tuple都与输出具有相同的类型。这样我们就可以避免使用变体类型。
  • 我们传递的数据并不是特别大(复制构造并不是非常昂贵。)放宽这个约束需要更加谨慎地访问每个节点中的数据。
  • 有一些东西可以唯一地定义一起出现的消息。例如,如果您将数据的只读缓冲区传递给向量中的每个function_nodes,那么该缓冲区的地址就是让我们知道哪些消息放在一起的部分。

我在TBB工作已经有几年了,所以可能会有一些我不知道的事情,但我可以给你一个草图。

图表如下:

graph using vector of functions

(我实际上勾勒出了标签匹配连接的结构,因为它听起来就像你想要的那样。)

构造function_node的向量时,每个function_nodes都必须知道它的索引是什么。通常,这意味着向量是指向function_body的指针,并且每个节点都以索引作为其参数之一构建。

我假设function_nodes输出类似于缓冲区。该缓冲区传递给向量中的每个source_node's,每个function_node的输出类型为

  • 缓冲区地址
  • 节点向量中的function_node的索引
  • 其他神奇的善良

function_node是完成大部分工作的地方。它有

  • multifuncton_node的向量,由hash_maps索引索引,并带有缓冲区地址的键,包含各种缓冲区的每个function_node的结果。
  • 带有缓冲区地址密钥的function_node,包含为该缓冲区接收的nuber元素的计数。当它达到N时,你有所有的输入。

hash_map收到消息时,

  • 将数据添加到multifunction_node,其中i是hash_map[i][key]索引(在输入消息中),key是缓冲区地址
  • 增加function_node。如果现在是hash_count[key],那么
  • 构造结果值的向量,将每个结果值拉出该哈希表。
  • 如果您构建它,则转发该值,否则只返回。

对于如何传递和存储数据存在一些问题,以及如果希望重复使用值,如何清理元素,但这是基本草图。

答案 1 :(得分:0)

如果您在编译时为特定程序知道N,但是想要以通用方式实现图形以供在不同程序中使用的库,则BOOST_PP是小N的选项。

我实现了一个图,该图在连接最慢的节点输出continue_msg之后生成continue_msg。为此,我需要多个N缓冲节点,并将它们连接到具有N个相同类型(tbb :: flow :: continue_msg)端口的连接节点。

基本上,以下代码可以实现您想要的

for(int i(0); i < vec_node.size(); ++i)
    tbb::flow::make_edge(vec_node[i], tbb::flow::input_port<i>(node_join_));

...但是使用预编译器通过正确的make_edge调用“写”多行,但最多不超过N(对于N

    #include "boost/preprocessor/repetition/repeat_from_to.hpp"
    #include "boost/preprocessor/repetition/repeat.hpp"
    #include "boost/preprocessor/arithmetic/inc.hpp"

    ...

    #define MY_JOIN_NODE_VARIADIC_MAX 8
    #define MY_FUNC_IMPL(z, n, unused) tbb::flow::make_edge(vec_node[##n], tbb::flow::input_port<##n>(joinNode));
    #define MY_MAKE_IMPL(z, n, unused)                                  \
    template <size_t N, typename TJoinNode> void                        \
    makeAllEdges (TJoinNode& joinNode,                                  \
                     typename std::enable_if< N == n >::type * = 0)     \
    {                                                                   \
        BOOST_PP_REPEAT(n, MY_FUNC_IMPL, unused)                          \
    }
    BOOST_PP_REPEAT_FROM_TO(0, BOOST_PP_INC(MY_JOIN_NODE_VARIADIC_MAX), MY_MAKE_IMPL, unused)
    #undef MY_MAKE_IMPL
    #undef MY_FUNC_IMPL
    #undef MY_JOIN_NODE_VARIADIC_MAX

此代码是函数定义。然后可以调用“ makeAllEdges”。 (请注意,在此示例中,我假设makeAllEdges是一个类方法,而vec_node是该类的成员,并且在makeAllEdges的场景中也是如此。)