Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
2464 Discussions

tbb::task_scheduler_init(1) does not apply to flow_graph task scheduling

mklvml
Beginner
646 Views
I wanted to test the serial performance of my TBB code by using:
And it works as expected for plain parallel_for. But if I use the flow_graph API and a node's body makes the same parallel_for call, its executed by two threads (on my Core 2 Duo). After that, if I run the parallel_for again, it will run serially.
It this expected? How do I set my flow_graph to serial?
Thx
Code:
#include "tbb/compat/thread"
#include 
#include 


tbb::spin_mutex my_mutex;

using namespace tbb::flow;
using namespace tbb;


struct Body {
	void operator()(const tbb::blocked_range &r) const {

		{ tbb::spin_mutex::scoped_lock lock(my_mutex);
		cout<<"ThreadNr in 'Body': "<<:THIS_TBB_THREAD::GET_ID>(0, 2), Body());
			return 0;
			}
};
	

	
int testmain() 
{	
	
	cout<<"##########parallel_for only:###########"<(0, 2), Body());			 
	
	cout<<"##########parallel_for within flow_graph nodes###########"< node(g,node_body());
	node.try_put(continue_msg());
	g.wait_for_all();
		
	cout<<"##########parallel_for only:###########"<(0, 2), Body());			 
		
	return 0;
}
0 Kudos
3 Replies
Michael_V_Intel
Employee
646 Views

tbb::task_scheduler_init does apply to flow graph scheduling, but just not when you ask for 1 thread :) Unlike the other TBB algorithms, the flow graph does not use tbb::spawn for its tasks, but instead tbb::enqueue. Tasks that are enqueued are (1) executed in roughly first-in-first-out order and (2) are starvation resistant. If a task is enqueued and there are currently no worker threads available in the thread pool to execute it, a new thread is temporarily added to the thread pool to execute the task. So, when your code does the try_put, this enqueues a task to execute Body, but theres no thread available to execute this task (the main thread is busy doing the try_put). So a new thread is temporarily added to the thread pool. Hence you have 2 threads executing your graph.

If you were running on a larger system, say with 8 cores, and you created a task_scheduler_init init(2), you would see only 2 threads. But when asking for 1 thread, the library creates a thread to service your task so that it doesnt starve. So, unfortunately that means that there is no (easy) way to run a graph on only a single thread.

0 Kudos
mklvml
Beginner
646 Views
Thanks!
It would be great to have this documented, though.
0 Kudos
mklvml
Beginner
646 Views
My workaround:
  1. tbb::task_scheduler_init(1)
  2. graph::run(sleeping_body()), withsleeping_body::operator()(){ sleep(10000000000.0); }
  3. Start your graph: (for all) receiver.try_put()
  4. graph::decrement_wait_count()
  5. sleep(0.1) - this is used to ensure that the sleeping task is definitely run by the unwanted worker thread
  6. graph::wait_for_all()
(6.) will return as soon as the graph is done. I don't know what will happen to the sleeper thread. It probably could be canceled somehow.
Is this a more or less "safe way" to do it? Is there an easier way?
Thx
0 Kudos
Reply