Flow Graph Basics: Single-push vs. Broadcast-push

Nodes in the oneAPI Threading Building Blocks (oneTBB) flow graph communicate by pushing and pulling messages. Two policies for pushing messages are used, depending on the type of the node:

  • single-push: No matter how many successors to the node exist and are able to accept a message, each message will be only sent to one successor.

  • broadcast-push: A message will be pushed to every successor which is connected to the node by an edge in push mode, and which accepts the message.

The following code demonstrates this difference:

using namespace oneapi::tbb::flow;


std::atomic<size_t> g_cnt;


struct fn_body1 {
    std::atomic<size_t> &body_cnt;
    fn_body1(std::atomic<size_t> &b_cnt) : body_cnt(b_cnt) {}
    continue_msg operator()( continue_msg /*dont_care*/) {
        ++g_cnt;
        ++body_cnt;
        return continue_msg();
    }
};


void run_example1() {  // example for Flow_Graph_Single_Vs_Broadcast.xml
    graph g;
    std::atomic<size_t> b1;  // local counts
    std::atomic<size_t> b2;  // for each function _node body
    std::atomic<size_t> b3;  //
    function_node<continue_msg> f1(g,serial,fn_body1(b1));
    function_node<continue_msg> f2(g,serial,fn_body1(b2));
    function_node<continue_msg> f3(g,serial,fn_body1(b3));
    buffer_node<continue_msg> buf1(g);
    //
    // single-push policy
    //
    g_cnt = b1 = b2 = b3 = 0;
    make_edge(buf1,f1);
    make_edge(buf1,f2);
    make_edge(buf1,f3);
    buf1.try_put(continue_msg());
    buf1.try_put(continue_msg());
    buf1.try_put(continue_msg());
    g.wait_for_all();
    printf( "after single-push test, g_cnt == %d, b1==%d, b2==%d, b3==%d\n", (int)g_cnt, (int)b1, (int)b2, (int)b3);
    remove_edge(buf1,f1);
    remove_edge(buf1,f2);
    remove_edge(buf1,f3);
    //
    // broadcast-push policy
    //
    broadcast_node<continue_msg> bn(g);
    g_cnt = b1 = b2 = b3 = 0;
    make_edge(bn,f1);
    make_edge(bn,f2);
    make_edge(bn,f3);
    bn.try_put(continue_msg());
    bn.try_put(continue_msg());
    bn.try_put(continue_msg());
    g.wait_for_all();
    printf( "after broadcast-push test, g_cnt == %d, b1==%d, b2==%d, b3==%d\n", (int)g_cnt, (int)b1, (int)b2, (int)b3);
}

The output of this code is

after single-push test, g_cnt == 3, b1==3, b2==0, b3==0
after broadcast-push test, g_cnt == 9, b1==3, b2==3, b3==3

The single-push test uses a buffer_node, which has a “single-push” policy for forwarding messages. Putting three messages to the buffer_node results in three messages being pushed. Notice also only the first function_node is sent to; in general there is no policy for which node is pushed to if more than one successor can accept.

The broadcast-push test uses a broadcast_node, which will push any message it receives to all accepting successors. Putting three messages to the broadcast_node results in a total of nine messages pushed to the function_nodes.

Only nodes designed to buffer (hold and forward received messages) have a “single-push” policy; all other nodes have a “broadcast-push” policy.

Please see the Sending to One or Multiple Successors section of Flow Graph Tips and Tricks, and Flow Graph Basics: Buffering and Forwarding for more information.