1

I am experimenting Intel Graph Flow from TBB. I am super satisfied by the results and I found the product amazing, with unlimited possibilities. However I was confronted to a pb that I fixed but I am not satisfied. The pb is the following.

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

This pattern is applied when we want to sync and avoidto propagate n times a message (and his value). Intel provides an example which explained well the pb (and the solution - Intel example). My pb is the tupple constructed and the construction of the graph which used static approach. It is fully static, specially if the number of input edge (input_port<i> in the Intel example) to the join node are variables.

Does a guru of TBB-graph flow knows a "dynamic approach" to this pb ?

Best,

Tim [EDIT my code real pb]

I can do:

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_));

I can not do:

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

Due to the "tuple" and the "tbb::flow::input_port" function.

4

2 回答 2

3

节点上的端口数join是静态的(在编译时确定。)如果您想要一个输出的不同输入数,您需要能够指示消息进入的“端口”及其值.

TBB 有一个变体类型,它封装了一个端口号和一个值(它是 . 的输出indexer_node。)如果使用该类型(定义一个indexer_node,但不实例化它,并且可以使用::output_type节点的 )作为输入为 a 键入multifunction_node(可能有多个输出,但只能有一个输出),并让函数体multifunction_node决定何时具有正确数量的输出,然后您可以在输入值时存储它们,并且当multifunction_node看到“正确”数量的输入时,它可以构造一个输出值并将其转发给它的后继者。

该图将如下所示:

我看到的一个问题是您必须定义multifunction_node. 这也是一个静态声明,尽管你可能需要一个变体元组。

编辑:

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

  • 尽管N在编译时不知道,但在运行时它是已知且不变的。放宽此约束将需要在每条消息中传递一些额外的数据。
  • 尽管您使用的是 a tuple,但我相信这是因为的输出join_nodes是 a tuple(我尝试将向量添加为特例,但我认为这不是一个选项。)我假设function_nodes向量中的所有内容都具有与输出相同的类型。这样我们就可以避免使用变体类型。
  • 我们传递的数据不是特别大(复制构造不是超级昂贵)。放松这个约束将需要在访问每个节点中的数据时更加小心。
  • 有一些东西可以独特地定义一起去的消息。例如,如果您将只读数据缓冲区交给function_node向量中的每个缓冲区,则该缓冲区的地址就是让我们知道将哪些消息放在一起的部分。

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

该图将如下所示:

使用函数向量的图

(我实际上是在勾勒出标签匹配连接的结构,因为听起来这就是你想要的。)

当你构造 的向量时function_nodes,每个人都必须function_body知道它的索引是什么。一般来说,这意味着向量是指向 的指针function_nodes,并且每个节点都是用索引作为其参数之一构造的。

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

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

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

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

multifunction_node收到消息时,它

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

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

于 2017-07-21T12:42:37.960 回答
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 < MY_JOIN_NODE_VARIADIC_MAX,该选择是任意的,可以将其限制为“小”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 的范围内是已知的。)

于 2019-03-15T14:10:47.697 回答