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

tasks with priority execution order

dulantha_f
Beginner
3,040 Views
I'm trying to schedule tasks with priorities. I have a class that inherits from tbb::task with execute() method overriden. For testing purposes it's only printing out a line.
tbb::task* execute() {
cout<<"Task Number "<<_taskNumber<<" Priority: "<<_priority<
return NULL;
}
Then I create 30 task objects and use tbb::task::enqueue(taskObj, priority) to execute them. If I'm using incorrect syntax please correct me.
TestTask& task1 = *new(tbb::task::allocate_root()) TestTask(1,"low");
tbb::task::enqueue(task1,tbb::priority_t::priority_low);
I use low, high and normal priorities in random order.
But according to the output normal tasks execute first, and then highs and then lows. I would expect highs first, then normals and finally lows but that's not my output.
Thanks for ideas guys.
0 Kudos
32 Replies
SergeyKostrov
Valued Contributor II
731 Views
Hi. Thank you for the confirmation of the bug. We are working to fix other issues also related to the priorities in the scheduler, and will take this one into account of course.
It may be not a portability issue since I tested it with my working copy, not with a TBB release you are using..


Hi Anton,

It means that there are some differences between our TBB codes.

Yesterday I've spent a significant amount of time debugging of the TBB's 'generic_scheduler'
class,'arena' class and'observer::process_list(...)' method and I was able to see howall tasks
with'normal' prioritieswere executed first.

When are going to release anupdate or a new release of the TBB?

Best regards,
Sergey

0 Kudos
Anton_M_Intel
Employee
731 Views
Sounds great! Would you share your findings? Or you might want to contribute a patch through "Make a Contribution" link onthreadingbuildingblocks.org
It will be released when the patch will be scanned for legal issues and integrated into the code, so it can miss the next release.
0 Kudos
SergeyKostrov
Valued Contributor II
731 Views
Sounds great! Would you share your findings? Or you might want to contribute a patch through "Make a Contribution" link onthreadingbuildingblocks.org

[SergeyK] I'll take a look at "Make a Contribution" option. Thank you, Anton!

It will be released when the patch will be scanned for legal issues and integrated into the code, so it can miss the next release.

0 Kudos
Alexey-Kukanov
Employee
731 Views
Thank you dulantha_f for reporting the issue, and thank to everybody for investigation and the discussion.

Regrettably, I must admit that priority support for enqueued tasks is broken for now. Not without the help of this report, we realized some problems with the current implementation which probably never worked as intended. While we are working on a fix, I can suggest you aworkaround, hopefully sufficient for your case: associate a separate task_group_context with each task that you enqueue, and set the priority of this context in addition to the priority of the task. The attached example (based on Sergey K.'s code) illustrates how it can be done; the key lines are in the constructor of class CTask:

[cpp]     CMyTask( int iNumber, tbb::priority_t prio )
     {
          /* ... */
          tbb::task::change_group(my_context);
          tbb::task::set_group_priority(prio);
     };[/cpp]
0 Kudos
jimdempseyatthecove
Honored Contributor III
731 Views
An alternative (untested) would be to create multiple concurrent queues (one for each priority), then have the producer enqueue task shell functors. The consumers would then dequeue following your picking order, using the functor to dispatch to the task.

You would have a minor race issue recognizing first n-fills, last n-fills (for server task start-up/shut-down) which any competent programmer can solve.

Jim Dempsey
0 Kudos
dulantha_f
Beginner
731 Views
  1. CMyTask(intiNumber,tbb::priority_tprio)
  2. {
  3. /*...*/
  4. tbb::task::change_group(my_context);
  5. tbb::task::set_group_priority(prio);
  6. };
[cpp]     CMyTask( int iNumber, tbb::priority_t prio )
     {
          /* ... */
          tbb::task::change_group(my_context);
          tbb::task::set_group_priority(prio);
     };[/cpp]

this is actually what my workaround was for the time being. I was going to mention that priorities using group_contexts work but you beat me to :)

0 Kudos
dulantha_f
Beginner
731 Views
Latest release did fix the problem in my user cases. V4 update 5 to be exact.
0 Kudos
SergeyKostrov
Valued Contributor II
731 Views
Quoting dulantha_f
Latest release did fix the problem in my user cases. V4 update 5 to be exact.


Thank you for the update. ( I'm still using TBB v4Update 3... )

Best regards,
Sergey

0 Kudos
Anton_M_Intel
Employee
731 Views
Quoting dulantha_f
Latest release did fix the problem in my user cases. V4 update 5 to be exact.

Thanks! Glad to hear that it works for you.

0 Kudos
dulantha_f
Beginner
731 Views
I have a new problem though. Seems like a comparatively low priority task would every now and then be executed before a higher priority task. I have attached my code and screen shots to explain.
In my code there's class that the user creates which has his implementation. The actual task class that inherits from tbb::task executes the user's class' execute() method. There's another class that does the queuing that acts as a broker between the user class and the tbb::task inherited class.
In the following image normal19 was executed before 3 high priority tasks. In other runs a random normal task would get executed before highs. I didn't see any pattern for which normal priority task or how many high priority tasks would be executed after the normal priority task.
I'm using the latest TBB release.
main method:
int main()
{
tbb::task_scheduler_init init(7);
cout<<">>>>1\n";
UserTask t1("high1");
UserTask t2("high2");
UserTask t3("high3");
UserTask t4("high4");
UserTask t5("high5");
UserTask t6("high6");
UserTask t61("high6.1");
UserTask t62("high6.2");
UserTask t63("high6.3");
UserTask t7("low7");
UserTask t8("normal8");
UserTask t9("normal9");
UserTask t10("low10");
UserTask t11("high11");
UserTask t12("low12");
UserTask t13("normal13");
UserTask t14("low14");
UserTask t15("high15");
UserTask t16("low16");
UserTask t17("normal17");
QueueTasks qtask;
cout<<">>>>2\n";
qtask.queue(t1,tbb::priority_high);
qtask.queue(t2,tbb::priority_high);
qtask.queue(t3,tbb::priority_high);
qtask.queue(t4,tbb::priority_high);
qtask.queue(t5,tbb::priority_high);
qtask.queue(t6,tbb::priority_high);
qtask.queue(t61,tbb::priority_high);
qtask.queue(t62,tbb::priority_high);
qtask.queue(t63,tbb::priority_high);
qtask.queue(t7,tbb::priority_low);
qtask.queue(t8,tbb::priority_normal);
qtask.queue(t9,tbb::priority_normal);
qtask.queue(t10,tbb::priority_low);
qtask.queue(t11,tbb::priority_high);
qtask.queue(t12,tbb::priority_low);
qtask.queue(t13,tbb::priority_normal);
qtask.queue(t14,tbb::priority_low);
qtask.queue(t15,tbb::priority_high);
qtask.queue(t16,tbb::priority_low);
qtask.queue(t17,tbb::priority_normal);
cout<<">>>>3\n";
string test;
cin>>test;
return 0;
}
// class users create to implement their tasks
class UserTask
{
public:
UserTask(){};
UserTask(string label):m_label(label){};
~UserTask(){};
void execute(){
this_thread::sleep_for( tbb::tick_count::interval_t(.25));
//cout<<"In UserTask.execute()"<
}
private:
string m_label;
};
// class that extends from tbb::task
class MyTask : public tbb::task
{
public :
MyTask(UserTask& task, string userTaskName):m_userTask(task),m_name(userTaskName){};
~MyTask() {};
tbb::task* MyTask::execute(){
stringstream ss;
m_userTask.execute();
ss<<"Task done prio: "<<
cout<
return NULL;
}
private:
string m_name;
UserTask& m_userTask;
};
// class that does the queuing
class QueueTasks
{
public:
QueueTasks(){};
~QueueTasks(){};
void queue(UserTask& task, priority_t prio){
string tempLabel = task.getLabel();
MyTask & realTask = *new(tbb::task::allocate_root()) MyTask(task, tempLabel);
tbb::task::enqueue(realTask,prio);
}
};
0 Kudos
SergeyKostrov
Valued Contributor II
731 Views
Quoting dulantha_f
I have a new problem though. Seems like a comparatively low priority task would every now and then be executed before a higher priority task...

I checked my old TBBtest-cases with TBB v4 Update 3 and I didn't see that an issue was fixed. I know
that this is NOT the latest version of TBB.

Unfortunately, every time when some software update has to be done in my software development environment
it involves a complete re-testing of many-many pieces of software and test-cases. So, that is why my current version looks like "obsolete".
0 Kudos
dulantha_f
Beginner
731 Views
In the code I provided if I change the number of threads the scheduler is initialized with to a different number I don't see a problem with the execution order. So some test cases might not see the problem I'm seeing.
But TBB v4 update 5 fixed the normal priority tasks being executed before the high priority tasks.
0 Kudos
Reply