Nodes Priorities¶
[flow_graph.node_priorities]
Flow graph provides interface for setting relative priorities at construction of flow graph functional nodes, guiding threads that execute the graph to prefer nodes with higher priority.
namespace oneapi {
namespace tbb {
namespace flow {
typedef unsigned int node_priority_t;
const node_priority_t no_priority = node_priority_t(0);
} // namespace flow
} // namespace tbb
} // namespace oneapi
function_node
, multifunction_node
, async_node
and continue_node
has a constructor
with parameter of node_priority_t
type, which sets the node priority in the graph: the larger
the specified value for the parameter, the higher the priority. The special constant value
no_priority
, which is also the default value of the parameter, switches priority off for a particular node.
For a particular graph, tasks to execute the nodes whose priority is specified have precedence over tasks for the nodes with lower or no priority value set. When looking for a task to execute, a thread chooses the one with the highest priority from those in the graph that are available for execution.
Example¶
The following basic example demonstrates prioritization of one path in the graph over the other, which may help to improve overall performance of the graph.
Consider executing the graph from the picture above using two threads. Assume that the nodes f1
and
f3
take equal time to execute, while the node f2
takes longer. That makes the nodes
bs
, f2
, and fe
constitute the critical path in this graph. Due to the
non-deterministic behavior in selection of the tasks, oneTBB might execute nodes f1
and f3
in parallel first, which would make the whole graph execution time last longer than the case when
one of the threads chooses the node f2
just after the broadcast node. By setting a higher
priority on node f2
, threads are guided to take the critical path task earlier, thus reducing
overall execution time.
#include <iostream>
#include <cmath>
#include "oneapi/tbb/tick_count.h"
#include "oneapi/tbb/global_control.h"
#include "oneapi/tbb/flow_graph.h"
void spin_for( double delta_seconds ) {
oneapi::tbb::tick_count start = oneapi::tbb::tick_count::now();
while( (oneapi::tbb::tick_count::now() - start).seconds() < delta_seconds ) ;
}
static const double unit_of_time = 0.1;
struct Body {
unsigned factor;
Body( unsigned times ) : factor( times ) {}
void operator()( const oneapi::tbb::flow::continue_msg& ) {
// body execution takes 'factor' units of time
spin_for( factor * unit_of_time );
}
};
int main() {
using namespace oneapi::tbb::flow;
const int max_threads = 2;
oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, max_threads);
graph g;
broadcast_node<continue_msg> bs(g);
continue_node<continue_msg> f1(g, Body(5));
// f2 is a heavy one and takes the most execution time as compared to the other nodes in the
// graph. Therefore, let the graph start this node as soon as possible by prioritizing it over
// the other nodes.
continue_node<continue_msg> f2(g, Body(10), node_priority_t(1));
continue_node<continue_msg> f3(g, Body(5));
continue_node<continue_msg> fe(g, Body(7));
make_edge( bs, f1 );
make_edge( bs, f2 );
make_edge( bs, f3 );
make_edge( f1, fe );
make_edge( f2, fe );
make_edge( f3, fe );
oneapi::tbb::tick_count start = oneapi::tbb::tick_count::now();
bs.try_put( continue_msg() );
g.wait_for_all();
double elapsed = std::floor((oneapi::tbb::tick_count::now() - start).seconds() / unit_of_time);
std::cout << "Elapsed approximately " << elapsed << " units of time" << std::endl;
return 0;
}