Message Flow Graph Example#

This example calculates the sum x*x + x*x*x for all x = 1 to 10. The layout of this example is shown in the figure below.

../../../../_images/message_flow_graph.jpg

A simple message flow graph.#

Each value enters through the broadcast_node<int> input. This node broadcasts the value to both squarer and cuber, which calculate x*x and x*x*x, respectively. The output of each of these nodes is put to one of join’s ports. A tuple containing both values is created by join_node<std::tuple<int,int>> join and forwarded to summer, which adds both values to the running total. Both squarer and cuber allow unlimited concurrency, that is they each may process multiple values simultaneously. The final summer, which updates a shared total, is only allowed to process a single incoming tuple at a time, eliminating the need for a lock around the shared value.

#include <cstdio>
#include "oneapi/tbb/flow_graph.h"

using namespace oneapi::tbb::flow;

struct square {
  int operator()(int v) { return v*v; }
};

struct cube {
  int operator()(int v) { return v*v*v; }
};

class sum {
  int &my_sum;
public:
  sum( int &s ) : my_sum(s) {}
  int operator()( std::tuple<int, int> v ) {
    my_sum += get<0>(v) + get<1>(v);
    return my_sum;
  }
};

int main() {
  int result = 0;

  graph g;
  broadcast_node<int> input(g);
  function_node<int,int> squarer( g, unlimited, square() );
  function_node<int,int> cuber( g, unlimited, cube() );
  join_node<std::tuple<int,int>, queueing> join( g );
  function_node<std::tuple<int,int>,int>
      summer( g, serial, sum(result) );

  make_edge( input, squarer );
  make_edge( input, cuber );
  make_edge( squarer, get<0>( join.input_ports() ) );
  make_edge( cuber, get<1>( join.input_ports() ) );
  make_edge( join, summer );

  for (int i = 1; i <= 10; ++i)
      input.try_put(i);
  g.wait_for_all();

  printf("Final result is %d\n", result);
  return 0;
}

In the example code above, the classes square, cube, and sum define the three user-defined operations. Each class is used to create a function_node.

In function main, the flow graph is set up and then the values 1-10 are put into the node input. All the nodes in this example pass around values of type int. The nodes used in this example are all class templates and therefore can be used with any type that supports copy construction, including pointers and objects.