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

tbb failed running on machine with only one CPU

ccj_999
Beginner
428 Views
Intel Doc said "There is no guarantee that potentially parallel tasks actually execute in parallel, because the scheduler adjusts actual parallelism to fit available worker threads. For example, when given a single worker thread, the scheduler obviously cannot create parallelism". I am a little confused about this.

I read the TBB's source code and found that if the client doesn't set the thread number to initialize a task scheduler, the scheduler will use a default number. And the default thread number is coming from the processor numbers of the machine. Assume the machine has only one CPU, the default thread number is 1 and if the machine has two CPUs, the default thread number is 2.

But I still want the tasks running in parallel on a Machine with only one CPU, I don't want to use the Blocking Style. But TBB doesn't meet what I want.

Take the following code for example, I want the maintask working parallel with the child task, the maintask spawn the child task and then goes down to do its following job and doesn't wait for its child task finish. The child task should execute while the maintask goes down, this means the maintask and the child task are doing their jobs together. At the end of the program, the maintask will check whether the child task finishes or not, if the child task is still running, the maintask will wait for it finish.

#include "tbb/task.h"
#include "tbb/task_scheduler_init.h"

class mytask:public tbb::task
{
std::string _abc;
public:
tbb::task* execute()
{
sleep(3);
std::cout << _abc << std::endl;

return NULL;
}
mytask(std::string value):_abc(value){;}
};

void maintaskjob()
{
std::cout << "it's main task job..." << std::endl;
}

int main()
{
tbb::task_scheduler_init* oinit = new tbb::task_scheduler_init();
tbb::task& maintask = tbb::task::self();
tbb::task* test = new (maintask.allocate_additional_child_of(maintask)) mytask("one");
maintask.spawn(*test);

maintaskjob();

while(maintask.ref_count() > 1)
{
sleep(0);
}
return 0;
}

on Machine with more than 2 CPUs(including 2), the out put is the following
it's main task job...
one

But the problem is the code doesn't work on Machine with only 1 CPU. The program cannot exit. It seems that there is only one worker thread running the main task, but the main task never completes because it is waiting for its child task finish. But the child task never executes.....

I don't want to use the maintask.spawn_and_wait_for_all(*test), because this won't fit my goal. If I let the maintask waits for all its child task, the result is:
one
it's main task job...
This means the main task must waiting for all its child task finish and then it can start to run.

On the machine with only one CPU, I need to set the thread number to 2 manually when initialize the task scheduler in order to make the above sample code to run successfully.

tbb::task_scheduler_init* oinit = new tbb::task_scheduler_init(2);

It is werid and makes me uncomfortable. As a client developer, I still need to know about the machine I work on. And in Intel's Doc, it is said "for production code the default value of -1 should be used".

So what should I do?

Could someone help me with this issue?

Thanks so much in advance.


Best Wishes,

Jessica




0 Kudos
4 Replies
RafSchietekat
Valued Contributor III
428 Views

That's a very scary title... especially since it's not true: TBB does what it was designed to do (see below). Please be more careful next time!

Note that you're leaking task_scheduler_init (just make it an automatic variable instead).

Please have a look, e.g.,at thread "Can I separate task::spawn_root_and_wait() into 2 calls?" (it's not the only one with the same subject matter, and if I recall correctly the book also deals with it), and use it/them as an example for your main() function.

Because you are requiring concurrency from a toolkit designed for optional concurrency, it seems that you will have to initialize task_scheduler_init with "std::min(2,default_num_threads())" to get what you want (consider it a hack), butthis will also disableany optimisations for single-threaded execution. Otherwise use an explicit tbb_thread.

0 Kudos
ccj_999
Beginner
428 Views
Thanks so much for your fast respond, Raf!

I am sorry for the scary title and will pay attention to it nex time:)

I will read through the thread and see if it can help.



-----Jessica


0 Kudos
Alexey-Kukanov
Employee
428 Views
Quoting - ccj_999

Take the following code for example, I want the maintask working parallel with the child task, the maintask spawn the child task and then goes down to do its following job and doesn't wait for its child task finish. The child task should execute while the maintask goes down, this means the maintask and the child task are doing their jobs together. At the end of the program, the maintask will check whether the child task finishes or not, if the child task is still running, the maintask will wait for it finish.

The example requires concurrency, and moreover itappears to usefunctional decomposition. Explicit threading (e.g with thetbb_thread class) is the best fit.
0 Kudos
RafSchietekat
Valued Contributor III
428 Views

Alexey Kukanov: "Explicit threading (e.g with the tbb_thread class) is the best fit." It does have the advantage of not being a hack...Note that neither approach will actually guarantee concurrency yet, but I understand that this will be addressed in an upcoming version (see discussion about pipelines getting all tangled up).

0 Kudos
Reply